💰 SpendShield — Payment Guardrails for AI Agents
Before your AI spends real money, it passes through SpendShield.
An open-source payment safety layer for Python and MCP. Give your AI agent a spend-capped digital identity (KYA), run every payment through four deterministic gates, defend against prompt injection, and keep secrets in an encrypted vault.
🩸 Why this project exists (a real incident)
On August 9, 2026, my automation system ran a test order. I sent dry: true, expecting a price preview. The server only honored ?dry=1 — 4 orders of ¥99 were charged for real, and the money was gone.
This is not just my problem. AI agents are about to order food, top up accounts, and call paid APIs on your behalf. When AI starts spending real money, who puts a gate in front of it?
I turned my scar into a library.
✨ Three trust pillars
| Pillar | What it does |
|---|---|
| 🔑 Identity (KYA) | Every agent gets a digital identity with its own budget/blacklist/limits. Unregistered agents are denied by default. |
| 🎯 Intent alignment | New recipients and large amounts always require human sign-off — stops prompt-injected agents from spending without you. |
| 🔐 Secret vault | Keys encrypted at rest (AES-256), master key never on disk. Key access passes the gates and is fully audited. |
🚧 Four deterministic gates
Every spend passes all of them. Rules are code, not AI opinion — agents cannot argue, trick, or inject their way past.
| Gate | Default | Effect |
|---|---|---|
| 🧪 dry_run | On | Preview only. Nothing executes until you say so. |
| 💰 budget | Unlimited | Hard ceiling. Over budget means denied. |
| 🚧 max_amount | Unlimited | Per-transaction cap. |
| 🙋 approval | Off | Human sign-off — console, Telegram, or webhook. |
| 📜 audit | On | Every attempt recorded, exportable JSON. |
🚀 Quick start
pip install spendshield
from spendshield import SpendShield, KeyVault
guard = SpendShield(budget=200, dry_run=True, whitelist=["McDonald's"])
@guard.protect("order")
def place_order(amount, to):
return call_real_api(amount, to)
place_order(amount=99, to="McDonald's")
# => DryRunBlocked: dry_run mode, nothing executed
guard.dry_run = False
for i in range(4):
place_order(amount=99, to="McDonald's") # 3rd order blocked by BudgetExceeded
Agent identity (KYA)
guard = SpendShield(dry_run=False)
guard.register_agent("mcd_bot", budget=50, max_amount=30,
blacklist=["unknown_vendor"], whitelist=["McDonald's"],
rate_limit={"window_s": 60, "max_calls": 3})
@guard.protect("order", agent="mcd_bot")
def place_order(amount, to):
return call_real_api(amount, to)
Secret vault
python -c "from spendshield import KeyVault; print(KeyVault.generate_key())"
export SPENDGUARD_MASTER_KEY=*** # never commit this
vault = KeyVault("vault.json")
vault.store("mcd_sk", "sk_live_xxx")
guard = SpendShield(key_vault=vault)
guard.register_agent("mcd_bot", whitelist=["mcd_sk"])
sk = guard.get_secret("mcd_sk", agent="mcd_bot") # passes identity + intent gates
🤖 MCP Server
Claude Code, OpenClaw and any MCP-compatible agent can call the guard directly:
spendshield-mcp --policy spendshield.yaml
Tools: spend_protect / spend_status / spend_audit / spend_reset / secret_get
🧪 Tests
30 tests covering gates, identity, intent alignment, vault, and edge cases.
python3 -m pytest tests/
📝 Feedback & Contributing
- 🐛 Found a bug? Open an issue
- 💡 Have an idea? Suggest a feature
- 🔒 Security vulnerability? See SECURITY.md — report privately, not in a public issue.
- ⭐ Found it useful? Star the repo so other people who got burned by "test orders" find it.
📄 License
MIT — take it. May no one get burned by a "test order" twice in the AI era.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spendshield-0.6.2.tar.gz.
File metadata
- Download URL: spendshield-0.6.2.tar.gz
- Upload date:
- Size: 22.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f5eeb27218f570b4c39821768a285bab53f8271d55a3df3c1ff3919b0dbf160
|
|
| MD5 |
e75ceb7872008a0d64c2a5e53865265a
|
|
| BLAKE2b-256 |
b95ddfd0b69702a2fc75561849e56cafa0a69227186d04da381f2cb8a0fc5762
|
File details
Details for the file spendshield-0.6.2-py3-none-any.whl.
File metadata
- Download URL: spendshield-0.6.2-py3-none-any.whl
- Upload date:
- Size: 18.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
568006c08644b50554378800d812df1465a31a0e8099aac46466f95299a8d6ce
|
|
| MD5 |
d259d2342973e487ab5ea115731a8cb8
|
|
| BLAKE2b-256 |
1566c3081c838f0956eff7bd66ec8787ddd96613d948dda1de3881e996fc6952
|