Skip to main content

costbomb

Denial-of-wallet fuzzing for agent systems

Find the inputs that make your agent spend $500 to answer a $0.05 question. A fuzzer for economic failure: it hunts the prompts, tool-call loops, and context explosions that blow up your bill, and gates CI on spend regressions.

▶ demo GIF coming — costbomb automatically finding the input that runs the bill from 5¢ to $5

Status: 🚧 v0.2 in progress. costbomb-core is implemented and runnable standalone (below). It ships first inside stampede's adversarial:economic cohort (the embedded path), and this repo is the standalone extract. See Two launches, one codebase.


Why

Agent systems have a failure mode traditional software doesn't: they can be made to spend unbounded money. A crafted input sends an agent into a retry loop, a runaway tool-call chain, a context-window explosion, or a recursive sub-agent spawn — each burning tokens and dollars. This "denial-of-wallet" class is real, under-tooled, and terrifying for anyone running agents in production with a company card attached. Security fuzzers hunt crashes; costbomb hunts spend.

The failure is invisible to every other quality gate: an agent under a denial-of-wallet attack succeeds — no exception, no failed assertion, no red test. It just charged you $500. The oracle everyone else uses is the wrong oracle. costbomb's oracle is the invoice.

Quickstart

# v0.2 — from source (not yet on PyPI):
git clone https://github.com/swarmproof/costbomb && cd costbomb
python -m venv .venv && .venv/bin/pip install -e .

