wallets & agents · oauth 2.0 / oidc

Stop shipping
static API keys.

OAuth with no authorization server.

Agents authenticate with a key they already hold — scoped to one audience, short-lived, revocable on-chain. People sign in with a wallet they already have — no email, no password, nothing personal to store or leak. Both arrive as a standard OIDC token your stack already verifies.

00 — the whole thing

Verify a deed.
That's the integration.

No SDK ceremony, no callback dance, no service to call. You hold a deed the caller presented; you check it against the chain; you get claims back.

// npm i @grantor/verify
import { DeedVerifier } from "@grantor/verify";

const verifier = new DeedVerifier(RPC, REGISTRY, TENANT, AUDIENCE, ORIGIN, 120, 30);

// the challenge is yours — your nonce, your session store, single-use
const claims = await verifier.verifyAt(deed, challenge, now);

// then mint your own JWT; your OIDC stack carries on unchanged.
// membership, revocation and billing were all checked against the
// chain inside that one call.
# pip install grantor-verify
from grantor_verify import DeedVerifier

verifier = DeedVerifier(RPC, REGISTRY, TENANT, AUDIENCE, ORIGIN, 120, 30)

# the challenge is yours — your nonce, your session store, single-use
claims = await verifier.verify_at(deed, challenge, now)

# then mint your own JWT; your OIDC stack carries on unchanged.
// go get authgrantor.com/grantor-verify-go
v, err := verify.NewDeedVerifier(rpc, registry, tenant, audience, origin, 120, 30)

// the challenge is yours — your nonce, your session store, single-use
claims, err := v.VerifyAt(deed, challenge, now)

// then mint your own JWT; your OIDC stack carries on unchanged.
// Cargo.toml: grantor-verify = { features = ["sovereign"] }
use grantor_verify::sovereign_gate::verify_deed;

// the challenge is yours — your nonce, your session store, single-use
let claims = verify_deed(
    &deed, &policy, &challenge, now, &gate,
).await?;

// then mint your own JWT; your OIDC stack carries on unchanged.
// an agent mints its own deed — no token endpoint, no issuer
const deed = await agent.mintDeed(
  RPC, REGISTRY, TENANT,
  audience,
  origin,     // where YOU are
  challenge,  // yours
  now + 60,
  // vouch — before proving
  vouchSig, vouchEpoch, vouchExp,
  now,
);

// attach it to the request; you verify it the same way every time
fetch(url, { headers: { "X-Grantor-Deed": deed } });

// the key must be registered on-chain for this tenant.

Same call in each SDK — TypeScript, Python, Go and Rust today, more as bandwidth allows. grantor-verify is a library — there is no endpoint, and nothing of ours is running.

which one are you?

Four ways in.
Pick yours.

01 — why

Credentials you never
have to hold.

Three bad options, one bad assumption

Static API keys, an auth SaaS, or building it yourself — all three assume the caller is a person. Yours isn't.

Never hold personal data again

Your app receives a pairwise pseudonym — never an email, a password, or the wallet address. Nothing to breach, nothing to reset. Sovereign sign-in scopes it per app, so it is unlinkable across your other apps too.

Nobody runs anything

The credential is a deed: a self-proving instrument backed by a public register of record, not by a server you have to trust. Your app adds grantor-verify, checks the deed against the registry, and mints its own JWT — no crypto in your request path, no SDK lock-in.

02 — proof

Don't take it on faith.
Read the credential.

Every cell below ships today. Underneath is the entire exchange — what the caller runs, what crosses the wire, and what you run. Three moving parts, none of them ours.

Every combination ships today.
who's authenticatingsovereign
human wallet-derived, app-scoped key
user-sig
agent zero-knowledge membership proof
agent-zk
the caller mints a deed
// an agent, or a wallet
const deed = await agent.mintDeed(
  RPC, REGISTRY, TENANT,
  audience,
  origin,     // where YOU are
  challenge,  // yours
  now + 60,
  // vouch — before proving
  vouchSig, vouchEpoch, vouchExp,
  now,
);

// no issuer was called
the deed — this is the whole credential
{
  "v": 1,
  "mode": "user-sig",
  "tenant": 7,
  "aud": "https://app.example",
  "challenge": "rp-challenge-1",
  "exp": 123456,
  "sub": "794b8e61a658…",
  "pubkey": "02881ee55896…",
  "signature": "d33b8f4077…"
}

mode is user-sig or agent-zk. sub is recomputed from the key on verify, never trusted from the wire. No issuer signed any of it.

you verify it
// 1. a challenge you remember
const challenge = randomUUID();

// 2. check what comes back
const claims = await v.verifyAt(
  deed, challenge, now,
);
// throws ChallengeMismatch,
// Expired, BadProof, StaleRoot,
// TenantInactive, Chain

// 3. your session, your rules
setCookie(mySign(claims.sub));

A revoked agent is rejected. An unpaid tenant is rejected. A replayed challenge is rejected. Enforced by a public contract and your own verify call — with none of our infrastructure running, because there isn't any.

