# Self-onboarding for agents

⚠️ **Preview.** The canonical registry map is empty until the mainnet launch
(`docs/strategy/2026-08-01-launch-runbook.md`), so every example below runs
against a **local devnet** — free, disposable, no production USDC and no
production gas spent. The machine-readable twin of this page is
[`/.well-known/grantor-onboard.json`](/.well-known/grantor-onboard.json) —
same content, structured for code rather than prose; both are generated from
the same source (`crates/grantor-sdk-core/src/onboard_manifest.rs`) so they
can never drift from each other.

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, verbatim from the manifest's `funding.rule`
field. In full:

- **Asset**: USDC (`funding.asset`) — what you pay Grantor's on-chain
  registry contract in.
- **Gas asset**: ETH (`funding.gas_asset`) — what you pay the chain itself in,
  for your own transactions. Nobody sponsors this; see
  `docs/strategy/2026-07-28-r3-r6-sponsorship-decision.md` for why gas
  sponsorship is a rejected idea, not an oversight.
- **Chain**: Base, pending the mainnet launch (`funding.chain`) — the
  x402/agent-economy chain, chosen so an agent's own on-chain earnings are
  already denominated in exactly what it needs to spend here. See
  `docs/strategy/2026-07-25-unit-economics-and-chain-selection.md` (R4).
- **x402**: `funding.x402` — "agent earnings settling in USDC on Base are
  directly spendable on tenancy: `topUp` is a public contract call, no
  intermediary." If your agent already earns over x402 on Base, becoming a
  Grantor tenant costs it nothing beyond a contract call it can make itself —
  no facilitator, no new integration, no custody handoff.

There is no operator capital anywhere in this flow: the free tier's fee is
zero, every subsequent transaction is paid by the caller's own wallet, and
Grantor's registry contract has no owner power to move a tenant's balance
(see the billing section of `docs/sovereign-tier.md`). Abuse is
self-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 has to
run 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 (`crates/grantor-sdk-core/tests/onboard_manifest.rs`); if you see a
different number on a live deployment, this page is stale, not the contract.

## The kit

**Ships with the mainnet launch.** Until then, inside this repo: `node sdk/onboard/src/cli.js …`

```
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
```

`preflight` is the step to run the moment you read this page: it tells you
exactly what's missing — wrong chain, insufficient ETH for gas, insufficient
USDC for the tier fee — quoting the funding rule above, instead of letting a
later step fail opaquely. The kit never holds or generates a custody secret
beyond your own wallet key (read from an environment variable you set); it
never talks to any Grantor server, because 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
node sdk/onboard/src/cli.js 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 don't need mainnet, testnet USDC, or even a plan to try the whole flow:

```bash
just devnet
```

starts a real disposable `anvil` chain, deploys a fresh registry + mock USDC,
and creates and funds a tenant automatically — see [Develop
locally](guide/local-devnet.md) for the full config it writes to
`.grantor/grantor-devnet.json`. Point the kit at it (using the fast path until
mainnet launch):

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

Everything above runs with zero real funds and zero real gas — the fastest
way to see the whole loop (refused → onboarded → minting deeds) before
touching a real chain.

## 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.
Both files are generated together (`just onboard-gen`) from
`crates/grantor-sdk-core/src/onboard_manifest.rs`, which is why they can
never say two different things.

## 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.
