unclaimed_mode = "attested_only" — it’s the defineService default — and require an attestation on each signed request, a signal that a verified human stands behind the agent. This guide walks you through producing that signal using the canonical trust attestor at trust.afauth.org.
This is a core step of the default agent journey: the standard AFAuth service ships
unclaimed_mode = "attested_only", so your agent must link to a human before it can sign up. You can skip linking only when the target service advertises unclaimed_mode = "free" — then your signed request alone suffices.How it works
The agent never authenticates the human — the trust attestor does. The flow is a three-leg handoff:- Agent → Trust: open a link request, get a deep-link URL and a poll URL.
- Human → Trust (browser): sign in at
trust.afauth.org, confirm the request. - Agent ↔ Trust: poll until confirmed, then mint short-lived per-service JWTs as needed — each signed with the agent key, no bearer token.
Before you start
You need:- An agent identity — an Ed25519 keypair and the
did:keyderived from it.Agent.generate()(TypeScript) orafauth init(CLI) produces one. - A way to show a URL to a human — open in the user’s browser, print to the terminal, send via your own out-of-band channel, etc.
- The destination
service_didfor each service you intend to attest against. Read it from the service’s/.well-known/afauthdiscovery document.
The four steps
The@afauthhq/agent package ships a TrustClient that handles the link flow, polling, and per-service token minting. The examples below use it; the Trust API reference documents the wire surface if you’re integrating from another language.
Start a link request
link_url is what the human visits. req_id is the opaque handle you’ll use to poll. The whole request expires in 30 minutes (expires_in: 1800) — make sure the human can act within that window.Surface the deep link to the human
How you do this depends on the agent runtime:
- Server agents with a chat UI: render
link_urlas a button. - CLI agents: print the URL and optionally
open/xdg-openit. - Headless agents: send the URL via your existing notification channel (email, push, Slack, etc.).
trust.afauth.org/link, signs in if needed (email magic-link, OAuth), reviews the agent label, and clicks confirm.Poll for confirmation
linkPoll returns the binding on confirmation, undefined while pending, and throws on error:trust.afauth.org/account.If you persist the binding and want to skip the link dance on the next run, restore it on construction:Mint per-service attestation JWTs
Whenever you’re about to send a signed AFAuth request to a service that accepts The service verifies the JWT offline against
afauth-trust, call token(serviceDid). It mints a fresh JWT scoped to that service and caches by audience (refreshed at ~80% of TTL):trust.afauth.org/.well-known/jwks.json, checks aud == service_did and sub == signing DID, and either lets the request through or returns 401 invalid_attestation. Trust attestor JWTs cap at 900 seconds; never reuse them across audiences.Handling errors
TrustClient throws TrustHttpError for upstream failures with helpful predicates:
CLI shortcut
The reference CLI (v0.6.0+) wraps the same flow under afauth trust:
Recovery
Revoke an agent’s access — the agent keeps the samedid:key:
- The human signs in at
trust.afauth.org/accountand revokes the binding. The agent can no longer mint — its signed/v1/tokencalls returnbinding_revoked— so attested access lapses within the attestation lifetime. - To restore, the agent starts a new link request (step 1 above) — the human confirms it the same way.
did:key (see Recover a compromised key): the new DID has no binding yet, so there is nothing to revoke here. Just run the link flow under the new key, from the same human account, and the new DID gets a fresh binding. The same sub_h carries forward because it is keyed on the human, not the key (§10.5.1).
Recovery is always agent-initiated, human-confirmed. There is no separate “support” path that can override that — the same property that holds for AFAuth account recovery holds here.
Where to next
What this signal proves
The trust attestor model: verification claim, no PII, governance.
Trust API reference
Endpoint shapes, JWT shape, error codes, rate limits.
Attestation concept
Where
afauth-trust sits among the four §10 attestor classes.The ceremony
Trust attestation vs. owner-claim — what each one binds.