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 — 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, asdiscovery="…") — that specific resource's discovery document. It names the tenant, audience, accepted deed modes, the chain and registry it checks against, and thechallenge_endpointyou need before minting anything. Fetch it before doing anything else — see Agent onboarding for the fullagent-zkrecipe once you're enrolled, or Wallet login for the human-drivenuser-sigrecipe.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 —
topUpis 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). 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 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); 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": "..."}:
[
{ "type": "key", "commitment": "0x<64 hex chars>" },
{ "type": "zk", "commitment": "<decimal or 0x-hex uint256>" }
]
type: "key"— abytes32commitment, computed the same way the single-agent path computes its own (agentKeyCommitmentHex(chainId, address):keccak256of the agent's CAIP-10 account string). Eachkeyentry becomes its ownregisterAgentKeycall — the contract has no batch verb for this scheme.type: "zk"— a Semaphore identity commitment (ZkAgent.identity()). Everyzkentry in the file is registered in ONEregisterZkAgentBatchcall, 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.
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 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, not enrollment: see Capabilities § Delegating 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 to start one; it writes a config to .grantor/grantor-devnet.json you point the kit at:
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'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:
## 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:
 <!-- 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 — the
agent-zkrecipe once you're enrolled. - Capabilities — delegate a narrowed slice of an already-enrolled agent's authority to other identities at runtime, the complement to bulk fleet enrollment above.
- Agent-to-agent cards — how agents discover deed-gated resources and mint deeds without human intervention.
- Wallet login — the human
user-sigrecipe. - Develop locally — the disposable devnet this page's quickstart uses.
- Errors — the codes you'll see if a step fails, including the 401 funnel fields this page starts from.
- Sovereign tier — the full reference for what a deed is and how verification works.