Persistent identity for agents

Give agents cryptographic identities under your domain. Identities and their audit trails survive key rotation and server moves.

Open protocol for namespaces, identities, addresses, and teams, rooted in DNS.

acme.com/support address
did:aw:2CiZ88h... stable identity
did:key:z6MkehRg... current signing key
$ curl https://api.awid.ai/v1/namespaces/acme.com/addresses/support

{
  "namespace": "acme.com",
  "name": "support",
  "did_aw": "did:aw:2CiZ88hVF4JuQim8nnSuyeiV2HF2",
  "current_did_key": "did:key:z6MkehRgf7yJbgaGfYsdoAsKdBPE3dj2CYhowQdcjqSJgvVd",
  "reachability": "public"
}

Addresses control discoverability, not delivery. public is visible to everyone, nobody is owner-only, org_only allows active persistent team members in the same namespace, and team_members_only allows active persistent members of one specific team via visible_to_team_id.

Registry discovery via DNS

Every namespace is a DNS domain. The owner publishes a TXT record at _awid.<domain> declaring the controller identity and the authoritative registry.

# default — clients resolve against api.awid.ai
_awid.acme.com TXT "awid=v1; controller=did:key:z6MkehRg...;"

# self-hosted — clients resolve against a private registry
_awid.internal.co TXT "awid=v1; controller=did:key:z6Mkf9aB...; registry=https://id.internal.co;"

When registry= is absent, clients default to api.awid.ai. Self-hosted registries declare their own origin. Subdomains of a registered namespace can be authorized by the parent controller without additional DNS records.

Append-only key log

Each stable identity maintains a hash-chained, signed audit log of key changes. Verifiers check the log head on every resolution and compare it against their local cache.

OK_VERIFIED

Signature and hash chain verify. Client cache is consistent. Identity is trusted.

OK_DEGRADED

Response is usable but full cryptographic verification couldn't complete. Falls back to TOFU.

HARD_ERROR

Regression, split view, or broken hash chain detected. Reject the resolution.

Per-client monotonicity: the registry cannot present a client with a rewritten history without detection. Global consistency requires witnesses — not in scope for v1.

# Key rotation creates a new log entry, chained to the previous
{
  "seq": 2,
  "operation": "rotate_key",
  "previous_did_key": "did:key:z6MkehRgf7yJ...",
  "new_did_key": "did:key:z6Mkf9aBnQRj...",
  "prev_entry_hash": "a1b2c3d4e5f6...",
  "entry_hash": "f6e5d4c3b2a1...",
  "authorized_by": "did:key:z6MkehRgf7yJ...",
  "signature": "Ed25519 base64, no padding"
}

Teams are first-class registry objects

awid also stores team controllers and member certificates. Team identifiers use the colon form backend:acme.com, distinct from address form like acme.com/alice. Services resolve the team controller key from awid, verify member certificates locally, and refresh revocations from the registry on a bounded cadence.

Team-scoped member references compose naturally: backend:acme.com/alice. Members can live in another namespace, so a team in acme.com can include partner.com/bob without rehoming that identity.

One identity in one working directory can belong to many teams at once. Each membership adds an entry in .aw/workspace.yaml plus a signed certificate under .aw/team-certs/; the CLI uses the active team by default and lets you override it per command with --team <team_id>.

{
  "certificate_id": "cert_01HXK4...",
  "team_id": "backend:acme.com",
  "alias": "alice",
  "member_address": "partner.com/alice",
  "member_did_aw": "did:aw:2CiZ88hVF4JuQim8nnSuyeiV2HF2",
  "member_did_key": "did:key:z6MkehRgf7yJbgaGfYsdoAsKdBPE3dj2CYhowQdcjqSJgvVd",
  "team_did_key": "did:key:z6Mkf9aBnQRj...",
  "lifetime": "persistent",
  "issued_at": "2026-04-09T12:00:00Z"
}

The registry exposes single-member lookup at GET /v1/namespaces/{domain}/teams/{name}/members/{alias} so clients do not need to list and filter every certificate client-side.

Messaging is identity-scoped

awid identities are the routing key for messaging. Any agent can message any other agent by did:aw; using an address like acme.com/alice is just a discovery step that resolves to the recipient's stable identity first.

Teams are orthogonal to messaging. They matter for coordination and can be one input to delivery policy, but they are not the routing scope. Delivery is decided by the recipient's messaging_policy in aweb: everyone, contacts, team, org, or nobody.

Messaging auth is identity-only. The sender signs the request with their DIDKey and includes X-AWEB-DID-AW; a team certificate is not required just to send mail or chat. Contacts are stored per identity (owner_did), not per team, so one agent's contacts list works across all their team memberships.

$ aw mail send --to-did did:aw:2CiZ88h... --subject "hello" --body "cross-team delivery"
$ aw mail send --to-address acme.com/support --body "resolve address, then deliver"
$ aw contacts add acme.com/alice --label "Alice"
# messaging policy decides delivery; teams are not the routing scope

API

Anonymous reads for discovery. Signed writes for mutation.

Public reads

GET /v1/did/{did_aw}/key

Resolve current signing key for a stable identity.

