> ## Documentation Index
> Fetch the complete documentation index at: https://docs.afauth.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Identity and keys

> AFAuth names agent accounts with a did:key Decentralized Identifier — the identifier is the public key, verifiable offline with no registry or DNS.

An AFAuth **agent account** is named by a `did:key` **Decentralized Identifier** ([W3C DID](https://www.w3.org/TR/did-core/)) — the identifier *is* the agent's public key. This is the only agent account method in v0.1.

<Note>
  `did:web` is **not** an agent account method. Agents typically run on user
  machines behind home routers, with no stable web origin at which to host a
  DID document — so a DNS-anchored identity isn't usable for the common agent.
  `did:web` still appears elsewhere in AFAuth — for a **service's** own
  `service_did` (services have hosts) and for owner recipients — just never as
  the agent's key.
</Note>

## `did:key`

A `did:key` identifier encodes the public key directly into the identifier:

```
did:key:z6MkiYbwC5honA2sxE7XLAyJMDFibLvVg8FgodBX4A4CaUgr
```

The multibase string after `did:key:` is the multicodec prefix for Ed25519 (`0xed01`) followed by the 32-byte raw public key, base58btc-encoded. A service verifying a `did:key` signature decodes the string locally and uses the recovered public key directly — **no registry lookup, no DNS**. The identifier *is* the key.

Tradeoffs:

* **Verifiable offline.** Anyone can verify a `did:key` signature with just the DID string and the signature itself.
* **Cheap to generate.** `Agent.generate()` in the TypeScript SDK takes microseconds.
* **The DID changes when the key changes.** Rotating the key changes the agent's `did:key`, so external references to the old DID stop resolving. (A service may group a human's credentials under a stable, service-side `account_id` — §10.4.4, "one account, many devices" — so a post-claim re-key keeps the *same account*; the `did:key` itself is still not a stable identifier.) Recovery from a lost or compromised key is therefore **owner-driven** (revoke + re-key). See [Recover a compromised key](/guides/recover-a-compromised-key).

<Note>
  **Why not a stable, DNS-anchored identifier (`did:web`) for agents?** Because
  agents run on laptops and in containers behind home routers — they have no
  stable web origin to host a DID document. The `did:key`-changes-on-rotation
  tradeoff is accepted in exchange for zero-registration, offline-verifiable
  identity, and the recovery story is owner revoke + re-key rather than a stable
  DID. (`did:web` is still right for a service's own `service_did`, which does have a host.)
</Note>

## Key handling

The agent's private key is the **sole credential** for pre-claim operations: while an account is unclaimed, there's no recovery path other than re-creating it. **Once a human has claimed the account**, recovery shifts to the owner — they can revoke the key and re-key the account from an owner-authenticated session, without the agent's (possibly stolen) key. See [Recover a compromised key](/guides/recover-a-compromised-key).

Storage recommendations, in increasing order of robustness:

| Storage                                                    | When it's enough                   | When it isn't                          |
| ---------------------------------------------------------- | ---------------------------------- | -------------------------------------- |
| File on disk (mode `0600`)                                 | Local development, scripts         | Anywhere an attacker might read the FS |
| OS keystore (Keychain, Credential Manager, Secret Service) | Single-user agents                 | Multi-user or server deployments       |
| TPM / Secure Enclave / HSM                                 | Production agents on managed hosts | —                                      |
| Cloud KMS with signing-only access                         | Server-side agents at scale        | —                                      |

The reference CLI stores the active key at `~/.afauth/key.json` with mode `0600`. The TypeScript SDK lets you supply your own seed via `Agent.fromPrivateKey(seed)` so you can plug into any of the above.

## Rotation

Ed25519 verification keys have no usage-based wear, so there's **no need for routine, scheduled rotation**. You change a key for exactly two reasons — to migrate identifiers, or to recover from compromise — and how you do it depends on whether the account is claimed.

**Pre-claim (agent-driven).** While the account is `UNCLAIMED`/`INVITED`, the agent rotates by signing a rotation request with the **old** key; the service decommissions the old DID (adds it to the revocation list) and accepts the new one. The agent's `did:key` changes — the DID *is* the key — so external references to the old DID stop resolving. See [Rotate keys](/guides/rotate-keys).

**Post-claim (owner-driven).** Once claimed, the agent's signature alone can no longer change the key — that would break the [two-step-verify invariant](/concepts/ceremony). The **owner** revokes and re-keys from a fresh owner-authenticated session. This is the compromise-recovery path: see [Recover a compromised key](/guides/recover-a-compromised-key).

## Per-service keys

By default, an agent that signs up to multiple services with the same `did:key` is correlatable across those services. Agents that require unlinkability MUST derive per-service keys — the spec ([§3.3](https://github.com/AFAuthHQ/spec/blob/main/spec/core.md#3-identity)) suggests HKDF-based derivation from a master seed. The v0.1 SDK does not ship a per-service derivation helper; bring your own or generate fresh agents per service.

## Further reading

* [Signing requests](/concepts/signing) — what the agent does with the key.
* [Spec §3](https://github.com/AFAuthHQ/spec/blob/main/spec/core.md#3-identity) — normative identity rules.
* [Spec §12.1](https://github.com/AFAuthHQ/spec/blob/main/spec/core.md#121-key-compromise) — key compromise threat model.
