# 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](../sovereign-tier.md#every-capability-every-language).

## The quickstart

```js
// 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](verify-tokens.md#or-one-call).

> **`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](enterprise-registry.md); the standard SDK cannot
> express one otherwise.

> **Developing? Don't spend anything.** `just devnet` ([Develop
> locally](local-devnet.md)) 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](../../register.html) walks any
wallet through it. On a local devnet:

```bash
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:

```bash
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

- **Humans with a wallet** → [Wallet login](wallet-login.md) (`user-sig`)
- **Humans with a passkey** → [Passkey login](user-passkey.md)
- **Autonomous agents** → [Agent tokens](agent-tokens.md) (`agent-zk`)

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](concepts.md) — the mental model behind all of this.
- [Verify a deed](verify-tokens.md) — errors, session JWTs, scaling the challenge store.
- [Errors](errors.md) — every code a relying party branches on, and which are 401 vs 503.
- [Sovereign tier](../sovereign-tier.md) — the full reference: discovery, origin binding, every check the verifier makes.
