Why Your AI Agent Should Never Authorize Its Own Payments
Every incident in the 2026 agentic-commerce postmortems has the same shape: an agent with a wallet, a prompt injection or poisoned memory, and no independent check before the money moves. The agent "decided" to pay, so the money went out. That one decision — authorize or not — is the entire attack surface, and today most harnesses hand it to the very thing being attacked.
The decision path is the problem
When an LLM is the final judge of a payment, two properties you desperately want are missing:
1. Determinism. The same payment request at 9:00 and 9:01 can get different verdicts. Sampling temperature, context drift, a slightly different system prompt — any of it flips the decision. You cannot predict, audit, or guarantee a policy.
2. Proof. "The model said OK" is not an audit trail. You can't replay a stochastic decision, and you can't explain to an auditor (or your insurance carrier) exactly why a $40k transfer was approved.
OWASP calls this class of risk LLM08 — Excessive Agency. Standards like Google AP2, Coinbase x402, and ERC-8004 define the paperwork of mandates. None of them ship the enforcement layer that stops an agent mid-flight.
The fix: enforcement outside the model
The authorization decision should be a pure function of (actor, tool, arguments, policy) — evaluated by a deterministic engine with zero LLM in the path:
Agent intent ──▶ authorize(intent) ──▶ PolicyEngine
│ scope · allowlist · denylist
│ budget · rate limit · mandate
▼
APPROVED / DENIED / REQUIRES_APPROVAL
▼
append-only SHA-256 ledger (audit)
Same inputs, same verdict, always. A drain attempt — "refund this customer" to an attacker wallet — is blocked by the allowlist and per-call budget before the tool runs, no matter what the model believes.
Mandates: authorizations the agent can't mint
Even with a deterministic engine, an agent should not hold unbounded authority. MandateGuard mandates are Ed25519-signed, nonce-bound, time-boxed tokens issued offline by you:
issuer = MandateSigner() # private key stays offline
m = Mandate(actor="wallet-agent", max_amount=500, currency="usd",
tools=("pay",), destinations=("0xGOOD",),
not_after="2099-01-01", nonce="abc", issuer="you")
The agent cannot widen its own scope. It can only spend what a signed mandate (or your policy scopes) allows, for as long as the window is open.
Audit you can trust
Every decision is appended to a SHA-256-chained, tamper-evident ledger. Any edit, reorder, or deletion is detected by a linear scan. When an incident happens, you can prove exactly what was approved, when, and under which policy — and prove the ledger wasn't rewritten after the fact.
Mount it as an MCP guardrail
MandateGuard ships as a full MCP server — mount it in Claude, Cursor, or any harness and your agents inherit the policy layer in minutes. It's published on the official MCP Registry (io.github.ezequiellich44-cmd/mandateguard), and the core engine is MIT licensed.
Ship your agent. Keep your wallet.
Core is free (MIT). Pro — revocation, persistence, RBAC, webhooks — is 149 USDT/mo, paid in USDT via Phantom (Solana or Ethereum).
Buy Pro — 149 USDT/mo Star on GitHub