# Self-onboarding for agents

⚠️ **Preview.** The canonical registry map is empty until the mainnet launch,
so every example below runs against a **local devnet** — free, disposable, no
production USDC or gas. The machine-readable twin of this page is
[`/.well-known/grantor-onboard.json`](/.well-known/grantor-onboard.json) —
same facts, structured for code; the two are generated together and cannot
drift.

This page answers one question: **you tried to call a Grantor-gated resource
and got refused — what do you do next?** No human, no browser, no consent
screen required to get from "refused" to "a paying tenant minting deeds."

## You hit a 401 — what it means

A `DeedGuard`-protected resource that refuses you sends this:

```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Grantor-Deed realm="https://api.example.com", discovery="/.well-known/grantor-deed"
Content-Type: application/json

{
  "error": "MissingDeed",
  "error_description": "no deed presented",
  "discovery": "/.well-known/grantor-deed",
  "learn": "https://chaingrantor.com/.well-known/grantor-onboard.json"
}
```

Two things to read out of it:

- `discovery` (also in the header, as `discovery="…"`) — **that specific
  resource's** discovery document. It names the tenant, audience, accepted
  deed modes, the chain and registry it checks against, and the
  `challenge_endpoint` you need before minting anything. Fetch it before
  doing anything else — see [Agent onboarding](agents/onboarding.md) for the
  full `agent-zk` recipe once you're enrolled, or [Wallet
  login](guide/wallet-login.md) for the human-driven `user-sig` recipe.
- `learn` — this document's own URL. It is the SAME for every Grantor-gated
  resource anywhere: a stable, global answer to "what is this, and how do I
  become someone who can pass?" That's what the rest of this page covers.

If you have never interacted with Grantor before, you are not yet a tenant —
`discovery` describes how to *authenticate to an existing tenant's app*;
becoming a tenant yourself is a separate, earlier step, covered next.

## The funding rule

> you pay your own gas — nobody sponsors you, and nobody can drain anyone
> else's funds

That's the whole funding model. In full:

- **Asset**: USDC — what you pay Grantor's on-chain registry contract.
- **Gas asset**: ETH — what you pay the chain for your own transactions. Nobody
  sponsors this; gas sponsorship is a deliberate non-goal.
- **Chain**: Base, pending the mainnet launch — the x402/agent-economy chain,
  so an agent's own on-chain earnings are already denominated in what it spends
  here.
- **x402**: if your agent already earns over x402 on Base, becoming a tenant
  costs it nothing beyond a contract call it makes itself — `topUp` is a public
  call, no facilitator, no custody handoff.

No operator capital touches this flow: the free tier's fee is zero, every other
transaction is paid by the caller's own wallet, and the registry contract has
no owner power to move a tenant's balance (see [Sovereign
tier](sovereign-tier.md)). Abuse is rate-limited by the abuser's own gas bill,
not by anything Grantor enforces.

## The five steps

