# Agent tokens (`agent-zk`)

An enrolled agent authenticates with an **`agent-zk` deed**: a zero-knowledge
proof that it belongs to your tenant's on-chain membership registry, without
revealing *which* member. There's no browser, no issuer, no `/token`
endpoint, and no client secret — `ZkAgent.mintDeed` (every language) is the
entire mint API. If you haven't already, read [Concepts](concepts.md) for the
mental model (deeds, pseudonymity, the on-chain trust anchor).

## Don't need on-chain enrolment? Use `user-sig` instead

If your agent is authenticating straight to an app's own API and you don't
need per-agent revocation or anonymous membership, the permissionless
`user-sig` mode needs no enrolment round trip — any signing key works. See
[Wallet login](wallet-login.md); the same `authenticate()` helper composes
discover → challenge → mint for you:

```ts
import { authenticate } from "@grantor/agent";

const deed = await authenticate("https://api.example.com", signMessage, {
  chainReader,
  chainRegistry: REGISTRY,
});
```

`chainReader` (an `eth_call` seam) and `chainRegistry` (the on-chain registry
address YOU trust, pinned out-of-band) are REQUIRED: `authenticate()` uses
them to verify the application can show a tenant admin's on-chain vouch for
this origin *before* it signs anything. See [Constructing a
`chainReader`](../sovereign-tier.md#constructing-a-chainreader).

## The `agent-zk` recipe

`authenticate()` does not apply to `agent-zk` — an already-enrolled agent
composes `discover()`, a challenge fetch, and `ZkAgent.mintDeed` by hand
instead. The full worked example (all four languages), the same-origin rule,
and the origin-provenance vouch check `mintDeed` performs before it proves,
are documented once, in [Sovereign tier § What it
is](../sovereign-tier.md#what-it-is) and [§ Discovery — how an agent finds
all this](../sovereign-tier.md#discovery-how-an-agent-finds-all-this). Read
those rather than a second copy here.

⚠️ **Read the caveats, not just the recipe.** `mintDeed`'s provenance check
closes `agent-zk` cross-origin linkability and membership disclosure *by
prevention* — but only when your holder actually runs it, and only against
an origin that has published a valid vouch. See [Sovereign tier § Origin
binding — what this closes, and what still does
not](../sovereign-tier.md#origin-binding) and [§ Origin
provenance](../sovereign-tier.md#origin-provenance) for exactly what is and
is not closed today.

## Prerequisites (one-time, on-chain, done by the tenant operator)

Before an agent can mint an `agent-zk` deed, the tenant operator must, on
`GrantorRegistry`:

1. `createTenant` and keep it funded (`Active` or `Grace` — see
   [Concepts](concepts.md#tenants-tiers-and-billing)).
2. `registerZkAgent` — enrol the agent's Semaphore identity commitment in
   the tenant's on-chain membership tree (`registerZkAgentBatch` to enrol
   several agents in one call).
3. Sign and publish an **origin vouch** for the app's origin, so
   `ZkAgent.mintDeed`'s provenance check can pass — see [Sovereign tier §
   Origin provenance](../sovereign-tier.md#origin-provenance). A tenant
   admin revokes a vouch with `bumpOriginEpoch` if an origin is
   decommissioned or compromised.

## Verifying it

Your app verifies an `agent-zk` deed exactly the way it verifies a
`user-sig` one — the same `DeedGuard`/`DeedVerifier`, the same `verify_deed`
call. See [Verify a deed](verify-tokens.md).

## Full agent-integration walkthrough

See [Agent integration](../agents/README.md) and [Agent
onboarding](../agents/onboarding.md).
