Skip to main content
Your agent’s private key is compromised. This runbook locks the attacker out and restores service. It applies to claimed accounts — the agent can’t fix this itself (its signature is exactly what’s compromised), so recovery runs through the human owner and, if you use it, the trust attestor.
Pre-claim (unclaimed) accounts have no owner to recover through — re-create the account with a fresh key. See Rotate keys.

Order matters

Recovery spans two layers that share no transaction — the attestor (stops new attestations) and each service (holds its own revocation list and account state); these are the global and local levers of the revocation model. You are the sequencer, and two asymmetries make the order load-bearing:
  • Per-binding revoke is permanent; whole-account pause is reversible. Resuming a paused account restores minting for every binding you didn’t separately revoke — so a careless pause-then-resume silently re-arms the stolen key.
  • A re-key produces a new DID with no attestor binding. Re-keying restores the agent at your services, but can’t reach attested_only services until you re-link the new key at the attestor.
Do these in order:
  1. Revoke the compromised binding at the attestor — permanent.
  2. Revoke the key at each service that trusts the signature directly (§8.4).
  3. Re-key each of those services to a fresh key (§8.2).
  4. Re-link the new key at the attestor.
  5. Resume the account — only if you blanket-paused it in step 1, and only after the compromised binding is revoked.
1

Revoke the compromised binding at the attestor — permanent

If your agent links to a human through the trust attestor, sign in at the attestor dashboard, find the compromised agent under Linked agents, and Revoke it. This is per-agent and permanent — there is no un-revoke — so it’s the durable containment for the stolen key, and it leaves your other agents running.Revoking stops new attestation tokens for that key immediately. Services that re-check attestation on each request — or that hold an attested session (§10.7) — lock the thief out within the token lifetime (≤ 15 min), since the agent can no longer mint a refresh. Tokens already minted stay valid until they expire — revoking stops new issuance, it does not recall outstanding tokens.
Prefer per-binding Revoke over Pause all agents for a single stolen key. Pausing stops every agent under the human and is reversible: resuming restores minting for every binding you didn’t revoke. Use Pause only as a temporary blanket stop when you can’t tell which agent leaked — and still revoke the compromised binding before resuming (step 5).
2

Revoke the key at each service that trusts it directly

A service that authenticates the agent’s signature — rather than re-checking an attestation on every request — never hears about the attestor revoke. At each such service the owner revokes the key: see Revoke an account. The old key then fails with 401 revoked_key.
3

Re-key each service to resume (§8.2)

Revoke pauses the account. To bring the agent back on the same account, the owner re-keys from a fresh owner session:
// POST /afauth/v1/accounts/me/keys/rekey
// body { current_account_did, new_account_did }
export async function handleOwnerReKey(req: Request, session: OwnerSession) {
  return server.handleKeyReKey(req, session);
}
The owner binding carries forward and account_id is stable — the re-key swaps the credential within the same account (§10.4.4: one account, many devices), it does not create a new one. The agent’s credential becomes the new DID (returned in the response as agent_did); the old DID stays revoked. The agent restarts with the new key.
4

Re-link the new key at the attestor

A re-key gives the agent a new did:key, and the attestor binding was keyed to the old DID — so nothing carries over automatically. Until you re-link, the new key has no attestation and cannot reach attested_only services (the SDK default).Re-run the link flow under the new key, from the same human account:
afauth trust link        # run as the re-keyed agent; the human confirms as before
This creates a fresh binding for the new DID. Because sub_h is keyed on the human, not the key, each service sees the same sub_h as before — per-human policy and state carry forward (§10.5.1). Full flow: Link your agent to a human.
5

Resume the account — only if you paused it

If in step 1 you used the blanket Pause all agents (not per-binding revoke), resume it now at trust.afauth.org/account — resuming requires a fresh sign-in (§7.5). Resume only after the compromised binding is revoked: resuming restores minting for every binding that isn’t individually revoked.If you used per-binding Revoke in step 1, skip this — the account was never paused.

What recovery can and can’t reach

  • The agent’s local state is untrusted. Don’t rely on the agent cleaning up its own cache or ledger — the attacker controls it. Recovery is owner- and service-side only.
  • Signature-only, non-attested services are a blind spot for attestor-side action. Only that service’s own revoke reaches them (step 2). For high-value operations, gate on live attestation — per-request, or a lighter-weight attested session (§10.7) — so an attestor-side revoke actually takes effect.
  • Re-key swaps the credential, not the account. account_id is stable across a re-key (§10.4.4), so the owner binding and any state you keyed on account_id carry forward; only the agent’s credential DID changes (returned as agent_did), so service-internal state keyed on the raw DID must be moved. Agent credentials are did:key — there is no stable-identifier agent DID method, so the credential rolling on re-key is inherent.

You don’t need routine rotation

Ed25519 auth keys have no usage-based wear — there’s no security reason to rotate on a schedule. Rotate to recover from compromise (this guide) or to migrate identifiers, not by the calendar. Spec: §8.2, §8.4, §8.5.