Baldur
English | 한국어
This project is complete. What was built, what the numbers said, and what survived: retrospective (Korean).
An API you depend on goes down for an hour. What happens to your app?
Requests hang until they time out, every worker fills up, and the jobs that failed in that hour 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.
import baldur
@baldur.protected("summarize", dlq=True, timeout=60.0)
def summarize(doc_id: str) -> str:
return llm_api.summarize(doc_id)
No Redis, no Docker, no config to start: that decorator runs in-memory until you go multi-process.
When the provider dies — or just gets slow — mid-traffic:
- Your app keeps answering. A hang becomes a failure at the 60-second bound, the circuit breaker opens, and calls fail fast — so a slow provider doesn't take every worker down with it. The endpoints that don't need it keep working.
- Failed jobs are kept, not lost. Every call that failed for good is captured with its arguments and listed in the built-in console.
- They come back. A small replay handler tells Baldur how to re-run one; replay the parked jobs from the console with a click, or automatically when the provider recovers — opt-in, with a Celery worker.
Django, FastAPI, Flask, and Celery adapters included.
The shipped demo's dependency is a payment gateway: it goes unreachable
mid-traffic, seven charges are captured with their arguments, and all seven are
replayed on recovery. Zero lost. Same loop for any call — a real run, with the
breaker states and DLQ tallies read live from the framework. The decorator
itself is pip install baldur-framework and nothing else; the demo adds the
celery extra for its in-process stand-in worker — still one process, no
Redis, no broker. Run it yourself:
pip install "baldur-framework[celery]"
python -m baldur.scripts.demo_self_healing
Already using your SDK's retries? Keep them. Baldur doesn't replace retry — it adds what retry can't: a breaker so one incident doesn't cost every request its retries, one wall-clock bound on what the caller waits, a fallback, and the capture-and-replay no retry library gives you.
Install
The Python package is baldur (you import baldur); the PyPI distribution is
baldur-framework.
pip install baldur-framework # framework-agnostic core
pip install baldur-framework[django] # Django integration
pip install baldur-framework[fastapi] # FastAPI integration
pip install baldur-framework[flask] # Flask integration
pip install baldur-framework[celery] # Celery task protection
pip install baldur-framework[redis] # Redis-backed shared state
pip install baldur-framework[prometheus] # Prometheus metrics
The same decorator, any dependency
A payment gateway, your database, an email provider — the call site never changes:
@baldur.protected("charge-customer", dlq=True)
def charge(order_id: str, amount_cents: int) -> dict:
# Circuit breaker by default; dlq=True parks the call if it fails for
# good, with its arguments, and replays it once the gateway recovers.
return payment_gateway.charge(order_id, amount_cents)
When the gateway dies, the breaker opens and your service answers fast instead of stacking up timeouts; the charges that failed on the way out wait in the dead-letter queue and come back when it closes. (Replay is for work that failed on the way out — never for a business rejection, and never for a checkout the customer already walked away from: where that line sits.)
Need more than the default? Compose the pipeline declaratively:
@baldur.protected(
"summarize",
timeout=30.0, # one bound on what the caller waits
fallback=lambda: last_good_summary(), # graceful answer while OPEN
idempotency_key="doc_id", # a redelivered job pays once
)
def summarize(doc_id: str) -> str:
return llm_api.summarize(doc_id)
Notice what isn't there: retry=. Your SDK almost certainly retries
already — anthropic and openai default to two attempts with backoff, boto3
has an adaptive mode — and it retries better than a generic wrapper can,
because it knows which status codes are worth another attempt and honours
retry-after. Keep it. What no SDK gives you is the rest: a breaker, so a
provider incident doesn't mean every request pays its retries before failing;
one wall-clock bound on what your caller waits, retries included (an SDK's own
worst case is timeout × (max_retries + 1) — 30 minutes at anthropic's
defaults); a fallback; and a dedup key that survives a job redelivery the SDK
never sees. retry=True is there for the calls that don't retry themselves.
Sync and async callables are both supported — the decorator auto-detects coroutine functions.
What's in the box (OSS, Apache-2.0)
| Capability | What it gives you |
|---|---|
| Circuit breaker | Stops cascading failure; bounded half-open probes on recovery |
| Retry with backoff | Exponential backoff with jitter and bounded attempts |
| Fallback & composition | One ordered pipeline for all resilience patterns |
| Idempotency | Concurrent duplicate calls execute the side effect exactly once |
| Bulkhead isolation | Each dependency gets a fixed slice of concurrency, so one slow dependency can't drain every worker |
| Dead-letter queue + replay | A call that fails for good is captured with its context and replayed once the dependency recovers |
| Health checks | Liveness/readiness that reflect real dependency state |
| Graceful shutdown | Drain in-flight work cleanly on restart and deploy |
| Metrics | Prometheus and OpenTelemetry, emitted by default |
| System control | Instant kill switch and dry-run mode for Baldur's automation — no redeploy |
| Web console | Built-in operations console: live breaker state, controls, recovery |
| Precomputed cache | Health/status endpoints answer from a warm cache, so constant probing stays cheap |
The read path heals the same way. Here a Django app under live HTTP traffic (recorded from a demo harness driving it) loses its network path to Redis for 21 seconds — every request keeps returning 200 off the in-memory cache tier, and the Redis tier resyncs itself on recovery:
Documentation
Full documentation lives at https://baldur.sh.
- What is Baldur? — the problem it solves and how
- Getting started: Django · FastAPI · Flask · Celery
- Concept guides — one page per capability, linked throughout this README
- API reference
- Troubleshooting
- Compatibility
Using Baldur with AI assistants
Building with an AI coding assistant (Claude Code, Cursor, Copilot, Codex)? Run
baldur init-ai in your repo to drop an AGENTS.md (read by Cursor, Copilot,
and Codex) plus a CLAUDE.md that imports it for Claude Code — together they
teach the assistant to reach for @baldur.protected("name") instead of
hand-rolling a circuit breaker. See
Using Baldur with AI assistants.
Compatibility
| Component | Minimum | Tested in CI |
|---|---|---|
| Python | 3.11 | 3.11 · 3.12 · 3.13 |
| Django | 4.2 | 4.2 LTS · 5.2 LTS · 6.0 |
| FastAPI | 0.100 | latest ≥ floor (smoke) |
| Flask | 2.3 | latest ≥ floor (smoke) |
| Celery | 5.3 | 5.4 |
| Redis server | — | 7.x |
See Compatibility for the full matrix, the Python × Django test grid, and the version support policy.
Running this across a fleet?
Baldur PRO adds the fleet-level machinery on top of the same API — nothing in the core gets relicensed or replaced: DLQ at scale (batch replay from the console, success-rate-driven pacing, a disk-durable outbox, and archive/purge retention), a hash-chained audit trail, unified notifications, emergency mode, bulkhead thread-pool isolation, adaptive throttling, canary recovery, governance gates, and a meta-watchdog that watches Baldur itself. See the full OSS vs PRO capability matrix and pricing.
Early access
Baldur is in early access: the API is stable and the core is tested under sustained load with Sentinel failover, but the project is young — minor releases may still ship breaking changes, always with a changelog entry. It is looking for a small number of teams already running a Python service in production to work with directly. If that is you, the details and how to reach me are in Discussions.
License
Baldur is released under the Apache License 2.0 — see LICENSE and NOTICE.
Contributing
Contributions are welcome under the Apache License 2.0. Pull requests are accepted through a sign-off-based DCO flow — see CONTRIBUTING.md for the full model.
- Ideas, or showing what you built → Discussions.
- Bugs / feature requests / docs → open an issue or a pull request.
- Security → see SECURITY.md (no public issues for vulnerabilities).
- Usage questions / commercial →
support@baldur.sh.
Metadata
Release files for baldur-framework 1.16.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| baldur_framework-1.16.0.tar.gz | 2.7 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| baldur_framework-1.16.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 6.1 MB
Release files / baldur_framework-1.16.0.tar.gz
| Download URL | baldur_framework-1.16.0.tar.gz |
|---|---|
| Size | 2.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ead754e5e638f9fe5bc4434a1e35451019d5f9d33f3536cf6a18dcfcf8504241
|
|
BLAKE2b-256 checksum How to use checksums |
a570dd333facfc19a5dd4c37fd568492c2a4719d39b0b2a3bf26efbb0e9e5eee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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 27, 2026.
Transparency logRelease files / baldur_framework-1.16.0-py3-none-any.whl
| Download URL | baldur_framework-1.16.0-py3-none-any.whl |
|---|---|
| Size | 3.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a2ceefda9a5dede89baf3132077079cb21bad1256895a45eaed9fb75522dd71b
|
|
BLAKE2b-256 checksum How to use checksums |
bf716e06ba7d9d1dae9742a863322e2823dd41d996899edf87322e0bf60fd75f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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 27, 2026.
Transparency log