Skip to content

Architecture

Components

graph LR
    A[pico-boot init] -->|entry point| B[pico_actuator]
    B --> C[ActuatorController]
    C -->|List injection| D[HealthIndicator components]
    C -->|List injection| E[InfoContributor components]
    C --> F[ActuatorSettings]
    C --> G[health.gather]

Four small modules, one responsibility each:

Module Responsibility
controller.py HTTP layer — routes, status codes. A pico-fastapi @controller.
health.py Pure aggregation logic. No FastAPI, no pico-ioc imports.
config.py ActuatorSettings + the two protocols.
exceptions.py PicoActuatorError.

Design decisions

Reuse pico-fastapi's @controller instead of an APIRouter. Route wiring, DI resolution and result normalization (the (body, status) tuple) are inherited for free, and the controller is a regular component — overridable in tests like any other.

Protocols + List[...] injection instead of a registry. HealthIndicator and InfoContributor mirror the FastApiConfigurer idiom: implement the protocol, mark the class @component, done. No decorators to import, no registration order to think about.

health.py stays pure. The aggregation logic imports nothing from the web stack, so it is unit-testable without booting an app — and reusable if a non-HTTP transport ever needs it.

Failure isolation over fail-fast. One broken indicator reports DOWN with its error; the endpoint itself never 500s. An actuator that dies with its dependencies is useless exactly when you need it.

Liveness is dependency-free by design. /health/live returns UP unconditionally: it proves the event loop answers, so orchestrators restart genuinely hung processes — not processes whose database blinked.

Stability and versioning

This module follows the ecosystem policy in ADR-014: API Stability and Deprecation. The public API is exactly what __all__ exports plus the actuator.* settings keys and their defaults, pinned by tests/test_exports.py. Before 1.0 a breaking change ships as a minor release; a deprecated name keeps working, with a DeprecationWarning naming its replacement, for at least one minor release and 90 days before removal.