Skip to content

Revocation

A DelegationCert carries issued_at and expires_at. The natural way for it to stop being valid is to wait until now > expires_at. But sometimes you need it to stop being valid right now — the agent’s key was leaked, the principal changed their mind, the relationship ended. That’s what revocation is for.

┌─────────────────────────────────────────────┐
│ Principal (Alice) │
│ │
│ Decides to revoke a cert she issued │
│ (e.g. agent's key was leaked). │
└──────────────────┬──────────────────────────┘
│ 1. Signs RevocationList {
│ issuer: Alice
│ revoked_cert_ids: [...],
│ issued_at: now,
│ expires_at: now + 1h
│ hybrid signature
│ }
┌─────────────────────────────────────────────┐
│ Distribution │
│ │
│ - Principal hosts at known URL, OR │
│ - Verify service hosts globally, OR │
│ - Registry / CDN of choice │
└──────────────────┬──────────────────────────┘
│ pulled by verifiers
│ on a short TTL
┌─────────────────────────────────────────────┐
│ Verifier │
│ │
│ On every verify_bundle: │
│ for cert in bundle.delegations: │
│ if cert.cert_id in revocation_list: │
│ return identity_status: revoked │
└─────────────────────────────────────────────┘
{
"version": 1,
"issuer_id": "92cb0a15572d7a71",
"issuer_pub_key": { "ed25519": "...", "ml_dsa_65": "..." },
"revoked_cert_ids": [
"0a3b...c9d2",
"9f4e...12a8"
],
"issued_at": 1800000000,
"expires_at": 1800003600,
"signature": { "ed25519": "...", "ml_dsa_65": "..." }
}

The list is itself signed — by the principal whose cert is being revoked. The verifier authenticates the list (signature valid, not expired) before consulting it. An attacker can’t forge revocations without the principal’s private key.

The verifier is fail-closed on sustained unavailability: if the revocation list can’t be fetched and the cached copy has aged out, the verifier rejects with identity_status: revocation_unavailable. The principal’s intent (“I issued this cert”) is trusted only as long as we can confirm “the principal still wants this cert to be valid.”

Verifier behavior:
Cert covered by a known revocation list endpoint?
├ List fetched recently (within TTL):
│ ├ cert in revoked_cert_ids? → REJECT (revoked)
│ └ cert NOT in revoked list? → continue verification
├ List stale (last fetch > TTL):
│ ├ refresh succeeded: → reapply above
│ ├ refresh failed, cached copy
│ │ still within max-staleness: → continue verification (degraded)
│ └ refresh failed, cached copy
│ beyond max-staleness: → REJECT (revocation_unavailable)

Recommended TTLs:

SurfaceTTLMax staleness
Meeting verification60 s300 s
Voice gateway30 s120 s
API gateway60 s300 s
Physical AI / offline5 min1 hour (mission-duration cached)

Tighter TTL = revocations propagate faster, more network calls. Looser TTL = better availability during partial outages, slower propagation.

When Alice → Agent-A → Agent-B, two revocation lists matter:

Verifier checking Agent-B's bundle:
For each cert in bundle.delegations (Alice→A, A→B):
- Fetch revocation list signed by cert.issuer.
(Alice's revocation list for Alice→A.
Agent-A's revocation list for A→B.)
- If cert.cert_id is in that issuer's revocation list → REJECT.
Both lists must be available and current.

Alice can revoke Alice→A, which transitively invalidates everything Agent-A sub-delegated (because Agent-B’s chain still has the first link). Agent-A can revoke A→B specifically without affecting Alice’s relationship with Agent-A.

The protocol does not support a global “revoke everything from Alice” call — that would be operationally dangerous (Alice fat-fingers and 10,000 customer agents stop working). Revocation is always per-cert. If you need a global revoke, your application enumerates every cert in the chain and revokes them individually.

The protocol doesn’t dictate. Three common models:

  1. Self-hosted by the principal. Alice publishes the signed revocation list at her own domain. Verifiers cache and refresh. Works for small numbers of principals; doesn’t scale to millions of users each running an endpoint.

  2. Managed by Ratify Verify. Identities AI’s commercial product hosts revocation lists for every customer at a global CDN. Verifiers point at one well-known endpoint. Scales; this is what enterprises use.

  3. Customer-controlled but registered. Hybrid: the principal stores the signed revocation list, Ratify Verify hosts a registry that points to where each principal’s list lives. Principal stays in control; Verify provides the addressing layer.

RevocationShort expiry
MechanismSigned list checked at verify timeCert’s expires_at is soon
Propagation speedSeconds (TTL-bounded)Bounded by cert lifetime
Verifier costNetwork call (cacheable)None — just a timestamp check
Operational burdenMust maintain a hosting endpointNone
Best forCompromised keys; relationship terminatedRoutine refresh; minimizing blast radius

A common pattern is short expiry + revocation together. Issue certs with 1-hour expiry by default, refresh on a heartbeat, only revoke when something goes wrong. Worst case is 1-hour blast radius even if revocation fails entirely.

Revocation list encoding and verifier behavior are normative: SPEC.md §11.