baldur — Facade & Bootstrap
The top-level entry points: bootstrap wiring, the resilience facade, the leader-elected scheduler, SQL transaction scope, the admin server, and the framework-extra hooks. Every name below is covered by SemVer compatibility guarantees in v1.x.
Bootstrap
init
init(
quarantine_callback: (
Callable[[Exception], None] | None
) = None,
task_backend: str = "inline",
) -> None
Initialize the Baldur framework.
Idempotent — safe to call multiple times. Second and later calls are silent DEBUG-level no-ops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
quarantine_callback
|
Callable[[Exception], None] | None
|
Optional callable invoked with the
|
None
|
task_backend
|
str
|
Scheduler execution mode (429 Part 6, C13).
- |
'inline'
|
Resilience facade
protect
protect(
name: str,
fn: Callable[[], T],
*,
fallback: (
Callable[[], T] | Callable[[Exception], T] | None
) = None,
dlq: bool | None = None,
retry: (
bool
| RetryPolicyConfig
| ResiliencePolicy[T]
| None
) = None,
circuit_breaker: bool | None = None,
timeout: float | None = _TIMEOUT_UNSET,
idempotency_key: (
str | Callable[[PolicyContext], str] | None
) = None,
idempotency_fail_open: bool | None = None,
idempotency_ttl: timedelta | None = None,
idempotency_execution_ttl: timedelta | None = None,
context: PolicyContext | None = None
) -> T
Run fn under Baldur's composed resilience pipeline and return its value.
Composition order (outer→inner): Fallback → CircuitBreaker → Timeout →
Retry. fn runs the full retry.max_attempts before the fallback
serves; the fallback is the last resort covering retry exhaustion, a
timeout, AND a CB-open rejection. On all-failed WITHOUT a fallback the
original exception is re-raised; final failures optionally flow through
DLQSink.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Service identifier. Used as the Circuit Breaker key, Retry domain, and Prometheus label — keep it stable per downstream. |
required |
fn
|
Callable[[], T]
|
Zero-argument callable to protect. Must be idempotent when
|
required |
fallback
|
Callable[[], T] | Callable[[Exception], T] | None
|
Optional callable invoked when the protected call fails and
the pipeline falls back; its return value is returned. Accepts
either zero arguments ( |
None
|
dlq
|
bool | None
|
When True, final failures flow into the DLQ repository resolved
via |
None
|
retry
|
bool | RetryPolicyConfig | ResiliencePolicy[T] | None
|
|
None
|
circuit_breaker
|
bool | None
|
When True, wraps |
None
|
idempotency_key
|
str | Callable[[PolicyContext], str] | None
|
Opt into composed dedup so a retried (or re-submitted)
operation runs its side effect once, and a concurrent in-flight
duplicate (double-submit, duplicate webhook) is blocked rather than
executed in parallel. A |
None
|
idempotency_fail_open
|
bool | None
|
Cache-error fail direction for the dedup gate.
|
None
|
idempotency_ttl
|
timedelta | None
|
Dedup memory window — how long a completed operation
is remembered (how long duplicates stay blocked after success).
|
None
|
idempotency_execution_ttl
|
timedelta | None
|
In-flight execution window — how long a
running claim is honored before a crashed attempt becomes
retryable. |
None
|
context
|
PolicyContext | None
|
Optional |
None
|
Returns:
| Type | Description |
|---|---|
T
|
Whatever |
Raises:
| Type | Description |
|---|---|
Exception
|
Re-raises the underlying error when all branches fail and no fallback produced a value. |
IdempotencyDuplicateError
|
A duplicate was blocked by the dedup gate —
already completed (SKIP) or a concurrent in-flight call (ABORT). The
same type |
IdempotencyUnavailableError
|
A cache I/O error prevented the dedup check
and |
ValueError
|
|
aprotect
async
aprotect(
name: str,
fn: Callable[[], Awaitable[T]],
*,
fallback: (
Callable[[], Awaitable[T]]
| Callable[[Exception], Awaitable[T]]
| None
) = None,
dlq: bool | None = None,
retry: (
bool
| RetryPolicyConfig
| ResiliencePolicy[T]
| None
) = None,
circuit_breaker: bool | None = None,
timeout: float | None = _TIMEOUT_UNSET,
idempotency_key: (
str | Callable[[PolicyContext], str] | None
) = None,
idempotency_fail_open: bool | None = None,
idempotency_ttl: timedelta | None = None,
idempotency_execution_ttl: timedelta | None = None,
context: PolicyContext | None = None
) -> T
Async counterpart of protect() — full CB + Retry + Fallback + DLQ parity.
This is the async twin of protect() — the split is intentional (matching
httpx/redis-py/SQLAlchemy), so await-ness is explicit at the call site rather
than inferred. Use protect() for a sync callable, aprotect() for a
coroutine; @protected auto-dispatches between them.
circuit_breaker and retry apply on the async path exactly as in
protect(): None resolves against ProtectSettings.default_* (CB on
by default, retry off), an explicit True / RetryPolicyConfig opts in.
Composition order mirrors sync — Fallback (outermost) → CB → Timeout →
Retry — so retry runs its full max_attempts before the fallback serves,
one exhausted retry-sequence is a single CB failure, and the timeout bounds
the whole sequence (a global, not per-attempt, timeout). The fallback is the
last resort covering retry exhaustion, timeout, AND CB-open. Async
dlq=True routes an exhausted failure to the DLQ sink (the async retry
stage arms should_dlq).
A pre-built TenacityBridgePolicy passed as retry= is auto-converted
to AsyncTenacityBridgePolicy and runs under tenacity.AsyncRetrying.
Any OTHER sync ResiliencePolicy passed as retry= raises
NotImplementedError (it cannot be safely awaited).
Parity gap — outbound 429 coordination. protect()'s retry stage shares
a cooldown across workers when a downstream returns 429; the async retry
stage does not, so rate_limit_aware / rate_limit_key on a
RetryPolicyConfig are inert here. To coordinate on the async path, pass
a tenacity bridge carrying a rate_limit_key as retry= — noting that
its wait blocks the event loop.
Idempotency (idempotency_key / idempotency_fail_open /
idempotency_ttl / idempotency_execution_ttl) behaves as in
protect, driven by an async-native guard/hook that
AsyncPolicyComposer awaits directly over AsyncIdempotencyGate (zero
thread hop; awaitable redis.asyncio dedup I/O). Dedup is exactly-once for
the concurrent case; across a process crash between the side effect and the
post-execution mark the operation may run again after the gate record's TTL
expires (an essential at-least-once limit — see protect).
protected
protected(
name: str,
*,
fallback: (
Callable[[], Any]
| Callable[[Exception], Any]
| None
) = None,
dlq: bool | None = None,
retry: (
bool
| RetryPolicyConfig
| ResiliencePolicy[Any]
| None
) = None,
circuit_breaker: bool | None = None,
timeout: float | None = _TIMEOUT_UNSET,
idempotency_key: (
str | Callable[[PolicyContext], str] | None
) = None,
idempotency_fail_open: bool | None = None,
idempotency_ttl: timedelta | None = None,
idempotency_execution_ttl: timedelta | None = None,
context_from: (
Callable[..., PolicyContext] | None | Literal[False]
) = None
) -> Callable[[Callable[..., T]], Callable[..., T]]
Decorator form of protect().
Auto-detects coroutine functions and dispatches to aprotect() when
appropriate, so @protected(...) works uniformly for sync and async
callables. Arguments passed to the decorated function are forwarded to
fn via partial binding.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
Service identifier — see |
required |
fallback
|
Callable[[], Any] | Callable[[Exception], Any] | None
|
Same as |
None
|
dlq
|
bool | None
|
Same as |
None
|
retry
|
bool | RetryPolicyConfig | ResiliencePolicy[Any] | None
|
Same as |
None
|
circuit_breaker
|
bool | None
|
Same as |
None
|
timeout
|
float | None
|
Same as |
_TIMEOUT_UNSET
|
idempotency_key
|
str | Callable[[PolicyContext], str] | None
|
Same as |
None
|
idempotency_fail_open
|
bool | None
|
Same as |
None
|
idempotency_ttl
|
timedelta | None
|
Same as |
None
|
idempotency_execution_ttl
|
timedelta | None
|
Same as |
None
|
context_from
|
Callable[..., PolicyContext] | None | Literal[False]
|
Controls auto-population of |
None
|
aprotected
aprotected(
name: str,
*,
fallback: (
Callable[[], Awaitable[Any]]
| Callable[[Exception], Awaitable[Any]]
| None
) = None,
dlq: bool | None = None,
retry: (
bool
| RetryPolicyConfig
| ResiliencePolicy[Any]
| None
) = None,
circuit_breaker: bool | None = None,
timeout: float | None = _TIMEOUT_UNSET,
idempotency_key: (
str | Callable[[PolicyContext], str] | None
) = None,
idempotency_fail_open: bool | None = None,
idempotency_ttl: timedelta | None = None,
idempotency_execution_ttl: timedelta | None = None,
context_from: (
Callable[..., PolicyContext] | None | Literal[False]
) = None
) -> Callable[
[Callable[..., Awaitable[T]]],
Callable[..., Awaitable[T]],
]
Async-only decorator. Use @protected for mixed sync/async callsites;
prefer @aprotected when you want a type-checker error on misuse
against a sync function.
context_from, idempotency_key, idempotency_fail_open,
idempotency_ttl, and idempotency_execution_ttl behave
identically to @protected (async parity). circuit_breaker and
retry apply on the async path just as on the sync one (CB on by default),
so DLQ entries from async pipelines carry the captured context.
Scheduler
get_leader_scheduler
get_leader_scheduler(
resource_name: str = DEFAULT_SCHEDULER_RESOURCE,
elector: LeaderElector | None = None,
) -> LeaderScheduler
Return the LeaderScheduler singleton.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
resource_name
|
str
|
Resource name |
DEFAULT_SCHEDULER_RESOURCE
|
elector
|
LeaderElector | None
|
Leader elector for a first construction of this resource's
singleton. |
None
|
Returns:
| Type | Description |
|---|---|
LeaderScheduler
|
The LeaderScheduler instance |
SQL storage
sql_transaction
sql_transaction(conn: Any) -> Any
Suspend repo-scoped auto-commit for the duration of the block.
Usage::
with sql_transaction(conn):
dlq_repo.save(...)
cb_repo.update(...)
# single commit (or rollback on exception) applies to both.
All repositories whose get_connection returns conn during
the block skip their per-call commit. The context manager itself
issues the final commit, or rollback on exception.
Admin server
start_admin_server
start_admin_server(
port: int | None = None,
bind: str | None = None,
*,
register_shutdown: bool = True
) -> AdminServer
Public entry point — start the admin server.
Arguments override the corresponding settings when provided; otherwise
settings (BALDUR_ADMIN_*) apply. Subsequent calls return the already
running server.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
port
|
int | None
|
Override |
None
|
bind
|
str | None
|
Override |
None
|
register_shutdown
|
bool
|
When True (default), integrates with
:class: |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
The |
AdminServer
|
class: |
Raises:
| Type | Description |
|---|---|
AdminAuthRequiredError
|
non-localhost bind without an API key. |
stop_admin_server
stop_admin_server(timeout: float = 5.0) -> None
Stop the singleton admin server if running. Idempotent.
Framework extras
fastapi_lifespan
async
fastapi_lifespan(
app: FastAPI,
) -> AsyncIterator[dict[str, Any]]
Initialize Baldur on app startup, drain on shutdown.
Yields an empty mapping so callers can attach lifespan-scoped state via
the standard lifespan_state pattern; the dict is reserved for
Baldur-internal use and may carry diagnostic state in future versions.
init_flask
init_flask(
app: Flask,
service_name: str | None = None,
rate_limit: int | None = None,
window_seconds: int | None = None,
) -> None
Initialize Baldur for a Flask app.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app
|
Flask
|
The Flask application instance. |
required |
service_name
|
str | None
|
Optional upstream identity. When supplied,
CB pre-flight + post-response observation are enabled. When
|
None
|
rate_limit
|
int | None
|
Per-instance override for the middleware rate limit
(requests per window). |
None
|
window_seconds
|
int | None
|
Per-instance window size override. |
None
|