# 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 — and since Grantor operates no hosted service, there is no server on the other end of that call. 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.