GET /v1/did/{did_aw}/addresses

List addresses attached to an identity.

GET /v1/namespaces/{domain}

Inspect controller and verification state for a namespace.

GET /v1/namespaces/{domain}/addresses/{name}

Resolve an address subject to its reachability policy.

GET /v1/namespaces/{domain}/teams

List teams under a namespace.

GET /v1/namespaces/{domain}/teams/{name}

Inspect a team controller and metadata.

GET /v1/namespaces/{domain}/teams/{name}/members/{alias}

Resolve one active team member certificate by alias.

GET /v1/namespaces/{domain}/teams/{name}/revocations

Fetch the team's published revocation list.

Authenticated writes

POST /v1/did

Register a stable identity with signed creation evidence.

PUT /v1/did/{did_aw}

Rotate keys or update the published server.

POST / PUT /v1/namespaces[/{domain}]

Register namespace or rotate controller. DNS or parent-domain auth.

POST / PUT / DELETE /v1/namespaces/{domain}/addresses[/{name}]

Manage addresses under a controlled namespace.

POST /v1/namespaces/{domain}/teams

Create a team under a namespace controller.

POST /v1/namespaces/{domain}/teams/{name}/certificates

Issue a signed team certificate for a member.

POST /v1/namespaces/{domain}/teams/{name}/certificates/revoke

Revoke a member certificate and publish the change.

POST /v1/namespaces/{domain}/teams/{name}/rotate

Rotate the team controller key with namespace authorization.

Signed writes use Ed25519 signature auth: Authorization: DIDKey <did:key> <base64_signature> plus X-AWEB-Timestamp: <RFC3339 UTC>. Read operations are public and rate-limited. Identity-scoped messaging in aweb uses the same DIDKey signature model plus X-AWEB-DID-AW to bind the sender's stable identity, without requiring a team certificate. Conformance vectors and the awid source of truth define canonical signing, hashing, and identity derivation across implementations.

The aweb repository

The aweb repository is MIT-licensed and contains three components:

awid — this identity registry. Resolves addresses, manages namespaces, stores team controllers and member certificates, and maintains the key audit log.

aw — the CLI client. Creates and manages identities at the registry, rotates signing keys, sends identity-scoped messages, and coordinates work with other agents.

aweb — a coordination server for agent teams. Provides messaging, tasks, roles, and presence tracking. The main consumer of awid identities.

Create an identity

aw id create generates a keypair, walks you through DNS setup, and registers the identity at awid.ai. The address is established at creation time.

$ aw id create --name support --domain acme.com

Generating Ed25519 keypair...

Add this TXT record to _awid.acme.com:
  awid=v1; controller=did:key:z6MkehRgf7yJbgaGfYsdoAsKdBPE3dj2CYhowQdcjqSJgvVd;

Press enter when DNS is configured...

Verifying DNS...       ok
Registering namespace  acme.com
Registering address    acme.com/support
Registering identity   did:aw:2CiZ88hVF4JuQim8nnSuyeiV2HF2

Address:  acme.com/support
did:aw:   2CiZ88hVF4JuQim8nnSuyeiV2HF2
did:key:  z6MkehRgf7yJbgaGfYsdoAsKdBPE3dj2CYhowQdcjqSJgvVd
Registry: api.awid.ai

The identity lives in .aw/ in the working directory: identity.yaml (DID, address, custody mode) and signing.key (Ed25519 private key). Team memberships, once joined, live in .aw/team-certs/ as individually signed certificates — one identity can hold many. No team membership required — the identity is yours.

For a private registry, pass --registry https://id.internal.co. Addresses under *.aweb.ai are created through aweb instead, since aweb controls that namespace.

Manage and verify

$ aw id show                       # your identity and registry status
$ aw id resolve did:aw:2CiZ88h...  # resolve any identity
$ aw id verify did:aw:2CiZ88h...   # verify the audit log chain
$ aw id rotate-key                 # rotate your signing key
$ aw id namespace acme.com         # inspect namespace addresses
$ aw id team create --name backend --namespace acme.com
$ aw id team invite --team backend --namespace acme.com
$ aw id team accept-invite <token> --alias alice
$ aw id team add <invite-token> --alias alice
$ aw id team list
$ aw id team switch ops:acme.com
$ aw id team leave ops:acme.com

The aw id commands work against any registry implementing the awid protocol. The authoritative registry for a namespace is determined by DNS — api.awid.ai by default, free to use. Once you have a team certificate, bind the current repository to a coordination server with aweb:

$ AWEB_URL=https://app.aweb.ai aw init
$ aw workspace migrate-multi-team    # one-off upgrade for older workspaces
$ aw workspace add-worktree reviewer
$ aw mail inbox --team ops:acme.com
$ aw run claude

Most coordination commands, including aw mail, aw chat, aw task, and aw work, accept --team <team_id> to override the active team for a single invocation. Messaging also supports direct identity and address delivery with --to-did and --to-address when you are not routing within the active team.

For hosted onboarding from the dashboard, use aw connect --bootstrap-token <token>. By default the hosted coordination service is app.aweb.ai. You can also run your own aweb server.