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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
//! The `/admin/v1` protocol and service boundary: how durable state is changed,
//! by whom, and under what guarantees.
//!
//! A stateful deployment has two entirely separate surfaces (ADR 0027). `/v1`
//! serves inference from an immutable snapshot and never queries the control
//! plane. `/admin/v1` — this module — is the only way desired state changes, and
//! every request on it reads or writes the control plane. The separation is the
//! availability argument: a control-plane outage stalls administration and
//! convergence while replicas keep serving.
//!
//! | Module | Owns |
//! | --- | --- |
//! | [`error`] | the typed envelope and its closed vocabulary of stable codes |
//! | [`auth`] | administrative identity: OIDC humans, attributed breakglass, and the disjointness from inference credentials |
//! | [`protocol`] | the mutation preconditions: idempotency key, expected revision, dry run, audit summary |
//! | [`diff`] | the redacted semantic diff between two complete desired states |
//! | [`reads`] | bounded read projections: state, history, audit, convergence |
//! | [`service`] | the one path a mutation takes: mode, authority, read, validate, diff, publish |
//! | [`mod@router`] | the route boundary, its authentication layer, and its precondition layer |
//!
//! # The five properties this slice exists to make structural
//!
//! **Stateless mode answers without a backend.** [`AdminService::stateless`]
//! holds no [`ControlPlaneStore`], so every operation is
//! [`AdminError::StatefulModeRequired`] and there is nothing to touch — not a
//! connection, not a query, not a health check.
//!
//! **Inference credentials carry no administrative authority.** There is no
//! conversion from an inference principal to an [`AdminIdentity`], the two
//! routers layer different authentication, and a presented `axt1.` token or
//! `x-api-key` is refused with [`AdminAuthError::InferenceCredential`] rather
//! than being looked up.
//!
//! **A mutation cannot skip its preconditions.** The idempotency key and the
//! expected revision are parsed by the router's layer for any mutating route and
//! required by the service, and the expected revision is checked against the head
//! before any state is hydrated.
//!
//! **A dry run has no durable effect.** It stops after validating the complete
//! candidate and computing the diff, and never calls `publish_revision`.
//!
//! **Secrets are absent by type.** Resource bodies are never rendered: a diff and
//! a state read describe a body by form, checksum, and — for a blob — digest and
//! size. Backend text stays in [`AdminError::operator_detail`], which is logged
//! and never serialized. Credentials live in a `SecretString` inside
//! [`AdminCredential`], which has no [`Debug`] rendering of its material, and no
//! error variant can hold presented material.
//!
//! # Contract only
//!
//! Nothing here is constructed by `serve`, and [`router::admin_route_specs`] is
//! empty: the resource handlers, their bodies, and CLI parity are #143's, and the
//! durable store this composes over is #140's. What ships is the boundary they
//! land into, plus the contract tests — against the in-memory store oracle and
//! fake authorities — that hold the five properties above.
//!
//! [`AdminCredential`]: auth::AdminCredential
//! [`AdminAuthError::InferenceCredential`]: auth::AdminAuthError::InferenceCredential
//! [`AdminIdentity`]: auth::AdminIdentity
//! [`AdminError::StatefulModeRequired`]: error::AdminError::StatefulModeRequired
//! [`AdminError::operator_detail`]: error::AdminError::operator_detail
//! [`AdminService::stateless`]: service::AdminService::stateless
//! [`ControlPlaneStore`]: crate::backends::control_plane::ControlPlaneStore
//! [`Debug`]: std::fmt::Debug
// The administrative facade. `allow(unused_imports)` for the same reason
// `desired_state` needs it: this is a binary crate, and a re-export that no
// handler names *yet* is still part of the contract #143 builds against.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;