# Grantor > Identity for callers that aren't people. Agents authenticate with a key they > already hold — scoped to one audience, short-lived, revocable on-chain — > instead of a static API key that can't be scoped, rotated per-agent, or > revoked without breaking everyone. People sign in with a wallet: the relying > app receives a pairwise pseudonym scoped to `(tenant, origin)` — never an > email, a password, or a wallet address. Both arrive as a self-certifying > credential called a **deed**, which the relying app verifies in-process > against the public on-chain `GrantorRegistry` — the trust anchor and billing > ledger — and turns into its own standard OIDC token any JOSE library > verifies. There is no authorization server anywhere in this path: nothing > to run, trust, or subpoena. Grantor operates no hosted service. If you are an AI agent, you can integrate autonomously: no human, no browser, no consent screen required. Minting a deed and verifying one both ship in TypeScript, Python, Go and Rust. Becoming a tenant is not a signup either: `createTenant` on the registry contract, then USDC to that same contract. No form, no account, no server on the other end. Grantor's own admin dashboard authenticates the same way it asks you to: with a deed, not an account (`admin-sig`, below). Trust is anchored on-chain: the registry of agent-key commitments and tenant billing status is public, tamper-evident and pay-gated. Any verifier reads it over any public RPC. Subjects are pairwise and pseudonymous — unlinkable across tenants. In `user-sig` mode the derivation scopes per `(tenant, origin)` — deliberately NOT per audience, so one deployment sees one subject across all its own APIs and can assign it a role; subjects stay unlinkable across origins and across tenants. In `agent-zk` mode the proof discloses membership in the tenant's registry without revealing which member. ## Deed modes - `user-sig` — a human's wallet-derived, app-scoped key. Verified by `verify_deed`. On-chain check: tenant billing only. - `agent-zk` — an enrolled agent's zero-knowledge proof of membership in the tenant's on-chain registry tree, without revealing which member. Verified by `verify_deed`. On-chain check: root recency (revocation) + tenant billing. - `user-zk` — a human's zero-knowledge proof of membership in the tenant's own **user allowlist** — a separate on-chain tree from `agent-zk`'s — without revealing which member. Same crypto as `agent-zk`, over a different tree. Verified by `verify_deed`. On-chain check: root recency (revocation) + tenant billing. - `user-passkey` — a human's WebAuthn/passkey credential, bound to your origin by the browser (the strongest anti-phishing of the human modes). Verified by `verify_deed`. On-chain check: tenant billing only. - `user-1271` — a smart-contract wallet (Safe & co) proving control via EIP-1271. Identity is the wallet address. Verified by `verify_deed` — the signature check is an on-chain `isValidSignature` call, so it is unverifiable offline by design. On-chain check: wallet signature + tenant billing. - `admin-sig` — not a mode your RP consumes. It's what Grantor's own control-plane dashboard uses to authenticate its admins: a wallet signature that recovers to an address. Verified only by `verify_admin_deed` — `verify_deed` rejects it, so an ordinary relying party structurally cannot accept one. Makes no on-chain check, deliberately: gating it on tenant billing would lock a lapsed tenant's admin out of the one page they need to pay from. - `zk-chain` — ⚠️ **preview.** A zero-knowledge proof of an anonymous, bounded-depth delegation chain rooted at a tenant member, revealing none of the intermediate keys, grants, or the true chain length. Authenticates an (effective) authority commitment, not a principal — verified by a dedicated entry point, `verifyDelegationChainAt` / `grantor_verify::sovereign_gate::verify_delegation_chain`, never `verify_deed`. Single-contributor deterministic trusted setup, pending external cryptographic review — do not use for production authority. ## Sovereign flow 1. **Discover.** `GET` the relying party's `/.well-known/grantor-deed` document (`grantor_sdk_core::discovery::DISCOVERY_PATH` in Rust; `discover(origin)` in TypeScript/Python, `Discover` in Go). It names the `tenant`, `audience`, `challenge_endpoint`, accepted `modes`, `chain` (id + registry address), and a required `origin_vouch`. 2. **Verify origin provenance, before signing or proving anything.** Recover the vouch's signer against the origin you yourself are talking to — never a value read out of the document — and confirm `isOriginVoucher` on-chain. The SDK pins the canonical registry for you (`Registry.canonical()`, the default everywhere); a custom address exists only on the licensed [dedicated path](/docs/guide/enterprise-registry.html). `authenticate()` / `signInWithDeed()` (`user-sig`) and `ZkAgent.mintDeed` (`agent-zk`) both run this check first, in all four languages: a hostile origin never gets a signature or a proof out of you. Exception: Rust has no `authenticate()` / `signInWithDeed()` — a Rust `user-sig` holder composes this check by hand (see the four-step recipe in `docs/sovereign-tier.md`; leaving the step out is silent, everything still "works", against every origin). The `agent-zk` mint path runs the check inline in all four languages, including Rust. 3. **Challenge.** Fetch a single-use challenge from the document's `challenge_endpoint`. 4. **Mint.** `user-sig`: derive a root seed from one wallet signature, derive a per-`(tenant, origin)` app key, sign the challenge — `mintUserDeed` / `signInWithDeed`. `agent-zk`: rebuild your Merkle membership path from the on-chain tree and produce a ZK proof bound to the challenge, audience and an expiry — `ZkAgent.mintDeed`. Neither step is a token exchange or an issuer round-trip; the proof or signature itself is the credential. 5. **Present.** Attach the deed and the challenge in their own headers, `x-grantor-deed` and `x-grantor-challenge` — never the challenge read back out of the deed's own claimed fields, which would let a caller pick its own nonce. 6. **Verify.** The relying party calls `DeedVerifier.verifyAt` (TypeScript), `.verify_at` (Python), `.VerifyAt` (Go), or `grantor_verify::sovereign_gate::verify_deed` (Rust, crate feature `sovereign`): cryptography first, then one `eth_call` for tenant billing status (plus root recency for `agent-zk`). `sub` is a pseudonym **recomputed** from the proof, never trusted off the wire. The relying party then mints its own local JWT; Grantor never sees it. ## Not yet a tenant? A 401 tells you how to become one Every `DeedGuard` rejection is a machine-followable funnel, not a dead end. A `401` carries `WWW-Authenticate: Grantor-Deed realm="…", discovery="…"` plus two additive body fields: `discovery` (that resource's own discovery document) and `learn` — a stable URL, `/.well-known/grantor-onboard.json`, identical everywhere, pointing at the onboarding manifest (schema `v: 1`) and its narrative twin, [`/docs/ONBOARD.html`](/docs/ONBOARD.html). Both describe becoming a paying tenant end to end — `createTenant` → `topUp` → `drawPeriod` → enroll a key → mint and verify your first deed — with the exact funding rule ("you pay your own gas — nobody sponsors you, and nobody can drain anyone else's funds") and the `npx grantor-onboard` kit that automates every step. No human, no browser, no consent screen required. ## Verify a deed - Ships as `DeedVerifier` in TypeScript, Python and Go, and `grantor_verify::sovereign_gate::verify_deed` in Rust — call `verifyAt` / `verify_at` / `VerifyAt` / `verify_deed` with the deed, your expected challenge, and a chain gate. - The deed guard — challenge issuance, single-use burn, the shared error codes — ships in all four languages, with thin adapters for Express, FastAPI and `net/http`. It burns a challenge BEFORE verifying, so a flood of bogus deeds cannot probe which challenges are live. - Errors are a structured enum, not a flat string: `TenantInactive` (billing — the customer must top up) from `BadProof` / `StaleRoot` (attack or revoked) from `Chain` (an RPC problem worth retrying). All map to HTTP 401 except `Chain`, which maps to 503 — an RPC outage is not the caller's fault. - **Multi-RPC quorum (optional)**: construct the verifier over several independent RPC endpoints — `newQuorum` / `new_quorum` / `DeedVerifierNewQuorum` / `new_quorum` (TS/Python/Go/Rust) — and every chain read must agree unanimously; a single lying or divergent provider vetoes the read (`QuorumDivergence` → 503) instead of being trusted. A single URL is simply a quorum of one. The guarantee is provider independence; a lie all providers agree on is still undetectable. - `x-grantor-deed` carries the deed (base64url or bare JSON); `x-grantor-challenge` carries the challenge, in its own header, always — never inside the deed itself. - **Session JWT (optional)**: mint your own session however you already do — that stays first-class — or make one call for a standard ES256 JWT signed with **your own key** and verifiable by any JOSE library: `sessionJwt`/`session_jwt`/`SessionJwt`/`session_jwt` (TS/Python/Go/Rust). - **Enterprise dedicated registries (optional)**: the standard SDK cannot express a custom registry address anywhere — every construction takes a `RegistryRef`, and `Registry.canonical()` is the default everywhere. A dedicated deployment (your own `GrantorRegistry` instance) requires an operator-signed license grant, verified offline against compiled-in operator keys, checked at construction and on every verify/mint call (grace, then hard refusal past `exp + grace_secs`). See `/docs/guide/enterprise-registry.html`. - **MCP servers**: gate a remote MCP server without an authorization server — install the deed guard, publish the discovery document; agents authenticate with deeds. Two modes: `user-sig` for any wallet-holding caller (billing is the only gate), or `agent-zk` to gate a fleet you control by on-chain enrollment — membership is the authorization for who can mint a valid `agent-zk` deed. Advertising both modes means either credential works at the RP's exchange endpoint by default — the guard verifies whichever mode a deed declares, and its claims now do carry the verified `mode` back out (crypto-checked, never the wire envelope), but that field existing enforces nothing by itself: fleet-only gating still needs the RP to check it — cheaply pre-verify against the deed's own envelope `mode` for a free refusal, then authoritatively against the verified claims — see /docs/guide/mcp-server.html. Scope: this serves agents you build or control; off-the-shelf MCP hosts (Claude Desktop et al.) speak spec OAuth 2.1 and are out of scope for this flow. - **Agent-to-agent card discovery** — a resource publishes its deed requirements in a structured card; agents parse the card and mint deeds autonomously. See /docs/guide/a2a.html for the recipe agents follow when they discover a card. ## Notes - Capability matrix, precisely: **every capability ships in TypeScript, Python, Go and Rust.** The `user-sig` deed holder path (wallet login: derive a root seed, derive an app key, mint a user deed), the `agent-zk` deed holder path (zero-knowledge membership proving), the `admin-sig` deed holder path (dashboard login — not something your RP needs, listed here only for completeness), deed verification (the RP side), the deed guard (challenge issuance, single-use burn), the registry-ref helpers (`Registry.canonical()`/`.devnet()`/`.dedicated()`), dedicated-registry license verification, and license-status introspection are all four-language, proven against one shared conformance-vector file plus a live-chain end-to-end run per language. `just capability-matrix` fails the build if any cell is missing. If you are an agent deciding what to attempt: whatever your language, you can do all of it. This is a standing product rule — a capability that ships in one language has not shipped. - Billing is prepay-then-draw and custodial to nobody: `topUp` credits your tenant inside the registry contract, `drawPeriod` is **permissionless** and transfers one period's fee to an immutable treasury, and `withdrawBalance` returns any undrawn balance to an address a tenant admin chooses. Only consumed periods are non-refundable. The contract has no owner power to move tenant balances, and an invariant test asserts its USDC holdings always equal the sum of those balances. - `Grace` precedes `Inactive`, and deeds already minted stay valid to their own `exp` — going unpaid never invalidates a credential already in the world. - Standards: OpenID Connect, OAuth 2.0, EIP-191, EIP-4361 (SIWE), RFC 7638 (JWK thumbprint), RFC 7636 (PKCE), RFC 9474 (blind RSA), Semaphore (ZK membership), HKDF.