Every path — human-operated or fully autonomous — is the same five on-chain
calls plus one off-chain proof. Each has a one-liner in the
[`grantor-onboard`](#the-kit) kit:

| # | Step | Contract call | What it does |
|---|---|---|---|
| 1 | `createTenant` | `createTenant(address,uint8)` | Returns your new tenant id; the calling address is its first admin. |
| 2 | `topUp` | `topUp(uint256,uint256)` | Approve USDC, then credit your tenant's balance. Skip entirely on the free tier — its fee is zero. |
| 3 | `drawPeriod` | `drawPeriod(uint256)` | Draws one period's fee from your balance to the treasury and flips `status` to `Active`. Permissionless — anyone (a keeper, a cron job, you) can call it once a period is owed. |
| 4 | `enroll` | `registerAgentKey(uint256,bytes32)` or `registerZkAgent(uint256,uint256)` | Registers a key commitment (plain) or a Semaphore identity commitment (`agent-zk`, membership-hiding) for the caller who will mint deeds. |
| 5 | `prove` | — (off-chain) | Mint a deed and verify it locally, end to end, against your own new tenant — proof the whole chain works before you point it at anything real. |

`createTenant`/`topUp`/`drawPeriod`/`registerAgentKey` are all cheap contract
calls — cents in gas on an OP-stack L2 like Base. `registerZkAgent` is the
one heavier step (~801k gas, mostly Merkle-tree Poseidon hashing — see
[MCP server auth § economics](guide/mcp-server.md)); still cents on Base, and
`registerZkAgentBatch` amortizes it further across a fleet.

## Tiers

| Tier | Fee (USDC) | Period | Trial | Apps | Issuer keys | Agents | Admins | Users |
|---|---|---|---|---|---|---|---|---|
| 0 — Free | 0 | 30 days | 30 days | 1 | 1 | 2 | 1 | 2 |
| 1 — Pro | 25 | 30 days | none | 5 | 3 | 25 | 5 | 25 |
| 2 — Scale | 99 | 30 days | none | 25 | 10 | 250 | 25 | 250 |

The free tier costs nothing and needs no `topUp` — `drawPeriod` still runs once
to flip `status` to `Active` (a zero-fee draw is still a draw). These numbers
are pinned against the real deploy script by an automated test; if a live
deployment shows a different number, this page is stale, not the contract.

## The kit

**Ships with the mainnet launch.**

```
npx grantor-onboard preflight   # chain + balance checks against these numbers, per-prerequisite ✓/✗
npx grantor-onboard create      # createTenant → topUp (if fee > 0) → drawPeriod
npx grantor-onboard enroll      # registerAgentKey (or --zk for registerZkAgent, or
                                 # --commitments <file.json> to bulk-enroll a fleet)
npx grantor-onboard prove       # mint a deed, verify it locally, print the verified claims
npx grantor-onboard serve       # the same four verbs as MCP tools, for MCP-native agents
```

Run `preflight` the moment you read this page: it names exactly what's missing
— wrong chain, insufficient ETH for gas, insufficient USDC for the tier fee —
instead of letting a later step fail opaquely. The kit holds no custody secret
beyond your own wallet key (read from an environment variable you set), and
talks to no Grantor server — there is none, only the chain and the resource's
own public discovery endpoint.

## Bring your fleet

Running more than one agent under this tenant? `enroll --commitments
<file.json>` registers a whole fleet's commitments in as few as **two**
transactions total, instead of one `enroll` per agent — which would also
need each agent's own wallet key, since the single-agent path above always
registers the CALLING wallet's own commitment.

The file is a JSON array; each entry is `{"type": "key"|"zk", "commitment": "..."}`:

```json
[
  { "type": "key", "commitment": "0x<64 hex chars>" },
  { "type": "zk", "commitment": "<decimal or 0x-hex uint256>" }
]
```

- **`type: "key"`** — a `bytes32` commitment, computed the same way the
  single-agent path computes its own (`agentKeyCommitmentHex(chainId,
  address)`: `keccak256` of the agent's CAIP-10 account string). Each `key`
  entry becomes its own `registerAgentKey` call — the contract has no batch
  verb for this scheme.
- **`type: "zk"`** — a Semaphore identity commitment (`ZkAgent.identity()`).
  Every `zk` entry in the file is registered in ONE `registerZkAgentBatch`
  call, producing a tree byte-identical to registering each commitment
  one-by-one, but paying the Merkle-root recomputation once for the whole
  batch instead of once per agent.

```bash
grantor-onboard enroll --config <path> --tenant <id> \
  --commitments fleet.json
# {"enrolled":{"keys":3,"zk":12},"tenantId":"7"}
```

The **whole file is validated before any transaction is sent** — one
malformed entry fails the whole call, naming its index and the reason, with
nothing on-chain yet spent. This is the tenant ADMIN's tool: whoever runs it
must be the admin wallet, and every commitment in the file becomes an
independently-trusted agent key on your tenant, capped by the tier's
`maxAgents` (a single shared budget across the `key` and `zk` schemes — see
[Tiers](#tiers) above).

**A different tool for a different shape of "fleet": delegation.** Bulk
enrollment is for agents you, the admin, already know about and are
registering up front. If instead ONE already-enrolled agent needs to hand a
*narrowed* slice of its own authority to other identities at runtime — no
admin re-enrollment per agent, independently revocable — that's
[capability delegation](guide/capabilities.md), not enrollment: see
[Capabilities § Delegating](guide/capabilities.md#delegating-delegate) for
the `delegate` recipe. The two compose within one org: enroll the root
agent's key once via `enroll` (or a fleet file), then let it delegate to as
many downstream identities as your app's authority model needs, with no
further on-chain calls.

## Try it now: local devnet

You need no mainnet, no testnet USDC, and no plan to run the whole loop. A
disposable local devnet — a fresh `anvil` chain with its own registry, mock
USDC, and a pre-funded tenant — runs refused → onboarded → minting deeds
against zero real funds and zero real gas. See [Develop
locally](guide/local-devnet.md) to start one; it writes a config to
`.grantor/grantor-devnet.json` you point the kit at:

```bash
grantor-onboard preflight --config .grantor/grantor-devnet.json --tier 1
grantor-onboard create    --config .grantor/grantor-devnet.json --tier 1
grantor-onboard enroll    --config .grantor/grantor-devnet.json --tenant <id>
grantor-onboard prove     --config .grantor/grantor-devnet.json --tenant <id>
```

## SDKs

Once you're a tenant, integrate with whichever SDK matches your stack — every
capability ships in all four, always ([Concepts § The polyglot
split](guide/concepts.md#the-polyglot-split)'s "all languages or no go"
rule):

| Language | Package |
|---|---|
| TypeScript | `@grantor/sdk` |
| Python | `grantor_sdk` |
| Go | `chaingrantor.com/grantor-go` (vanity import path, ships post-launch) |
| Rust | `grantor-sdk-core` |

## The manifest

`/.well-known/grantor-onboard.json` is this page's machine-readable twin —
schema `v: 1`, `status: "preview"` until the mainnet map is populated. It
carries the same chain/funding/tier/step/kit/SDK facts in a shape a program
can parse directly, so an agent never has to scrape prose to onboard itself.

## For RP repos

Include this fragment in your project's README or documentation:

```markdown
## Authenticating to this project's deed-gated resources

This project's APIs/MCP servers require a Grantor deed (anonymous, on-chain-gated auth — no signup).
On a 401 with `WWW-Authenticate: Grantor-Deed`: fetch the `discovery` path in the response,
follow `learn` for the full onboarding manifest, or run `npx grantor-onboard preflight`.
Docs: https://chaingrantor.com/docs/ONBOARD.md (once chaingrantor.com is live). You pay your own gas; nobody sponsors you.
```

Add the deed-gated badge to your README:

```markdown
![deed-gated](https://chaingrantor.com/badge-deed-gated.svg) <!-- domain decided 2026-08-12: chaingrantor.com -->
```

Both make the onboarding path self-discoverable for agents: a 401 response names the `learn` URL once,
and your README signals deed-gated authentication status before an agent invokes any protected method.

## See also

- [Agent onboarding](agents/onboarding.md) — the `agent-zk` recipe once
  you're enrolled.
- [Capabilities](guide/capabilities.md) — delegate a narrowed slice of an
  already-enrolled agent's authority to other identities at runtime, the
  complement to [bulk fleet enrollment](#bring-your-fleet) above.
- [Agent-to-agent cards](guide/a2a.md) — how agents discover deed-gated resources
  and mint deeds without human intervention.
- [Wallet login](guide/wallet-login.md) — the human `user-sig` recipe.
- [Develop locally](guide/local-devnet.md) — the disposable devnet this page's
  quickstart uses.
- [Errors](guide/errors.md) — the codes you'll see if a step fails, including
  the 401 funnel fields this page starts from.
- [Sovereign tier](sovereign-tier.md) — the full reference for what a deed
  is and how verification works.
