Skip to content

Concepts: DIDs, passports & signed actions

Identity (did:key)

Each agent/operator is an Ed25519 keypair encoded as a did:key. Because the public key is embedded in the identifier, a verifier resolves it offline — no registry, no network. The trade-off is no rotation/revocation at the DID level; handle those at the passport level (short expiresIn) or move to did:web for long-lived operators.

Passport (a Verifiable Credential)

A passport is a W3C VC signed by the operator about the agent:

{
  "type": ["VerifiableCredential", "AgentPassport"],
  "issuer": "did:key:<operator>",
  "credentialSubject": {
    "id": "did:key:<agent>",
    "name": "support-bot",
    "model": "claude-fable-5",
    "scopes": ["email:send", "crm:read"],
    "operator": "did:key:<operator>"
  }
}

verifyPassport checks the operator's signature and returns this subject. Scopes are free-form strings you define ("resource:action" works well). "*" grants all.

Signed actions

An action is an EdDSA JWT signed by the agent:

{ "iss": "did:key:<agent>", "act": { "type": "email:send", "target": "a@b.com" }, "iat": 1785600000 }

verifyAction proves the JWT was signed by the agent's key. Store these to build a tamper-evident audit trail: even a compromised app server can't forge an action without the agent's private key.

Trust chain

operator DID  ──issues──▶  passport (VC)  ──about──▶  agent DID  ──signs──▶  action (JWT)

A verifier trusts an action if: (1) the action verifies to the agent DID, (2) the passport verifies to a trusted operator DID and names that same agent, and (3) the passport grants the scope the action needs.