# Agent onboarding (`agent-zk`)

Terse, machine-actionable steps for an enrolled agent to go from "has a
Semaphore identity registered on-chain" to "holds a verified deed for a
resource API." No browser, no issuer, no human in the loop for the runtime
flow. Prerequisite: a tenant admin has already run `registerZkAgent` for
your identity commitment and published an origin vouch for the app's
origin — see [Agent integration § What still requires a
human](README.md#what-still-requires-a-human-once-on-chain).

## 1. GET discovery

```bash
curl -s https://api.example.com/.well-known/grantor-deed
```

Read `tenant`, `audience`, `modes`, `challenge_endpoint`, `chain`
(`id`/`registry`) and `origin_vouch` from the response. Confirm `"agent-zk"`
is in `modes` before doing anything else.

## 2. GET a challenge

Resolve `challenge_endpoint` (a path, e.g. `/auth/challenge`) against the
**same origin** you fetched discovery from — never a different host:

```bash
curl -s https://api.example.com/auth/challenge
```

Single-use, short-lived (`max_ttl_secs` in the discovery document bounds
how long a deed built against it can live).

## 3. Check the origin vouch BEFORE proving anything

Recover the vouch's signer against the origin **you yourself are talking
to** (never a value read out of the document), and confirm
`isOriginVoucher(tenant, signer, epoch)` on-chain, reading against a
registry **you** pinned out of band (a `RegistryRef` — `Registry.canonical()`
by default; a custom address only via the [enterprise licensed
path](../guide/enterprise-registry.md)) — never `chain.registry` from the
document. Refuse if it fails, is expired, or exceeds the 90-day TTL ceiling.
Full detail, in every language: [Sovereign tier § Origin
provenance](../sovereign-tier.md#origin-provenance).

`ZkAgent.mintDeed` performs this check itself, in every language, before it
generates a proof — so composing the recipe correctly means letting it run
in this order, not skipping straight to step 4.

## 4. Prove membership and mint

Call `ZkAgent.mintDeed` (`mintDeed` in TypeScript/Python/Go;
`grantor_sdk_core::sovereign::mint_deed` composed by hand in Rust) with the
RPC endpoint, your pinned `RegistryRef`, the tenant, audience, the origin you
fetched discovery from, the challenge, an expiry, and the vouch. It rebuilds
your membership proof from the tenant's on-chain event log and produces a ZK
proof bound to all of the above. The
worked example, in every language, is in [Sovereign tier §
Discovery](../sovereign-tier.md#discovery-how-an-agent-finds-all-this).

## 5. Present the deed

```
GET /anything HTTP/1.1
Host: api.example.com
X-Grantor-Deed: <base64url deed>
X-Grantor-Challenge: <the challenge from step 2>
```

The resource server verifies it itself, with `DeedGuard`/`DeedVerifier` —
no issuer, no JWKS, no Grantor process anywhere in this loop.

## Constraints

- The challenge is single-use — a spent or unknown one is
  `UnknownChallenge`.
- `exp` must be within the discovery document's `max_ttl_secs`.
- Your Semaphore identity secret never leaves your process — the SDK's API
  shape only ever emits proofs, never the secret itself.
- The tenant must be `Active` or `Grace` on-chain, and your membership root
  must still be recent (not revoked) — both are on-chain reads the
  verifier performs, not something a retry can spoof.

## Errors to handle

Same codes as any deed verification failure — see
[Errors](../guide/errors.md). The two you are most likely to hit composing
this flow by hand: `BadOriginVouch` (the origin's vouch didn't check out —
refusing before proving is correct, not a bug) and `TenantInactive`
(billing — nothing to do but wait for the operator to top up).

## See also

- [Agent integration](README.md) — the overview and the self-onboarding model.
- [Agent tokens (`agent-zk`)](../guide/agent-tokens.md) — the mode explained for humans.
- [Sovereign tier](../sovereign-tier.md) — the full reference.
- [Self-onboarding](../ONBOARD.md) — not yet enrolled? Start there: becoming a
  tenant, the funding rule, and the `grantor-onboard` kit that automates
  every step this page assumes is already done.
