pip install baldur-framework
An API you depend on goes down for an hour.
What happens to your app?
Requests hang, every worker fills up, and the jobs that failed are gone. Whether it’s OpenAI, your payment provider, or your email service — Baldur fixes all three with one decorator, for Python services that don’t have anyone on call.
from baldur import protected
@protected(name="payments.charge", retry=True,
circuit_breaker=True, dlq=True)
def charge(order_id: int) -> Receipt:
return gateway.charge(order_id)
- payments.charge TimeoutError — retries exhausted, parked to DLQ
- payments.charge TimeoutError — parked to DLQ (4 of 5)
- circuit payments OPEN — 5 failures on the window
- 26 calls rejected in 0.3 s — the dying gateway is left alone
- gateway probe healthy — HALF_OPEN trial ok
- circuit payments CLOSED — dlq.replay: 5 parked charges, 5 ok
- incident resolved — no one was paged
You were asleep for all of this. Recorded from a live run of the demo app — every line is a real event.
Run it yourself — one process, no Redis, no broker:
pip install "baldur-framework[celery]"
python -m baldur.scripts.demo_self_healing
The same 59 seconds, as your console recorded them.
Every failure, heal, and replay lands in the healing ledger — the first thing you see on Baldur's built-in console. Nothing to stand up: the console ships with the framework, and Baldur runs on an in-memory store until you point it at Redis to share state across processes.
A screenshot, not a mock — the run above, as the console
drew it.
Real requests, a real worker, real replay: auto-replay is armed
by the replay handler the demo app registers for
payments.charge.
Your stack already fights failures —
in five places that don't talk.
Retries live in the HTTP client, dead letters in the broker, outlier detection in the mesh. Each layer solves its slice and drops the rest. And Python raises the stakes: across 16 workers, an in-memory breaker set to open after 5 failures opens after your downstream has taken 80.
- httpx / urllib3 retries Retry the request, then forget it. No circuit state, and a call that runs out of retries is simply gone.
- tenacity + pybreaker Two solid libraries that don't know about each other. Making them cooperate is on you — and neither keeps the call that failed.
- Celery / SQS dead letters Park the failed job, then wait for a human. Nothing probes for recovery or decides when replay is safe.
- Envoy / Istio See connections, not meaning. A proxy can't preserve a call's arguments, guard idempotency, or replay an order.
- Temporal Answers all of it — once you move the code into its workflow model. A platform migration for one critical path.
@protected The application layer is the only one that knows the call — its name, its arguments, whether it is safe to run twice. Baldur puts the whole loop there: retry, break, park, probe, replay — one decorator, in your process, sharing state through the Redis you already run. No sidecar, no platform migration.
The overhead is measured —
not promised.
Every figure below comes from a dated run on a stated topology, published with the qualifiers that license it. When re-measurement broke one of our numbers, we withdrew it instead of defending it.
- ~39 µs per protected call on the in-memory default — the decorator alone, no network in the path
- +1.4–1.8 ms of server-side CPU per call with idempotency over Redis — three round trips, below saturation
- +1.1% request-throughput cost of the full protected path, measured below saturation
- ~953 B of Redis per parked call — backlog memory is arithmetic, not a surprise
Topology moves all of these — worker count, concurrency model, payload shape. How each number was measured, and the one we withdrew →
Reliability as a decorator, not a platform migration.
-
01 — decorate
Mark what must not fail
Wrap the calls that page you — payments, webhooks, third-party APIs. One decorator composes circuit breaker, retry, fallback, and DLQ — you pick the flags per call; sane defaults, no config server.
-
02 — contain
Failures stop spreading
Retries with backoff absorb blips. When a dependency truly goes down, the circuit opens and failing calls park in the dead-letter queue instead of piling up.
-
03 — heal
Recovery runs itself
Baldur probes for recovery, closes the circuit, and replays the parked calls in order — you choose which failures are safe to replay, and idempotency guards the ones that must never happen twice. The incident closes before the write-up begins.