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 — 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 for the full agent-zk recipe once you're enrolled, or Wallet login 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 kit:

#StepContract callWhat it does
1createTenantcreateTenant(address,uint8)Returns your new tenant id; the calling address is its first admin.
2topUptopUp(uint256,uint256)Approve USDC, then credit your tenant's balance. Skip entirely on the free tier — its fee is zero.
3drawPerioddrawPeriod(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.
4enrollregisterAgentKey(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.
5prove— (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#

TierFee (USDC)PeriodTrialAppsIssuer keysAgentsAdminsUsers
0 — Free030 days30 days11212
1 — Pro2530 daysnone5325525
2 — Scale9930 daysnone251025025250

The free tier costs nothing and needs no topUpdrawPeriod 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": "..."}:

[
  { "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.
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 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 don't need mainnet, testnet USDC, or even a plan to try the whole flow:

just devnet

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

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's "all languages or no go" rule):

LanguagePackage
TypeScript@grantor/sdk
Pythongrantor_sdk
Gochaingrantor.com/grantor-go (vanity import path, ships post-launch)
Rustgrantor-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:

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

![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 — the agent-zk recipe 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-sig recipe.
  • 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.

This page is also served as Markdown — agents should read that. The whole tree is indexed for machines in llms.txt.