Mandate lets you run an AI agent that spends real funds — using your Anthropic key, your OpenAI account, or a model on your own hardware — while the chain, not the vendor, enforces what it can do. The AI never touches the chain directly. It holds no funds and no authority. Everything it does flows through an on-chain mandate: spending caps you set, a list of recipients you chose, an expiry date, and four independent ways to kill it. Every action it takes — and every action it declines — leaves a signed, hash-chained receipt that anyone can verify without trusting you, us, or the agent.
The thesis in one line: don't put the AI on the chain — put its leash on the chain.
- Your keys stay yours — the agent's authority lives in a contract you own; revoke it from your phone and the daemon halts within a block
- Ask-me-first spending bands — "up to 0.5 on its own, 0.5–20 asks my phone, never more than 25 a day" is enforced in the contract, not in a config file the agent could ignore
- Bring any brain — Anthropic API, any OpenAI-compatible endpoint, or pinned local weights; same leash, same receipts, honest labels for each
- Receipts for everything — each action is a signed receipt, hash-chained to the last and anchored to the chain by Merkle checkpoints; tampering, gaps, and withheld data are detected and named
- A verifier you can read in one sitting — the
watcherCLI is ~200 lines; "don't trust the dashboard" is the product's own advice - One shared contract, no per-agent deploy —
MandateHubis a permissionless public good; anyone issues a mandate on it and owns only their own - No token, no custody, no ETH RPC — commitments on-chain, data off-chain, all access substrate-native
Stand up a private copy of the real Paseo Asset Hub Next runtime (a chopsticks fork — real pallet-revive), deploy the contracts, and get a live mandate to drive with the CLI, daemon, studio, and watcher:
npm install && npm run build && npm run build:pvm
npm run local # deploys + issues a mandate + prints a cheat-sheet; Ctrl-C to tear downFull walkthrough: docs/LOCAL.md.
mandated sets the leash; dot (polkadot-cli) handles the chain. Keys and funding are dot; issuing the mandate is one command:
# chain prep with dot: a session key + its H160, fund it, map it
dot account create session
SESSION_H160=$(dot account inspect session --json | jq -r .h160)
dot paseo-asset-hub-next.tx.Balances.transferKeepAlive session 20000000000 --from owner
dot paseo-asset-hub-next.tx.Revive.map_account --from session
# issue the mandate: builds + signs the rulebook, binds its hash on-chain, funds it
mandated create --hub <HUB> --session $SESSION_H160 --preset dca \
--brain anthropic --model-id claude-sonnet-5 --allow <venue> --fund 500000000000
# run it
mandated up --hub <HUB> --mandate 7 --target <venue> --value 10000000000 --interval 86400000The complete hands-on guide — both tools woven together, with real use cases — is docs/GUIDE.md (also on the studio site under Guide).
Both CLIs speak fluent JSON and are built to be driven by an AI assistant. Hand them to one and a plain-English sentence becomes a live, hard-capped, fully-auditable financial agent — the power isn't that the AI can do more, it's that you can safely let it, because the leash is on the chain and not in a prompt.
Each agent has a public page — no login — showing every payment and refusal as a plain sentence, with one verification row per rule and a running total. It reads like a bank statement that checks its own math.
And because dashboards can lie, the same record verifies offline with no help from this site:
node watcher-cli/dist/index.js verify --file receipts.jsonl --manifest manifest.json
# → {"ok":true,"receipts":5,"rulesChecked":[...],"firstFailure":null}Your API key never appears in the manifest, on any chain, in receipts, or in logs — it enters through an env var and stays on your machine. CI greps every published artifact for key-shaped strings.
export MANDATE_ANTHROPIC_KEY=sk-ant-... # Anthropic
export MANDATE_OPENAI_KEY=sk-... # OpenAI / OpenRouter / vLLM / Ollama / llama.cpp
export MANDATE_OPENAI_BASE_URL=https://api.openai.com/v1
ops/hash-weights.sh ./models/llama-3.3-70b-q4_K_M.gguf # local pinned weights (the sovereignty option)The choice is labeled honestly, in public, forever:
| Mode | What's verified | Badge on the agent's page |
|---|---|---|
| Hosted (Anthropic / OpenAI) | every action, approval, and spend | "hosted model — actions provable, outputs not re-executable" |
| Local pinned weights, temp 0 | all of the above plus outputs are re-execution-challengeable | "pinned open weights — challengeable" |
One thing the chain cannot do: cap your API bill. The daemon enforces a local per-epoch inference budget and halts (with a receipt saying why) when it's spent — set a provider-console budget as the backstop.
mandated never reimplements chain plumbing — it calls one contract and writes receipts. Everything else is dot.
| Tool | Does | Reach for it when… |
|---|---|---|
dot (polkadot-cli) |
keys, funding, mapping, balances, transfers, storage, events, XCM, governance | you move or read anything on-chain that isn't the leash |
mandated |
issue a mandate, set the bands, run the agent, kill it, produce receipts | you're bounding what the agent may do |
mandated create --hub <HUB> --session <H160> --preset <name> # issue a mandate
mandated rules list # every preset, in plain words
mandated status --mandate 7 # alive? what may it do?
mandated pause / revoke --mandate 7 [--yes] # kill switches (owner)
mandated playbook # recipes: setup preamble + dca, allowance, notary, …Every command takes --json for scripts and AI agents.
Nothing here requires trusting this repo's authors — that's the point.
npm install && npm run build
npm test # protocol vectors, adversarial cases, crash drills
node watcher-cli/dist/index.js vectors # golden vectors: JCS, hashes, chains, Merkle, announcements
cd contracts && forge test # 48 tests: bands, kill paths, epoch caps, escrow, disputes, fuzzAdversarial cases are first-class tests: a tampered byte fails the signature rule, a suppressed receipt is a chain gap, a hidden receipt body is named DATA WITHHELD, a replayed approval is consumed-nonce-rejected — each one demonstrated and caught in CI.
Three planes, each doing the one job it's shaped for:
| Plane | Where | Holds | Lifetime |
|---|---|---|---|
| Authority | MandateHub + TaskEscrow on Paseo Asset Hub (pallet-revive) |
caps, allowlist, expiry, approvals, checkpoints, disputes, kill switch | permanent |
| Liveness | Statement store (People chain) | 512-byte signed receipt announcements — the hash chain travels here | TTL-bounded |
| Content | Bulletin chain | manifests, receipt bodies (batched), evidence bundles | ~2 weeks, renewable |
Permanence comes from Merkle checkpoints of the receipt stream posted on-chain every 32 receipts or 10 minutes. The raw stream can expire; the commitments cannot. An agent that announces a receipt but hides its body is caught: the announcement pins the hash forever.
All chain access is substrate-native via polkadot-api over WebSocket — there is no Ethereum RPC anywhere in this system. spec/ is the root of truth; when code and spec disagree, the code is wrong.
- It does not verify the semantic quality of any output, and hosted-model outputs are labeled not re-executable rather than pretended otherwise
- A compromised daemon host is bounded, not prevented: blast radius = remaining epoch cap + gas dust; rotate/revoke is the response
- The v1 dispute arbiter is per-mandate owner-set (centralized, stated in the threat model); API spend limits are daemon-local, not chain-enforced
The full threat model, including everything uncovered, ships publicly in spec/PROTOCOL.md and docs/.
| Piece | State |
|---|---|
spec/ — schemas, protocol, golden vectors |
✅ all implementations pass them in CI |
packages/core — canonicalization, hashing, Merkle, receipts, announcements, verifier |
✅ 18 tests incl. adversarial |
contracts/ — three-band MandateHub, TaskEscrow |
✅ 48 forge tests; on-chain deploy (resolc → PolkaVM) pending a funded key |
daemon/ — main loop, WAL, attestor, checkpointer, Hub client, transports |
✅ chaos drills green; DCA agent + kill drill run on-chain (chopsticks) |
CLI — create, up, rules, status, pause/revoke/heartbeat, playbook |
✅ full setup path (create → up → verify) proven end-to-end on the local fork; all support --json |
watcher-cli |
✅ offline verify + vectors + announcement/withholding checks |
studio/ — glass box, guide, transparency console, wizard shell |
✅ scaffold; Hub reads wired; GitHub Pages deploy ready (.github/workflows/pages.yml); live feeds pending |
| Planner + provider adapters (Anthropic / OpenAI-compatible / local) | ⏳ next |
| Approvals push flow, escrow demo, dotNS deploy | ⏳ see docs/ROADMAP.md |
Start with spec/PROTOCOL.md (the truth), then CLAUDE.md (build/test commands and hard rules — the repo contains zero secrets and CI enforces it). The golden vectors in spec/vectors/ were generated once with a dev-only key; regenerating them is a protocol event, not a chore.
License: not yet chosen — do not vendor this code until one lands.