Signing up is the same story: createTenant on that public contract, then USDC to it. No form, no account, no server of ours in either path. Our own dashboard is no exception — it signs in with a deed (admin-sig) through grantor-verify, on its own walled-off entry point: an ordinary relying party cannot accept an admin deed, by design.

03 — agent-native

Your agents adopt it
themselves.

No sales call, no human approving a ticket. An agent mid-task reads a machine spec and wires itself in. Keys are registered, scoped and revoked on-chain — verifiable by anyone, controlled by no one. There's no endpoint to call at all: the agent's proof is the credential.

  1. 1
    Fetch a challenge from your API
    your own nonce, single-use
  2. 2
    Prove membership, in zero knowledge
    against the on-chain tree
  3. 3
    Attach the deed to the request
    X-Grantor-Deed header
  4. 4
    Call the API
    verified in-process, no round trip
sovereign — no issuer, no token endpoint
# sovereign: there is no issuer, and no token endpoint
GET https://api.your-company.example/challenge
# ← { "challenge": "b1c4…" }   the RP's own nonce

# the agent proves membership of the on-chain tree, in zero knowledge,
# bound to that challenge — the proof IS the credential
GET https://api.your-company.example/resource
  X-Grantor-Deed: eyJtb2RlIjoiYWdlbnQtemsi…

# ← 200. verified in-process, against the chain. nothing else ran.

Gate an MCP server. No authorization server.

MCP authorization is optional — but when a server implements it, the spec's route is standing behind an OAuth 2.1 authorization server. A deed-gated server installs a library instead: DeedGuard verifies the credential in-process, against the same on-chain registry that gates billing. Two doors in.

user-sig

Public door

Any wallet may mint — permissionless by design. The deed proves the tenant's bill is paid; authorization past that is your own layer.

agent-zk

Fleet door

Only a commitment the tenant admin registered can produce a proof. REQUIRE_MODE=agent-zk refuses anything else at the exchange.

server.mjs — mount the guard
const g = grantorExpress({
  verifier,
  app,
  challengeEndpoint: "/auth/challenge",
  chainId: CHAIN_ID,
  modes: ["user-sig", "agent-zk"],
  vouchSignature: VOUCH_SIGNATURE,
  vouchEpoch: VOUCH_EPOCH,
  vouchExp: VOUCH_EXP,
});
app.get("/auth/challenge", g.challenge);

Read the MCP guide — the quickstart runs the whole proof on a local chain: just mcp-e2e.

For agents you build or control — off-the-shelf MCP hosts (Claude Desktop et al.) speak spec OAuth.

for machines If you're an AI agent, start here → /llms.txt

04 — pricing

Pay the registry.
Not a vendor.

There's nothing to buy but capacity. Your tenant lives in a public contract, and a tier is simply capacity — apps, signing keys, agents, team members — metered the same way on-chain for every tenant.

Free
$0capped · time-boxed

A real taste, on-chain. No card.

  • 1 app · 1 signing key · 2 agents
  • 1 team member
  • No feature gates on Free, either
  • Expires a fixed window after signup
  • Full docs & llms.txt
Start free
Scale
$99/mo

For growing products & agent fleets.

  • 25 apps · 10 signing keys · 250 agents
  • 25 team members
  • High volume · usage analytics
  • Grace period, never a cliff
  • Priority support
Get started
Enterprise
Customlicense · support

Commercial terms and a human to call.

  • Commercial license & security review
  • Custom caps and terms
  • SLAs & dedicated support
  • Deployment help, in your infra
Talk to us

“Why pay you if you run nothing?”

You're not buying a service — there isn't one running. You're buying standing in the registry: verification itself is gated by it, and a tier is simply capacity. Software is the easy part; standing on a public record anyone can check is the part worth paying for.

“What happens to me if you disappear?”

Nothing. In sovereign mode no process of ours runs in your auth path, and the trust anchor is a public contract readable from any RPC, by anyone. No auth server. No auth company. That's an architecture, not a slogan — there is no service to withdraw.

“Is this actually production ready?”

The contract carries invariant and fuzz coverage, the verifier and the full stack ship end-to-end suites, and the sovereign path has no runtime dependency on us to fail. Don't take it on trust — run the suites before you commit a line of your own code.

You can take your balance back.

Topping up credits your tenant, it doesn't pay us — funds sit in the contract, and withdrawBalance returns anything undrawn to an address you choose. Only periods you actually consumed are ours. There is no minimum, no lock-in and nobody to email, because the withdrawal is a function call and the balance was never in our custody. The registry has no owner power to touch it: an invariant test asserts its USDC holdings always equal the sum of tenant balances.

Billed on-chain in USDC per active period — rates are published in the registry contract, readable before you pay a cent. Free tenants are capped and expire a fixed window after creation; the window is anchored to signup, so switching tiers doesn't reset it. Long enough to ship something real, short enough to stay a trial rather than a permanent free tier.

get started

Register a tenant.
Free is on-chain too.

Add the verifier library — that's the integration. The free tier is a real, capped tenant registered on-chain — no card, no hosted sign-up. Scale up whenever you're ready; same registry, same tenant.

agents → /llms.txt · enterprise terms: get in touch