Build services that survive failure

Faultbox is fault injection for distributed systems. Most outages are ordinary failures your code never handled - Faultbox tests those paths: your service, failing dependencies, in CI.

v0.13.3 · Apache 2.0 · Linux (macOS via Lima) · single binary

faultbox.star
db  = service("db", "./db", interface("main", "tcp", 5432),
    healthcheck = tcp("localhost:5432"))
api = service("api", "./api", interface("public", "http", 8080),
    env = {"DB_ADDR": db.main.addr}, depends_on = [db],
    healthcheck = http("localhost:8080/health"))

def test_write_failure():
    def scenario():
        resp = api.post(path="/orders", body='{"item": "widget"}')
        assert_eq(resp.status, 503, "degrade cleanly when DB writes fail")
    fault(db, write=deny("EIO"), run=scenario)
$ faultbox test faultbox.star

PASS  test_write_failure  (0.42s)
 fault(db, write=deny("EIO"))
 POST /orders 503
 assert_eq(resp.status, 503)

Every run is an artifact

A run doesn't end at PASS/FAIL in the terminal. Every run writes a self-contained .fb bundle - spec, environment, full event trace, replay script - and one command turns it into a single-file HTML report: verdicts with drill-down, a swim-lane trace per service, the fault plan and its coverage. The terminal tells you that it broke; the report is where you understand why. Attach it to the PR or the incident ticket.

Faultbox HTML report - verdicts, fault diagnostics, and per-test outcomes

Open the live sample report

Mix real and simulated dependencies

Run dependencies for real in Docker where that's cheap, simulate the rest with protocol-aware mocks - OpenAPI-generated if you have the contract. Faults work identically on both, so the spec doesn't care which is which.

faultbox.star
api  = service("api", image="myteam/api")        # the one image you own
db   = service("db", image="postgres:16-alpine")  # real infra where it's cheap
auth = mock_service("auth",                       # simulated where it isn't
    interface("main", "http", 8090),
    routes = {"GET /token": json_response(200, {"ok": True})},
)

auth_down = fault_assumption("auth_down",
    target = auth.main,
    rules  = [error(path="/**", status=503)],
)

Mock services - HTTP, gRPC, Redis, Kafka, OpenAPI-generated →  ·  Where Faultbox fits - vs integration tests, load tests, prod chaos →

Install

curl -fsSL https://faultbox.io/install.sh | sh

Detects your platform, downloads the latest release, verifies checksum. Or build from source.

Why Faultbox

Protocol-level injection

Inject faults at HTTP, HTTP/2, gRPC, Postgres, MySQL, Redis, Kafka, NATS, MongoDB, Cassandra, ClickHouse, AMQP, Memcached, TCP, and UDP protocol level. Target specific queries, paths, topics, or CQL statements via transparent proxy.

Mock services

Stand in for dependencies you can't run: mock_service() HTTP/gRPC stubs, redis.server(state=...), OpenAPI-generated responses. In-process, no containers - and faultable exactly like real services.

Verdicts you can trust

Three-valued verdicts - PASS / FAIL / INCONCLUSIVE - with formal finite-trace semantics. A timeout is never a green. Temporal properties: eventually(), always(), monitor() state machines over the event log.

Deterministic exploration

Name the unknowns with choose() and nondet(); Faultbox fans them out into a tree of leaves and walks every fault, ordering, and choice. faultbox plan shows the tree; seed-based replay reproduces any failing leaf.

Starlark specs

Topology, faults, and assertions in one .star file. No YAML. No separate config language. The spec is executable code.

Syscall-level injection

Under the hood: deny, delay, or hold any syscall via seccomp-notify. No eBPF, no ptrace, no code changes. Syscall families expand automatically - write covers write, writev, pwrite64.

Everything else - recipes, event sources, named operations, 15 protocols, LLM tooling →

How it works

1
Write a spec Define topology, faults, and assertions in a single .star file
2
Start services Runtime launches binaries or containers and installs seccomp filters
3
Intercept syscalls Kernel pauses processes on target syscalls and asks Faultbox what to do
4
Inject & assert Deny, delay, or hold syscalls - then verify your service handles it

Powered by seccomp-notify - no ptrace, no eBPF, no code instrumentation. Faults are injected in the kernel, invisible to the target process.

Built for LLM agents

LLM agents write code. But who tests what happens when the database crashes, the network drops, or the disk fills up? Faultbox closes the loop.

1
Agent writes code

Your LLM agent builds a microservice. It writes handlers, connects to Postgres, adds Redis caching.

2
Faultbox generates tests

One command from docker-compose. Every dependency gets fault scenarios - disk failures, network drops, slow queries.

faultbox init --from-compose
3
Structured feedback

JSON output with diagnostics: "write fault fired 3 times but service returned 200 - missing error handling in the persist path."

4
Agent fixes the bug

The agent reads the diagnostic, finds the code, adds error handling. Runs tests again. All pass. Commits with confidence.

MCP native Built-in MCP server with 6 tools. Claude, Cursor, and any MCP client connect directly.
One command setup faultbox init --claude creates slash commands and MCP config. Zero configuration.
Actionable diagnostics Not just "test failed" - structured hints that tell the agent exactly what to fix and where.

Every LLM agent writing microservices needs to answer one question:
"What happens when things break?"

Faultbox is that answer.

LLM Integration Guide