01 What this is
- An open accountability layer for AI agents. A company signs a credential. A gateway verifies it. A compromised agent can be revoked by the company that issued it.
- Not RSA Agent ID. Not Microsoft Entra Agent ID. Those govern agents inside one tenant. This answers a different question: which issuer minted the agent, and will other servers accept it.
- Not for profit. One person with a working proof today. A neutral body later, if labs co-sign. Nobody sells the prefix registry. This is not a consortium.
- US-first. The domain is agentaccountability.us. Other countries can adopt the same schema later.
02 How a request works
- The issuing company signs a credential with its private key. The private key never leaves the company.
- The agent presents that credential in a header,
X-Agent-Credential. - The gateway checks prefix, signature, expiry, then that company’s revocation URL.
- Pass: forward the request and inject
X-Agent-ID. Fail: 403.
03 Credential
A signed document. Change one field and the signature breaks. This is the same GRO credential the demo verifies.
credential.json
{
"agent_id": "GRO-8CB7487B",
"company": "GRO",
"company_name": "Grok",
"agent_name": "ResearchBot-7",
"capabilities": ["web_search", "summarize"],
"kid": "k2",
"issued_at": 1791371562,
"expires_at": 1793963562,
"signature": "564f6874f12baf826debf2a2dd6776cf84086ab8a8a5eb7de530165527e46857f80fccb115158be6887b032edeec4a3bd7f720153ae7f7095e153d4aca035f0a"
}
companyis a three-letter prefix. Examples, not a live registry: OPI OpenAI, MUS Muse, BOT Anthropic, GRO Grok. A neutral body can add more later.kidbinds the credential to the key that signed it, so a leaked key can be retired.- Expiry is short on purpose. A week is a sane default for high-risk systems. Longer is a company choice.
04 Gateway
A reverse proxy in front of an API, DNS resolver, or control system. Stateless. No agent database. The URLs below are examples of the shape a company would publish. They are not live feeds.
agentid-gateway.yaml
# agentid-gateway.yaml
listen: ":8443"
upstream: "http://api.internal:8080"
mode: verify # or reject_all
companies:
OPI:
public_key_file: "/etc/agentid/opi.ed25519.pub"
revocation_url: "https://revocations.openai.com/v1/revoked.json"
MUS:
public_key_file: "/etc/agentid/mus.ed25519.pub"
revocation_url: "https://crl.muse.ai/agents/revoked.json"
BOT:
public_key_file: "/etc/agentid/bot.ed25519.pub"
revocation_url: "https://api.anthropic.com/agent-ids/revocations"
GRO:
public_key_file: "/etc/agentid/gro.ed25519.pub"
revocation_url: "https://x.ai/agent-ids/revocation-list"
cache:
revocation_ttl: "1h"
reverify_list_signature: true
headers:
credential: "X-Agent-Credential"
identity: "X-Agent-ID"
05 Check order
- Prefix registered. Unknown prefix is rejected.
- Signature verifies against that company’s public key for this
kid. - Not expired.
- Revocation list pulled from that company’s URL. The list signature is rechecked on every use, including cache hits.
- Agent id not on the list.
mode: reject_all. No credential, no access. Identified agents are blocked. Unidentified scripts are blocked too, same as requiring a badge at a door.06 See it refuse
These credentials were signed for this proof. The page checks the Ed25519 signature in your browser against the sample public keys. Edit a field and the signature fails. The sample revocation list contains OPI-A770391F. The clock is the proof clock, so the expired sample still fails.
07 Sample gateway code
verify
def verify(self, credential: dict) -> tuple[bool, str]:
code = credential.get("company")
company = self.companies.get(code)
if company is None:
return False, "unknown company prefix"
public_hex = company.keys.get(credential.get("kid"))
if public_hex is None:
return False, f"unknown key id {credential.get('kid')}"
try:
verify_ed25519(public_hex, credential)
except Exception:
return False, "bad signature"
if time.time() > credential["expires_at"]:
return False, "expired"
rev, status = self.revocation_list(code)
if rev is not None:
try:
self.check_revocation_list(rev) # every use, including cache hits
except Exception as exc:
return False, f"revocation list invalid: {exc}"
if credential["agent_id"] in rev["revoked_agents"]:
return False, f"REVOKED via {code} endpoint ({rev['reason']})"
return True, f"VALID (signed by key {credential['kid']}, revocations: {status})"
The only network call is the revocation fetch. Production is GET company.revocation_url. Air-gapped sites do not pull a public URL. They take a signed revocation bundle over their existing update channel. The browser demo uses a fixed sample list. It does not re-verify a signed list document, because this proof does not include one.
08 Revocation
- Each company publishes a signed list: agent ids, reason,
valid_until, key id. - Gateways cache it for about an hour and re-verify the signature every check. Trusting the list only at fetch time was the v1 hole. Editing the cache fails the signature.
- A company that will not publish a list can still issue credentials. Those agents pass signature and expiry only. Sites that require a revocation feed can refuse them.
09 Key rotation
- Companies rotate on a schedule, simulated at 90 days, and immediately on a suspected leak.
- Grace period: old key still verifies so live agents do not all die at once. Forgeries signed with the leaked key also work during grace.
- Retirement: old key is removed. Old credentials and forgeries fail with unknown key id. New key keeps working.
- Key custody stays with the company. A leak is their incident until they rotate.
10 Who does what
| Role | Owns | Does not own |
|---|---|---|
| Issuer (lab or platform) | Signing keys, credentials, revocation URL | The gateway, the prefix rules |
| Gateway operator | Verification in front of their server | Issuance, other companies’ keys |
| Project, later a neutral body | Prefix registry and schema | Keys, gateways, a database of agents |
Nvidia is the natural builder of gateway software and hardware. Nvidia should not own the registry. A vendor running the rules is a conflict, and the labs will treat it as a sales channel.
11 Where it sits
- Web and API servers: reverse proxy or middleware. One config, one header.
- DNS: credential on a signed request, gateway in front of the resolver. This is the control point if an agent is aimed at name service.
- Utilities and fintech: same binary,
reject_allif agents are not allowed. Air-gapped sites use a pushed, signed revocation bundle instead of a live URL. - The engineering is small. A team can stand up the proxy in about a week. The cost is policy, key handling, and keeping the revocation feed honest.
12 Why a company resists
- A signed agent id puts their name on the action. Today they can say it was the user or a wrapper.
- The revocation URL is a kill switch they must keep alive. Fail open and a stolen agent lives. Fail closed and their agents die everywhere.
- A leaked signing key is worse than a leaked API key. It mints agents other gateways will accept until rotation finishes.
- Portable agents weaken lock-in.
That resistance is the reason to do it. Banks, clouds, and regulated labs already get blamed. They want attribution. The rest want deniability.
13 Proof already run
- Valid credential passes. Tampered payload fails the signature.
- Stolen credential fails after the issuer publishes a revocation.
- Forged revocation list and a tampered cache both fail the list signature.
- Unknown prefix fails. Expired credential fails.
- Leaked company key works during grace, then fails after that key is retired.