1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//! The Arcature production health endpoints (AP2.1-10).
//!
//! Two endpoints, two responsibilities (PROGRAM.md AP2.1-10: "One health
//! endpoint convention (`/up/live`, `/up/ready`)"):
//!
//! - **`/up/live`** — liveness, independent of any external dependency.
//! `200` while the process is up; `503` once it has stopped.
//! - **`/up/ready`** — readiness, gated on the lifecycle state being `Ready`
//! **and** every application-registered readiness check passing. `200`
//! only then; `503` during `Starting`, `Draining`, `Stopped`, or when a
//! readiness check fails.
//!
//! Health and readiness are deliberately separate endpoints (do not overload
//! one): a liveness probe must not kill a process that is live but unready,
//! and a readiness probe must not route traffic to a process that is live
//! but still starting or already draining.
//!
//! # Composition
//!
//! [`router`] returns a stateless [`axum::Router<()>`] — standalone-first
//! (AGENTS.md §16): an expert user mounts it on their own Axum app without a
//! runtime dependency on `arcature` beyond the
//! [`Lifecycle`](crate::application::lifecycle::Lifecycle) handle. The
//! handlers read the
//! [`Lifecycle`](crate::application::lifecycle::Lifecycle) from an Axum
//! [`axum::Extension`] layer (installed via [`lifecycle_layer`]), so the
//! health router composes with any application state type `S` — it does not
//! require the app state to carry the lifecycle, and merging it into a
//! stateful router does not change the app's state type.
//!
//! The orchestrated paths (`Application::serve_with_health` /
//! `Application::run_with_health`) merge this router into the
//! application's routes before serving, installing the
//! [`lifecycle_layer`] so the health handlers observe the
//! same [`Lifecycle`](crate::application::lifecycle::Lifecycle) the
//! orchestration drives.
//!
//! The endpoints do not run on the application proxy or any post-routing
//! layer (maintenance, Inertia): they are plain routes, short-circuited by
//! Axum route selection before the fallback or the application's
//! middleware. A maintenance-mode 503 therefore does not wall the health
//! endpoints — `/up/ready` still reports the real lifecycle state, which is
//! what an operator draining traffic wants (PROGRAM.md AP2.1-10).
//!
//! # No information leakage
//!
//! The bodies are public-safe status words (`"live"`, `"ready"`,
//! `"starting"`, `"draining"`, `"unready"`, `"stopped"`). No dependency
//! names, no error messages, no internal paths. The readiness checks
//! themselves never receive the request and never emit output — they are
//! `Fn() -> bool` predicates the handler evaluates silently.
/// The public liveness response, for an app wiring its own liveness route.
pub use live_response;
/// The public readiness response, for an app wiring its own readiness route.
pub use ready_response;
/// The liveness path (`/up/live`).
pub use LIVE_PATH;
/// The readiness path (`/up/ready`).
pub use READY_PATH;
/// The Axum layer that installs the `Lifecycle` into every request's
/// extensions so the [`router`] handlers can read it.
pub use lifecycle_layer;
/// Build the health router: `/up/live` and `/up/ready`, stateless (`()`)
/// so it composes with any application state via a merge/nest.
pub use router;