# 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. - `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. ## 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 against a registry address you pinned out of band. `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. ## 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. - `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. - **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 returns no mode in its claims, so fleet-only gating needs the RP to check the deed's own envelope `mode` itself — 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. ## 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), and the deed guard (challenge issuance, single-use burn) 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.