Security
SpendVeto sits between an agent and its money. That's a position that has to earn trust with evidence, not adjectives. Here's exactly what's enforced, what isn't yet, and how to report a problem.
What's enforced today
- Policy before payment. Per-call caps, hourly budgets, call-rate limits, and chain/tool scope are checked against the live ledger before any signature is produced — not after.
- Approvals fail closed. A spend above your threshold pauses for a human decision. No answer within the timeout window means the call is denied, not allowed by default.
- Nested budget hierarchies. A delegated child's spend counts against its own cap and every ancestor's — a compromised or misbehaving sub-agent can't spend past what its parent was ever granted.
- Chain-scoped signatures. The chain is inside the signed payment message itself, so an authorization signed for one chain is cryptographically invalid on another — not just filtered by convention.
- Runaway auto-freeze. A wallet bursting past its call-rate threshold is frozen automatically, mid-loop, without waiting for a human to notice.
- ECDSA-signed receipts. Every settlement produces an independently verifiable signed receipt; the full ledger exports to CSV for reconciliation.
- Server-authoritative enforcement. The payment gate runs the full policy pipeline itself, server-side — so an agent holding its own key and running a hand-rolled client that skips its policy check still can't overspend: a validly-signed over-budget payment is refused at the gate, and above-threshold spends require a real, single-use approval record before settlement.
- Tamper-evident ledger. Every ledger entry is hash-chained to the one before it, so editing, deleting, or reordering any historical row is detectable; a verify-chain endpoint reports the first broken row.
- Payee allowlisting. Pin which recipient addresses an agent may pay at all — so even a compromised tool or a prompt-injected agent can't send money to an address that isn't on the list. Works globally and as a delegated scope that cascades through the budget tree.
- Request integrity. Every other control here answers "is this spend allowed?" — this one answers "is this the spend I allowed?". An authorization is bound to a canonical digest of the exact request it was granted for, so a payload changed between the decision and the execution is refused even though the payer, the price and the approval all still match. Bindings are single-use, time-bounded, and valid only for the agent they were issued to.
- Token scope, not just token validity. A shared payment token (ACP) or a signed intent (AP2) is minted for a purpose: an amount, a merchant, a window. The merchant validates the token; nobody validates the shopping. SpendVeto checks the session or cart against the credential that funds it — wrong merchant, out-of-scope category, a declared total its own line items don't add up to, or a ceiling quoted in a different currency than the charge.
- Dispute evidence packs. An agent purchase produces no device fingerprint, no IP, no browsing session — the evidence a chargeback is normally defended with simply doesn't exist, so agent transactions lose by default. Every governed spend can be assembled into a signed bundle: the entry pinned between its neighbouring hashes, the policy version in force with any later drift disclosed, the human approval, and the signed consents behind the delegation. Each pack also states what it does not establish — it never implies delivery or satisfaction.
- Authenticated admin surface. The endpoints that change policy, freeze wallets, mint budgets, or register tools sit behind bearer keys with roles (viewer / approver / admin). The first key is minted on the trusted host, never over the API, so there's no way to bootstrap privilege from an unauthenticated request.
Custody model
SpendVeto can run in two modes. In the default mode, the agent's own keypair signs payments locally after the policy pipeline approves — SpendVeto never holds funds. In the keyless / proxy mode, an enforcement proxy holds custody and signs on the agent's behalf only after governance passes, so an agent can operate without ever touching a private key. In both modes, no payment is possible without a policy decision.
What's not yet true
SpendVeto is a prototype in public. Simulate mode (the default) is real ECDSA against a local ledger — no real funds move. Testnet mode settles real x402 transactions on Base Sepolia. Neither mode has been through an external security audit yet, and mainnet settlement is not yet enabled. Treat this as pre-audit software: don't point it at funds you can't afford to lose until an audit has happened.
Verification, not marketing: every control above is exercised by an assertion in the 267-check suite (
npm run verify) on every change to the codebase — see
the verification docs.
Reporting a vulnerability
If you find a way to bypass a policy check, forge a payment signature, or defeat an approval gate, please report it privately before any public disclosure — email [email protected]. We'll acknowledge within 48 hours.