Per-agent scoped tokens with revocation. Implements ACRF-02 defense pattern.
Project description
acrf-tokens
Per-agent scoped tokens with revocation. Implements the ACRF-02 (No Standard Agent Identity) defense pattern.
Part of the ACRF framework: https://github.com/kannasekar-alt/ACRF PyPI: https://pypi.org/project/acrf-tokens/ Presented at RSA Conference 2026.
Try it in your environment right now
No Docker. No setup. Just Python 3.10+.
Step 1 - Install:
pip install acrf-tokens
Step 2 - Generate an issuer (one-time setup):
from acrf_tokens import TokenIssuer
issuer = TokenIssuer.generate(issuer_name="acme-auth")
issuer.save("issuer_private.json") # KEEP SECRET
issuer.save_public("issuer_public.json") # share with validators
Step 3 - Mint a token for each agent:
from acrf_tokens import TokenIssuer
issuer = TokenIssuer.load("issuer_private.json")
token = issuer.issue(
agent_name="PricingAgent",
scopes=["pricing:read", "trades:propose"],
ttl_seconds=3600,
)
# token is a string - send it to the agent
Step 4 - Validate before allowing any action:
from acrf_tokens import TokenValidator
validator = TokenValidator.load("issuer_public.json")
agent_token = validator.validate(token, required_scope="trades:propose")
# raises TokenError subclass if invalid, expired, revoked, or missing scope
Step 5 - Revoke compromised tokens instantly:
validator.revoke(agent_token.token_id)
validator.save_revocations("revocations.json")
The problem this solves
Most agent platforms today share a single API token across many agents. There is no per-agent identity, no scope, no expiration, no revocation. When that token leaks, every agent in the system is compromised.
This is ACRF-02: no standard agent identity.
acrf-tokens gives every agent its own short-lived, scoped, revocable token signed by a central issuer using Ed25519. Compromised tokens are revoked instantly. Stolen tokens expire on their own. No more shared secrets.
Security features built in
Ed25519 signed tokens Industry-standard cryptography. Issuer signs with private key, validator verifies with public key. No shared secret between issuer and validator.
Per-agent identity Every token names exactly one agent. No more "one key for all agents."
Explicit scope list Every token carries the exact set of permissions it grants. The validator enforces that the required scope is present before allowing the action.
Short-lived tokens Every token has an expires_at. Stolen tokens become useless on their own.
Instant revocation Compromised tokens can be revoked by token id (jti). Future validations fail immediately, regardless of expiration.
Public-key validation Validators only need the issuer public card. They never see the private key. You can have hundreds of validators with zero shared secrets.
Audit trail Every validation produces an audit record (success or failure) with token id, agent name, issuer, timestamp, and outcome.
CLI
Set the validator paths once:
export ACRF_TOKENS_PUBLIC=/etc/acrf/issuer_public.json
export ACRF_TOKENS_REVOCATIONS=/etc/acrf/revocations.json
Generate an issuer:
acrf-tokens generate-issuer acme-auth
Mint a scoped token:
acrf-tokens issue \
--issuer acme-auth_private.json \
--agent PricingAgent \
--scopes pricing:read,trades:propose \
--ttl 3600
Validate a token:
acrf-tokens validate <token-string> --scope trades:propose
Revoke a token:
acrf-tokens revoke <token-id>
List revoked tokens:
acrf-tokens list
How it works
- The issuer generates an Ed25519 keypair (one time)
- The public part is distributed to validators
- To mint a token, the issuer signs JSON claims (token_id, agent_name, scopes, issued_at, expires_at) with the private key
- The wire format is: BASE64(claims).BASE64(signature)
- Validators verify the signature with the public key
- Validators check expiration and revocation list
- If a required scope is requested, validators check it is present
- Only then is the token accepted and the AgentToken returned
What goes in the issuer private file
The issuer private file contains the Ed25519 private key. Treat it like any other production secret:
- AWS Secrets Manager / Azure Key Vault / GCP Secret Manager
- HashiCorp Vault
- Kubernetes secrets mounted at runtime
What NOT to do:
- Commit the issuer private file to source control
- Bake it into Docker images
- Distribute it to validators (only the public file goes there)
Real-world use
Wrap your agent action handler with one validation call:
from acrf_tokens import TokenValidator
from acrf_tokens.exceptions import TokenError
import os
VALIDATOR = TokenValidator.load_with_revocations(
os.environ["ACRF_TOKENS_PUBLIC"],
os.environ["ACRF_TOKENS_REVOCATIONS"],
)
def handle_request(request):
token_string = request.headers["X-Agent-Token"]
try:
agent_token = VALIDATOR.validate(token_string, required_scope=request.required_scope)
except TokenError as exc:
return {"error": "unauthorized", "reason": str(exc)}, 401
# request authorized; proceed using agent_token.agent_name and agent_token.scopes
return process(request, agent_token)
That is it. Every action handler is now per-agent authorized with revocation, expiration, and scope enforcement.
ACRF-02 control objectives addressed
SI-1 Every agent has a cryptographically verifiable identity distinct from user identity
SI-2 Tokens scoped per agent and per endpoint
SI-3 Identity material rotatable and revocable without impacting other agents
What this library does NOT do
- It does not handle user authentication (use OAuth/SSO)
- It does not encrypt the token contents (claims are visible in the token)
- It does not enforce rate limits (use a separate gateway)
It only ensures that an agent action is authorized by a token that was issued by a trusted issuer, names a specific agent, has not expired, has not been revoked, and contains the required scope. That is the ACRF-02 defense pattern.
Works with any Python AI agent framework
LangChain, CrewAI, AutoGen, MCP-based systems, custom agents. If your agents call APIs or each other, you can use this library.
Authors
Ravi Karthick Sankara Narayanan, Kanna Sekar
License
Apache 2.0
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file acrf_tokens-0.1.0.tar.gz.
File metadata
- Download URL: acrf_tokens-0.1.0.tar.gz
- Upload date:
- Size: 15.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a36b6cf99c74bbe97d542f5cd6a660c7545e3d6502ad0710740188dad095df1e
|
|
| MD5 |
7d18e78a29eb8e190790e4b9e8e12b56
|
|
| BLAKE2b-256 |
f85bf91b9e03ccd01972bd1d781531a5c8dd06d9b5803e0450a2338d4e7d29e5
|
File details
Details for the file acrf_tokens-0.1.0-py3-none-any.whl.
File metadata
- Download URL: acrf_tokens-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c53b61c555610db03b9f58cbf145ce57488f9958fcd36c349cf7c3deaed0147
|
|
| MD5 |
c830f9354ac2f9863ec0b0dd60756ad8
|
|
| BLAKE2b-256 |
8c25f6e68113ef71bfcd118a60a248a31a05b9635f92eaaa2cba3024275dabb8
|