Skip to main content

💰 SpendShield — Policy & Authorization Layer for AI Payments

Let AI agents spend real money — without spending it recklessly.

SpendShield is the policy and authorization layer between AI agents and money. It is not a wallet and not a payment processor: it sits at Agent ↔ Money and decides should this payment happen? using deterministic, explainable rules. Payment providers (Stripe, x402, wallets, cards) stay downstream — SpendShield never holds your money.

        HUMAN
          │  delegation
          ▼
   AGENT IDENTITY ──► INTENT ──► POLICY ENGINE ──► RISK ──► AUTHORIZATION ──► APPROVAL
                                                                              │
                                                      Stripe / x402 / Wallet ◄─┘
                                                                              │
                                                                          REAL MONEY
                                                                              │
                                                                          AUDIT

🩸 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. 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-way decisions, not just allow/deny

Every authorization returns a structured result — the reason is part of the product:

from spendshield import SpendShield

shield = SpendShield()
shield.load_policy("policy.yaml")

result = shield.authorize(agent="shopping-agent", amount=75, to="amazon.com")
❌ DENY
Reason: transaction $75.00 exceeds the $50.00 limit
  - max_transaction: transaction $75.00 exceeds the $50.00 limit (block)
Decision Meaning
ALLOW Safe to pay. Budget is reserved.
⏸️ APPROVAL Needs human sign-off. shield.approve(id) re-evaluates with current rules.
DENY Blocked, with structured RuleHits explaining exactly why.

📜 Policy DSL — rules as YAML

One policy, one place. Agent rules override global rules (CSS-like merge):

version: "2.0.0"
policy:
  budget:        { daily: 100, monthly: 1000 }
  transaction:   { max: 50 }
  merchants:
    allowed: [amazon.com, walmart.com]      # exact domain match, subdomains ok
    blocked: []
  approval:      { over: 30, new_merchant: true, channel: tg }
  rate_limit:    { window_s: 3600, max_calls: 5, max_total: 300 }

agents:
  shopping-agent:
    transaction: { max: 50 }

Before spending, simulate — never touches money:

from spendshield.policy import PolicySimulator

sim = PolicySimulator(policy_raw=policy)
sim.sweep("shopping-agent", "amazon.com", [20, 30, 50, 51, 80])
# {20: ALLOW, 30: ALLOW, 50: APPROVAL, 51: DENY, 80: DENY}

🛡️ Security Constitution — 8 invariants

The whole system is built around invariants that must never be violated, no matter how the code evolves:

  1. Unauthorized → no payment
  2. Over budget → no payment (spent <= budget always)
  3. Approval mismatch → no payment
  4. Invalid identity → no payment
  5. Replay → at most one valid authorization
  6. Concurrency → never breaks budget
  7. Engine failure → deny by default (fail-closed)
  8. An agent cannot obtain the ability to bypass SpendShield

V3+ layers (Intent, Risk) must not break these 8 rules.

🧪 Tested like it guards real money

  • 186 tests, all green — including 14 security/attack suites
  • 8 attack surfaces: budget_bypass, race_condition, replay_attack, double_spend, policy_bypass, approval_bypass, parameter_tampering, credential_leak
  • Security constitution tests (the 8 invariants above, verified under fuzz + concurrency)
  • Fuzzing: thousands of random attack combinations, Money Invariant must hold
  • Simulator ↔ real engine differential: 800 random requests, decisions must match exactly
  • Migration property tests: random V1 configs migrate without breaking intent
  • Every discovered hole → fix → permanent regression test. Release discipline: any P0/P1 security bug blocks release. Before each release we ask: "did this change give an attacker a new way to spend money?"

🧩 MCP — for AI agents themselves

10 tools over stdio JSON-RPC (python -m spendshield.mcp --policy policy.yaml):

spend_authorize / spend_approve / spend_reject / policy_sim / policy_apply / spend_protect / spend_status / spend_audit / spend_reset / secret_get

⚠️ Threat model & known limits (transparent)

  • MCP has no authentication — trust your host. policy_apply / spend_approve are host-level operations.
  • Approval IDs are 48-bit random — a library trusts its caller.
  • In-memory audit can be modified by code with process access (append-only audit is on the V8 roadmap).
  • Denormal amounts (< 1e-9) are accepted but harmless (no money impact).

🗺️ Roadmap

V1 prevent reckless spending ✅ → V2 Policy Engine ✅ → V2.1 Simulator ✅
→ V2.2 Security Harness ✅ → engine switch ✅ → MCP ✅ → V2 Hardening 🔄
→ V3 Intent Layer (anti prompt-injection) → V4 Risk Engine (deterministic → ML)
→ V5 Agent Identity / Delegation (IAM) → V6 Payment Rails
→ V7 Dashboard → V8 Enterprise

The metric that matters: real agents protected, real transactions gated, real dollars saved. Not stars.

🚀 Quick start

pip install spendshield
from spendshield import SpendShield

guard = SpendShield(dry_run=True)          # dry-run on by default
guard.load_policy("examples/policy.v2.yaml")

@guard.protect("order")
def place_order(amount, to):
    return call_real_api(amount, to)       # denied/needs-approval raises before this runs

V1-style constructor config still works — it is auto-migrated to the V2 engine:

guard = SpendShield(budget=200, dry_run=True, whitelist=["McDonald's"])

🔑 Secret vault

Keys encrypted at rest (Fernet), master key never on disk, key access passes the same gates and is fully audited:

from spendshield import SpendShield, KeyVault

vault = KeyVault("vault.json", master_key=os.environ["SPENDGUARD_MASTER_KEY"])
guard = SpendShield(key_vault=vault, approval="console")
sk = guard.get_secret("mcd_sk", agent="mcd_bot")   # gated + audited

SpendShield: the layer I wish I had before my AI spent my money.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spendshield-0.7.2.tar.gz (55.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spendshield-0.7.2-py3-none-any.whl (46.7 kB view details)

Uploaded Python 3

File details

Details for the file spendshield-0.7.2.tar.gz.

File metadata

  • Download URL: spendshield-0.7.2.tar.gz
  • Upload date:
  • Size: 55.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.2

File hashes

Hashes for spendshield-0.7.2.tar.gz
Algorithm Hash digest
SHA256 9c6a3603a7ad69289d682dce736a08f3841ef6df1e38d721d4d21f175206132d
MD5 03e10ec8cc7ba99a9456b467fb3ad4a9
BLAKE2b-256 9ddb589d634b60df869181854afb69a6fa8408288f8eb6e9ee1dca9f47057b94

See more details on using hashes here.

File details

Details for the file spendshield-0.7.2-py3-none-any.whl.

File metadata

  • Download URL: spendshield-0.7.2-py3-none-any.whl
  • Upload date:
  • Size: 46.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.2

File hashes

Hashes for spendshield-0.7.2-py3-none-any.whl
Algorithm Hash digest
SHA256 b753b3879122151ed44f0c8f7a55872c34e67cb38bf8885a8e00761795157142
MD5 f78f763f2547ab099b4816bfaaf85837
BLAKE2b-256 e25907ac36d3935acf633ad95542185e11ca663e48de64495392ff651db432a8

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.7.2 This release

2 files

0.7.0

2 files

0.6.2

2 files

0.6.1

2 files

0.6.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page