# Crack the bundled 5¢→$5 demo agent automatically, under a $2 self-cap:
costbomb run --target examples/demo_agent.py:handler
╭───────────────────── costbomb · denial-of-wallet ─────────────────────╮
│ 0.10¢ → $0.21 = 211× amplification                                    │
╰────────────── seed 1337 · own spend $1.83 · budget-capped ────────────╯
 #  class              worst $   ×    breakdown (model / tool / spawn)   flags
 1  retry-loop          $0.21   211×  $0.19 / $0.02 / 0.00¢              side-effect
 2  tool-storm          $0.08    …    …
 …
 skipped (target can't exhibit): recursion

Point it at your own agent (Python harness or HTTP endpoint), and gate CI on it:

costbomb run --target ./agent.py:handler --seed 1337          # explore
costbomb baseline update --target ./agent.py:handler          # commit .costbomb-baseline.json
costbomb run --target ./agent.py:handler --fail-on-regression # CI gate on spend regressions
costbomb run --target ./agent.py:handler --dry-run            # fast, zero-paid-call CI smoke
costbomb run --target ./agent.py:handler --use-llm            # optional LLM-assisted mutation (local Ollama by default)
costbomb run --target mock:./agent.py:handler                 # safe default: side-effects hit mockworld fakes, no --allow-side-effects

Template mutation is the default and needs no LLM. --use-llm lets a cheap/local model rewrite candidates toward more spend — and its own token cost is charged against the same --max-spend cap (the fuzzer can't secretly overspend on mutation), with automatic fallback to template mutation if the model is unavailable.

Your agent's handler returns what it spent (a RunRecord of model calls, tool calls, and sub-agent spawns), or emits the OTel GenAI trace directly. costbomb meters that truthfully — see examples/demo_agent.py.

The attack library

Named cost-explosion classes, each a seed strategy the fuzz engine mutates toward maximum spend: retry-loop · tool-storm · context-bomb · recursion · clarification-trap. Each declares whether it applies() to your target, so coverage is honest ("skipped recursion — target has no spawn capability"). The reporter ranks the worst offenders with exact reproduction, the $ each caused (broken down by model / tool / spawn), and which class triggered — then the CI gate fails the build if worst-case cost regresses.

How it works

A directed greybox fuzzer with a dollar-valued fitness function (seed → evaluate → select → mutate), with three agent-specific twists:

  • The cost meter is the oracle — true dollars is a sum over sources: model tokens (input/output/reasoning/cache) + tool-call fees + recursive sub-agent spawn cost. A token-only meter misses exactly where tool-storm and recursion live.
  • Wall-clock / infra cost — set an infra.usd_per_second rate in the price table and costbomb prices a run's duration (GPU-seconds, serverless time) into total_usd. The "$47k over 11 days" shape where time, not tokens, is the cost. On the demo slow agent, a 20s loop costs $0.20 of compute against a ~0.2¢ token bill.
  • Blast radius, not just the token bill — a side-effecting tool (charge_card, place_order) carries a downstream_usd consequence cost in the price table. costbomb reports both the agent's direct bill (total_usd) and the blast radius (blast_radius_usd = direct + real money moved), and can search for the worst blast radius with --fitness blast_radius_usd. On the demo refund agent, a 0.07¢ bill hides an $1,700 blast radius.
  • Exactly-once / duplicate-charge risk — when a loop re-fires a side-effecting tool for the same business key (34 charges to one order), costbomb costs the repeats as duplicate_effect_usd. It dedupes on business identity (34 charges to 34 different orders is $0, not a bug), honors calls the agent already deduped, and — with pip install costbomb[exactly-once] — cross-checks against the real exactly-once middleware (running the calls through @once) rather than guessing: "$1,650 in duplicate effects — charge_card×34, only 1 intended".
  • Fitness is p95 over k runs, not one sample — the target is non-deterministic, so a scalar would make findings and the CI gate flap.
  • A surrogate estimator pre-ranks candidates so real dollars are spent only confirming the promising ones — and the whole search runs under a hard cap on the fuzzer's own spend (--max-spend, default $2). The tool that finds runaway spend must never run away itself.

The CI gate separates price drift from agent regression (REQ-CI-3): it re-prices the baseline's recorded inputs under the current price table before comparing, so a provider raising prices doesn't fail your build — only a genuinely more-expensive-behaving agent does.

GitHub Actions

# .github/workflows/costbomb.yml
name: costbomb
on: [pull_request]
jobs:
  denial-of-wallet:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.11" }
      - run: pip install costbomb
      - run: costbomb run --target ./agent.py:handler --fail-on-regression --dry-run

Two launches, one codebase

costbomb is deliberately one core with two thin entrypoints (the Target protocol is the seam):

  1. Embedded (launch 1): costbomb-core runs inside stampede as the adversarial:economic cohort — its findings appear as the economic section of stampede's Agent Readiness Report. This proves the engine on a real host.
  2. Standalone (launch 2): the same core, extracted behind this CLI + CI gate — "denial-of-wallet fuzzing for AI agents."

Because the seam is Target.invoke(input) -> Trace from day one, extraction is a packaging move, not a rewrite. costbomb consumes stampede's shared contracts (the OTel GenAI trace profile, the RunReport model, the persona-pack schema) verbatim — vendored under src/costbomb/_vendor/ until stampede publishes them as a package. See docs/ARCHITECTURE.md §6.

Metering a real agent (no instrumentation)

Real agents aren't ./agent.py:handler files — they're live services, framework graphs, MCP servers, or product sessions. costbomb needs two things from any of them: to drive it with adversarial inputs, and to meter what each run costs. The low-friction way to meter is the proxy — point your agent's model base_url at costbomb and every LLM call is metered automatically, with zero code changes:

costbomb proxy --upstream https://api.anthropic.com --port 8100
# then set your agent's model base_url to http://127.0.0.1:8100
   your agent (unchanged)
        │  base_url → costbomb proxy      ← one env var; the whole integration
        ▼
   costbomb meter ── forwards ──▶  Anthropic / OpenAI / any compatible upstream
        │  reads real usage per call (input/output/reasoning/cache), prices it
        ▼
   fuzzer drives inputs via your agent's existing endpoint; brackets each run
  • In-process agents (a framework graph, direct-SDK code): use ProxyTarget — it brackets one driven input as one metered run while the agent's calls flow through a shared ProxyMeter. No RunRecord, no handler.
  • Out-of-process agents (HTTP/MCP service): run costbomb proxy; the fuzzer brackets a run with POST /costbomb/run/start … /finish, and calls carrying an x-costbomb-run header are attributed to it.

Because fuzzing has to try inputs (you can't find the worst input from passive logs), costbomb always drives the agent through the interface it already exposes — but metering never requires touching the agent's code. A useful side effect: a proxy sitting in front of a real provider records real usage, which is exactly what the meter-accuracy corpus needs (below).

Validation status (read before trusting the numbers)

costbomb is implemented and internally consistent, not yet validated against reality. Being honest about this matters more for a cost tool than for most software: the whole product rests on the meter being right, and that claim isn't proven yet.

Proven (synthetic / unit) Not yet validated (needs real runs)
Cost-meter arithmetic — token classes, tool fees, sub-agent roll-up (meter-accuracy harness, synthetic fixtures) Meter ≤1% vs a real provider invoice (NFR-8) — needs ≥20 recorded real runs; the corpus test currently skips at 0/20
Hard own-budget cap holds across seeds (incl. LLM-mutation cost) The 5¢→$5 story on a real LLM agent — only the scripted demo agent is cracked so far
Determinism, gate red/green + price-drift-separation logic Gate non-flakiness at recommended k on a real non-deterministic target
Side-effect-free default; no-LLM/dry-run mode Embedded path against a real stampede agent (PersonaTarget tested with a fake driver); MockworldTarget / LLMMutator against real backends (stub / mocked)

Reported dollars are a sum across different billers (LLM provider, Stripe, cloud), so validation is per slice — no single invoice proves the whole number. A green run proves the invoice-backed slices, not the modeled ones. See docs/VALIDATION.md for the per-slice biller/status table and the ready-to-run reconciliation harnesses (examples/validate_llm_slice.py runs a full ReAct agent through the proxy; examples/validate_stripe_slice.py reconciles the blast radius against Stripe test-mode). Add real fixtures via tests/fixtures/meter/README.md.

Design docs

SPEC.md · docs/PRD.md (REQ/NFR IDs) · docs/ARCHITECTURE.md (ADRs) · docs/DELIVERY-PLAN.md · docs/TEST-PLAN.md · CLAUDE.md.

Development

pip install -e ".[dev]"
pytest            # unit → integration → e2e + meter-accuracy corpus, mapped to the test plan
ruff check src tests

Part of the Swarm Proof toolkit

Trust infrastructure for the agent economy — seven projects, one thesis.

Project What it does
stampede Point a herd of realistic agents at your system before real ones arrive
mockworld A synthetic internet for agents — fake Stripe, Gmail, exchange, instantly
mcp-probe The CI quality suite for MCP servers — lint, contract-test, benchmark, load
costbomb ← you are here Denial-of-wallet fuzzing — find the inputs that make your agent spend $500
exactly-once Idempotency middleware so agent side-effects fire once
agent-postmortems A structured incident database + post-mortem standard for agent failures
awesome-agent-reliability The curated map of the field

License

Apache-2.0. Strict global budget cap on the fuzzing run itself; cheap/local models for mutation. Citable via CITATION.cff.

Metadata

Release files for costbomb 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for costbomb 0.2.0
File Size Uploaded Provenance
costbomb-0.2.0.tar.gz 126.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for costbomb 0.2.0
File Interpreter ABI Platform
costbomb-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 202.2 kB

Release files / costbomb-0.2.0.tar.gz

Download URL costbomb-0.2.0.tar.gz
Size 126.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c38a08bbdf77c67f602daf3e04d4f45cbb7aacd203c8cdddeca7320f0e558915
BLAKE2b-256 checksum
How to use checksums
13d508b5d55fb25a8d719ed8ccdb7abea0b7953babceda62976c67a20aebe2be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / costbomb-0.2.0-py3-none-any.whl

Download URL costbomb-0.2.0-py3-none-any.whl
Size 75.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
147a3ddd37ccec05483c6b008cb24f3a5359d8c322bbca65b3f578e5a4519d84
BLAKE2b-256 checksum
How to use checksums
69a7e8a45c36bad39b16ad6b0512fe2f25a46d0dcdda9a87c65760dde5841c67
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Security for costbomb 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for costbomb-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

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