afauth-trust identifier is reserved, but the operator role is open: anyone may run an attestor under a different iss (acme-trust, enterprise-corp-trust, …) and have consuming services list it in billing.accepted_attestors. Run one for a private agent fleet (your attestor, your verification policy) or as an alternative public attestor. Consumers verify your tokens offline against your JWKS — your uptime bounds token issuance, not verification.
The contract you must implement
An agent links once, then mints per-service tokens; a service fetches your JWKS and verifies offline. Concretely, you expose four endpoints (full shapes in the Trust API reference):| Method | Path | Purpose |
|---|---|---|
POST | /v1/link/start | Agent submits its DID + public key; you return a deep link for a human to confirm, plus a poll URL. |
POST | /v1/link/poll | Agent (signing req_id) retrieves its binding once a human confirms. |
POST | /v1/token | Agent (keyless §5 request signature) mints a JWT bound to a service_did. |
GET | /.well-known/jwks.json | Public verification keys, so services verify offline. Cache 300s; publish new kids ≥900s before use. |
/v1/token MUST have this shape, signed EdDSA with a kid present in your JWKS:
aud= the destinationservice_did. A token minted for service A must not verify against service B.exp − iat≤ 900s. The revocation window. Services must not cache a single token pastexp.sub_hcarries no PII and is pairwise: stable per(human, aud), unlinkable across services. Derive it as an HMAC of the human principal keyed per service — never the raw email/OAuth subject. Agents aredid:keyonly (nodid:web), sosubis the agent’sdid:key.
Stand one up
Choose an issuer and a domain
Pick an
iss that isn’t afauth-trust (e.g. acme-trust) and the HTTPS origin you’ll serve from (e.g. https://trust.acme.example). The iss string is what services add to accepted_attestors and what verifiers match — treat it as stable.Deploy the reference attestor (or implement the contract)
The fastest path is the open-source reference implementation behind
The issuer is currently a constant (
trust.afauth.org: AFAuthHQ/trust. It ships the four endpoints, keyless-mint verification, pairwise sub_h derivation, and kid rotation. Deploy it on any container host (it targets Railway/Docker).It is configured by environment — public origin, advertised JWKS URL, and the keys that protect signing material and derive pseudonyms:| Variable | Purpose |
|---|---|
PUBLIC_BASE_URL | Your public origin, e.g. https://trust.acme.example. Agents sign /v1/token against this, so it must match. |
JWKS_PUBLIC_URL | The canonical JWKS URL you advertise. |
TRUST_KEK_BASE64 | AES-256-GCM key-encryption-key protecting signing-key private material. |
TRUST_PSEUDONYM_KEY_BASE64 | HMAC key that derives the pairwise sub_h. |
TRUST_ADMIN_SECRET | Bearer secret for the operator-only key-rotation endpoints. |
export const ISS in src/lib/signing.ts) pinned to afauth-trust — fork and set it to your identifier. Everything else above is environment configuration. (If you implement the wire shape from scratch instead, the Trust API reference is the normative request/response contract.)Get services to accept you
Verification is offline and per-service — there is no central registry to register with. Ask each consuming service to add your (With
iss to its discovery document and point a verifier at your JWKS:defineService instead of raw new Server, accepted_attestors is derived from the attestor’s issuers automatically — see Accept an attestor.)The service-side walkthrough — discovery declaration, policy, failure modes — is Accept an attestor. Because consumers pin the JWKS URLs they trust, your attestor is honored only by services that explicitly list your iss.Point agents at you
Agents link to your base URL and mint exactly as they would against the default attestor:The agent-side details — multiple bindings, selection, the not-accepted error — are in Use a different attestor.
Operational notes
- Offline verification means your downtime is bounded. A
/v1/tokenoutage stops new mints; already-issued tokens keep verifying against your published JWKS until theirexp. - Rotate keys ahead of use. Publish each new
kidin your JWKS at least one 900-second window before signing with it, so consumer caches refresh without a verification gap. - Cap mint volume per binding. A per-binding daily token limit keeps a compromised agent key from acting as an unlimited attestation oracle (the reference attestor does this).
- You are the global revocation lever. When a human revokes a binding, stop minting for that agent immediately; previously-issued tokens still verify until
exp(≤900s), which is the window you’re trading off.
Where to next
Trust API reference
The normative endpoint, request, and JWT shapes to match.
The trust attestor
The federation model, the
verification claim, and what an attestation proves.Accept an attestor
The service side — declaring and verifying your
iss.Use a different attestor
The agent side — linking to and minting from your attestor.