AllowLatch
The spending turnstile for AI agents.
Say the rules in plain language. SERV drafts the policy. Code — not the model — decides
ALLOW, DENY, or ESCALATE. Put the gate in the signing path
(createGatedAgentKit); prefer hybrid Spend Permissions for on-chain daily caps.
The failure mode
A chat instruction is not a control.
An agent with a funded Base wallet can drain itself through loops, bad destinations, over-eager swaps, or prompt injection. Wallet SDKs sign what they are asked to sign. Something has to sit in front of that signature.
AllowLatch is that turnstile: hosted policy, deterministic gate, single-use allow-receipt. The LLM drafts and explains. It never overrides the verdict.
How it runs
Mandate in words. Verdict in code.
One path from owner intent to a signed transfer - with a hard stop in the middle.
-
01
Owner mandate
Limits, symbols, addresses, escalate threshold — in natural language.
-
02
SERV Copilot
Host Reasoning drafts MandatePolicy, surfaces conflicts, resists injection.
-
03
engine.ts gate
Deterministic ALLOW / DENY / ESCALATE. No policy applied → every spend is DENY.
-
04
Receipt, then sign
Single-use
jti. AgentKit signs only after ALLOW. Timeout → DENY.
Rule: only an applied MandatePolicy counts. Local demo drafts and offline heuristics are not production enforcement. If you never Apply / Go live, there are no spending rules — and the agent must not move funds.
Install
How you (and your agent) connect.
You do not run a server. You do not need SERV_API_KEY.
Path: SERV Draft on this site → Go live → Connect pack
(gateUrl + sessionSeal) → agent uses
createGatedAgentKit({ kind: 'site' }) so spends route through AllowLatch
before signing (middleware ≠ custody if a raw key remains — prefer hybrid Spend Permissions).
For you (human)
- Open this site → write a mandate → Draft (SERV Reasoning) → review.
- Go live — policy on the always-on gate + real receipts.
- Connect your agent — copy the gated-kit instruction / code / MCP.
- Optional: OpenServ discover only if the site gate is unreachable.
For your agent
- Read Connect pack or /api/host-info →
primary.gateUrl. - Owner already applied MandatePolicy (Go live). Keep
sessionSeal. - Required:
createGatedAgentKit({ gate: { kind: 'site', gateUrl, sessionSeal }, … })— supported spend path goes through the gate before you sign. assertSpendalone is advisory (does not remove a raw signer). ALLOW + receipt → may sign; else stop.
-
Site
Try + Connect pack
Fastest for owners: mandate here → Go live → paste Connect instruction into Cursor, Claude, or any AgentKit bot.
Open the gate ↓ -
npm
SDK ·
allowlatchRequired:
createGatedAgentKit(spend path through AllowLatch before signing).assertSpendis advisory only if a raw signer still exists.npm i allowlatch@^0.2.2 import { createGatedAgentKit } from 'allowlatch' -
MCP
Model Context Protocol
Cursor / Claude Desktop tools that call the same remote gate.
npx allowlatch-mcp # or: npm run mcp (from the repo) -
Skill
Agent skill (any LLM)
Paste or install the AllowLatch skill so Cursor, Claude, Codex, OpenServ, or any coding agent discovers the gate, applies your mandate, and never invents ALLOW. Same markdown works everywhere — not Cursor-only.
Skill source → · llms.txt · agent.json -
OpenServ
OpenServ (optional fallback)
Marketplace discover + x402 if the always-on site gate is unreachable. Primary path is
/api/gate($0.025 USDC on Base). Check /api/host-info.const services = await client.payments.discoverServices() const gate = services.find((s) => /allowlatch/i.test(s.name)) await client.payments.payWorkflow({ triggerUrl: gate.webhookUrl, input: { prompt } })
Connect pack · gated path preferred
Connect to AllowLatch via https://allowlatch.vercel.app (Go live → Connect pack).
Primary gateUrl: https://allowlatch.vercel.app/api/gate (always-on; agents pay $0.025 USDC x402 on Base).
I do not run a host and I will not give you SERV_API_KEY or ownerToken.
Required: npm i allowlatch@^0.2.2 → createGatedAgentKit({ gate: { kind: "site", gateUrl, sessionSeal, walletPrivateKey } })
so spends go through AllowLatch before signing (hybrid Spend Permissions when available; middleware ≠ custody).
assertSpend alone is advisory only — do not use it as the sole control if a raw CDP/wallet signer remains.
walletPrivateKey is the x402 payer only, not a host key.
- ALLOW + allow-receipt (jti) → only then may you sign.
- DENY, ESCALATE (ask me — never set humanApproved yourself), timeout → do not sign (fail-closed).
OpenServ triggerUrl is optional fallback only.
Never invent ALLOW.
Live case · Base mainnet · 22 Sep 2026
SERV drafts. Code decides. Base settles.
Full product path: SERV Reasoning drafts the mandate → Go live on
/api/gate → agent pays $0.025 x402 → deterministic DENY / ESCALATE / ALLOW →
SERV explains the DENY → only ALLOW + receipt may sign on Base.
-
01 · Fund
Owner tops up the agent wallet
Spender
0x36fe…3645held USDC + gas ETH on Base (earlier top-ups including 0x6e95…1816). -
02 · SERV draft
Plain language → MandatePolicy
Budget $2 USDC on Base. Max $0.10 per transfer and $0.50 per day. Only allow transfers to
0x5cc0…6205. No swaps. Escalate above $0.05.POST /api/copilot(actiondraft) via SERV Reasoning — Multipath · prompt_guard · shadow — modelgpt-5.4-mini, promptallowlatch-draft-v1, ~15s. Ready-to-apply policy: lifetime $2 · day $0.50 · per-tx $0.10 · confirm above $0.05 · swaps off · single allowlisted destination. -
03 · Go live
Durable site gate
Free same-site apply → policyId
serv-live-1790076763,durable: true(Turso) +sessionSealfor the agent Connect pack. -
04 · Paid checks
Always-on
/api/gate· $0.025 eachAgent paid native Base USDC x402 to operator
0xa918…F677(balance 0.495 → 0.57 USDC = three settlements). Not OpenServpayWorkflow— primary path is the Vercel gate.- DENY wrong destination → fee 0x3e3e…a0c6 · SERV explain (~25s): destination not on allowlist — verdict unchanged
- ESCALATE $0.08 above $0.05 confirm → fee 0x4a78…c0f3 · not signed
- ALLOW $0.04 + receipt → fee 0xa8ec…ae43 → signed transfer 0x399d…92b5
-
05 · Result
Product invariants held
SERV drafted and explained — never overrode allow/deny/escalate.
engine.tsowned every verdict. Fees hit the operator wallet. Signature only after ALLOW + consumed allow-receipt. Same flow via Install below.
SDK snippet
One call before every signature.
Required: createGatedAgentKit so the agent cannot call a raw signer in parallel.
walletPrivateKey is only the x402 payer — not a SERV/CDP host key.
assertSpend alone is advisory.
Full paths (MCP, skill, OpenServ discover) are in Install above.
import { createGatedAgentKit } from 'allowlatch'
// npm i allowlatch@^0.2.2 — required spend path through AllowLatch
const agent = await createGatedAgentKit({
gate: { kind: 'site', gateUrl, sessionSeal, walletPrivateKey },
})
await agent.transfer({ toAddress, amountUsd })
// assertSpend alone is advisory if a raw signer still exists
Pricing
Enforcement is paid and remote.
x402 gate call
apply · evaluate · explain · execute — or buy_pack for prepaid checks
$0.025
Evaluate pack credits
Pay once via buy_pack on /api/gate (or OpenServ
buy_evaluate_pack) → fixed credits (default 3). Burn with
packKey on evaluate. Client cannot choose the mint size.
~$0.008/check
Demo UI + local draft
Try SERV here. Snapshot export is watermarked - not production.
Free
Agents pay $0.025 USDC on always-on /api/gate
(or burn prepaid buy_pack credits when Turso durable is on).
OpenServ discover is optional fallback. Clients fail-closed on timeout.
Policy mutates need ownerToken. Details:
MONETIZE
·
SECURITY.
Use it here
Try AllowLatch now
Three steps: 1) write a spending rule → 2) SERV Draft → Apply → 3) Go live. After that your agent cannot move USDC without this gate. SERV drafts and explains; deterministic code returns ALLOW / DENY / ESCALATE.
Checking hosted Gate…
allowlatch · gate
SERV