pub(crate) fn require_lifecycle(
state: &AppState,
capability: impl FnOnce(&ServerPolicy) -> bool,
route: &str,
) -> Result<(), ApiError>Expand description
The one place this server’s own lifecycle refuses a request.
capability selects a field of ServerPolicy; route names what was
asked for, so the refusal says which surface deferred rather than only
that something did. Every caller is one line
(require_lifecycle(&state, |p| p.serves_reads, "…")?), which is the
point: a surface that consults the lifecycle by matching on the state
itself would be a second, drifting copy of
didbot_pds::server_state’s policy table.
§Where the read gate is applied, and where it is not
ServerPolicy::serves_reads is false only in booting and
provisioning, which last a store read and a keypair mint respectively
and during which no agent account can exist. So it is applied where an
absent or empty answer would be misread — listAccounts answering [],
subscribeRepos opening an empty stream, did.json answering 404 —
and not on the per-repository reads, where the natural RepoNotFound is
already the true answer and a 503 would say less.
The refusal is ApiError::not_ready — a 503 carrying a
Retry-After, never a 404 and never an empty answer. See that
constructor and didbot_pds::RETRY_AFTER_SECONDS for how a relay is
meant to tell “try later” from “broken” from “nothing here.”