docs / guide

view as .md

Getting started#

Your app verifies a deed — a self-certifying credential the caller mints itself — with a library call and an RPC read. No auth server runs anywhere. TypeScript below; the same calls in Python, Go and Rust.

The quickstart#

// Imports are by path — @grantor/verify is not yet on npm. This is exactly
// how the runnable example examples/mcp-server/server.mjs does it today.
import express from "express";
import { DeedVerifier } from "./sdk/verify/ts/pkg/grantor_verify_wasm.js";
import { grantorExpress } from "./sdk/verify/ts/src/express.js";
import { Registry } from "./sdk/verify/ts/src/registry.js";

const app = express();

const verifier = new DeedVerifier(
  process.env.RPC_URL,            // any RPC endpoint — the registry check is an eth_call, no gas, no API key
  Registry.canonical(),           // the SDK pins the canonical GrantorRegistry for you — no address here
  Number(process.env.CHAIN_ID),   // which chain the canonical map (and every chain read) resolves against
  Number(process.env.TENANT_ID),  // your tenant (step 0)
  "https://api.example.com",      // audience: what deeds must be scoped to
  "https://api.example.com",      // origin: what THIS deployment is — never taken from a request
  300,                            // max deed TTL, seconds
  30,                             // chain-read cache TTL, seconds
  false,                          // allowInsecureOrigin: true only for a localhost/dev origin
  Math.floor(Date.now() / 1000),  // construction-time clock
);

const g = grantorExpress({
  verifier, app,
  challengeEndpoint: "/auth/challenge",
  chainId: Number(process.env.CHAIN_ID),
  modes: ["user-sig"],            // add "agent-zk" to admit an enrolled agent fleet
  vouchSignature: process.env.VOUCH_SIGNATURE,   // step 0 — bare hex, no 0x
  vouchEpoch: Number(process.env.VOUCH_EPOCH ?? 0),
  vouchExp: Number(process.env.VOUCH_EXP),
});

app.get("/auth/challenge", g.challenge);
app.get("/api/me", g.protect, (req, res) => res.json({ you: req.deed.sub }));

app.listen(8930);

That is the whole server side: mounting auto-publishes /.well-known/grantor-deed, g.challenge issues single-use challenges, and g.protect verifies the deed (crypto + on-chain billing check) and puts the claims on req.deed. Sessions stay yours — mint whatever you already mint, or use the SDK's one-call session JWT.

Registry.canonical() is empty until mainnet launch. The compiled-in chain map has no entries yet, so a real-chain construction errors, naming the chain — expected today, not a bug. Develop against Registry.devnet(...) (below) until then. A custom registry address on a real chain requires an enterprise license; the standard SDK cannot express one otherwise.

Developing? Don't spend anything. just devnet (Develop locally) spins up a free, disposable local chain with the registry, a funded tenant, and an origin vouch already deployed, and writes an rpcUrl/registry/devTenant/adminKey config the snippet above reads — so you can skip Step 0 entirely until you move to a real chain.

Step 0: a tenant and a vouch#

Two one-time artifacts, neither of which is an account.

A tenant on the registry. createTenant plus funding on GrantorRegistry — the register page walks any wallet through it. On a local devnet:

PRIVATE_KEY=$DEPLOYER CONTRACT_ADDR=$REG USDC=$USDC TIER=1 \
  forge script contracts/script/SetupTenant.s.sol --rpc-url $RPC --broadcast

An origin vouch — the tenant admin signs your deployment's origin so holders can refuse imposters before signing anything:

MSG=$(node --input-type=module -e "
import { originVouchBinding } from './sdk/ts/pkg/grantor_sdk_wasm.js';
process.stdout.write(originVouchBinding(1, 'https://api.example.com',
  'https://api.example.com', 0, $(( $(date -u +%s) + 86400 * 30 ))));")
cast wallet sign --private-key $ADMIN_KEY "$MSG"   # strip the 0x → VOUCH_SIGNATURE

Point a caller at it#

The caller fetches /auth/challenge, mints a deed against it, and sends X-Grantor-Deed + X-Grantor-Challenge. Your /api/me answers with a pseudonymous sub. See it run end to end: just mcp-e2e.

Next#

  • Concepts — the mental model behind all of this.
  • Verify a deed — errors, session JWTs, scaling the challenge store.
  • Errors — every code a relying party branches on, and which are 401 vs 503.
  • Sovereign tier — the full reference: discovery, origin binding, every check the verifier makes.

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