did:web identity documents
Every AgentValet agent has a W3C Decentralized Identifier of the form:
did:web:api.agentvalet.ai:agents:<agent_id>It is derived from the agent id and nothing else, so it exists the moment an agent is registered. GET /v1/agents/me returns it as did. What is opt-in is publishing the DID document: once an organisation admin turns on Agent identity documents in Settings, each of that org’s agents resolves at
https://api.agentvalet.ai/agents/<agent_id>/did.jsonexactly as the did:web method specifies (colons become path segments, /did.json is appended). Any standard DID resolver, including the Universal Resolver, resolves it without knowing anything about AgentValet.
What the document says
The document publishes the same RSA public key the proxy verifies the agent’s bearer tokens against. Nothing is re-keyed or invented for the DID:
{
"@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1"],
"id": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83",
"controller": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83",
"verificationMethod": [{
"id": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83#key-3f1a9c2e",
"type": "JsonWebKey2020",
"controller": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83",
"publicKeyJwk": { "kty": "RSA", "n": "...", "e": "AQAB", "alg": "RS256", "use": "sig", "kid": "agt_7o00saqajyahinuuc6g83" }
}],
"authentication": ["did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83#key-3f1a9c2e"],
"assertionMethod": ["did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83#key-3f1a9c2e"],
"service": [
{ "id": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83#authzen", "type": "AuthZENPolicyDecisionPoint", "serviceEndpoint": "https://api.agentvalet.ai/v1/authzen/access" },
{ "id": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83#trqp", "type": "TRQPAuthorization", "serviceEndpoint": "https://api.agentvalet.ai/v1/trqp/authorization" },
{ "id": "did:web:api.agentvalet.ai:agents:agt_7o00saqajyahinuuc6g83#trqp-recognition", "type": "TRQPRecognition", "serviceEndpoint": "https://api.agentvalet.ai/v1/trqp/recognition" }
]
}The key fragment (#key-3f1a9c2e) is a fingerprint of the key, so it changes when the key is rotated. The service entries are how a relying party finds the decision endpoints: #authzen for AuthZEN 1.0 (POST /v1/authzen/access, bearer required), and #trqp / #trqp-recognition for the ToIP Trust Registry Query Protocol (POST /v1/trqp/authorization and /recognition), which answer publicly for agents whose organisation has opted in. All three return the effective answer, not just whether a grant exists. The same Settings toggle that publishes the document also opens the public TRQP path for the org’s agents, so a document never advertises an endpoint that would refuse to answer.
A verifier can prove the link in one step: sign anything with the agent’s private key and verify it with publicKeyJwk. scripts/did-web-resolve.mjs in the repository does exactly that.
What the document never says
Owner, organisation and agent name are never included. A document reveals that an agent id exists, its public key, and whether it is currently active. Agent ids are 80 to 108 bits of random and cannot be enumerated, so a document is only reachable by someone who already holds the id.
Status
The document tracks the agent’s lifecycle, visible within a minute (Cache-Control: public, max-age=60):
| Agent status | Resolution |
|---|---|
| Active, own key | Full document as above |
| Active, authenticated by AgentValet itself (Claude web MCP clients, child agents) | Keyless document whose controller is did:web:api.agentvalet.ai (and the parent’s DID for a child). This is the truthful statement: their tokens are verified against the broker’s key, not one they hold |
| Suspended | Document with the key present but empty authentication and assertionMethod: the key still belongs to the agent, it currently has no authority. A receipt signed before the suspension stays checkable |
| Revoked or deleted | 410 Gone with {"deactivated": true, "id": "<did>"} |
| Pending, unbound, unknown, or the organisation has not opted in | 404 |
The root identifier did:web:api.agentvalet.ai resolves at /.well-known/did.json and carries the broker’s own signing key, the one that verifies broker-issued tokens.
Honest limits
This is did:web, not did:webvh: there is no signed key-history log, so a verifier holding a receipt signed under a rotated or deleted key cannot resolve that historical key. Rotation is visible (the fragment changes) but not replayable. If you need verifiable key history, tell us.