koi_certmesh/core_setup.rs
1//! Construction, wiring, and route/subscription accessors for CertmeshCore.
2//!
3//! Part of the inherent impl CertmeshCore, split from lib.rs (certmesh M2).
4//! As a child module of the crate root, 'use super::*' inherits lib.rs's
5//! imports, sibling modules, and crate-private state/helpers as in the original.
6use super::*;
7
8impl CertmeshCore {
9 /// Construct a facade from an existing shared state.
10 pub(crate) fn from_state(state: Arc<CertmeshState>) -> Self {
11 Self { state }
12 }
13
14 /// The resolved filesystem paths this core operates on.
15 ///
16 /// The data root is resolved once at the composition root and injected
17 /// via the `*_with_paths` constructors; every operation reads it from
18 /// here. There is no ambient fallback.
19 pub fn paths(&self) -> &CertmeshPaths {
20 &self.state.paths
21 }
22
23 /// Create a new CertmeshCore with an unlocked CA and explicit paths.
24 pub fn new_with_paths(
25 ca: ca::CaState,
26 roster: Roster,
27 auth_state: Option<AuthState>,
28 paths: CertmeshPaths,
29 ) -> Self {
30 let rate_limiter = load_rate_limiter(&paths);
31 let posture_tx = initial_posture_tx(&paths);
32 Self {
33 state: Arc::new(CertmeshState {
34 paths,
35 ca: tokio::sync::Mutex::new(Some(ca)),
36 roster: tokio::sync::Mutex::new(roster),
37 auth: tokio::sync::Mutex::new(auth_state),
38 pending_challenge: tokio::sync::Mutex::new(None),
39 rate_limiter: tokio::sync::Mutex::new(rate_limiter),
40 approval_tx: tokio::sync::Mutex::new(None),
41 event_tx: koi_common::events::event_channel().0,
42 posture_tx,
43 renewal_failure_count: std::sync::atomic::AtomicU32::new(0),
44 }),
45 }
46 }
47
48 /// Create a CertmeshCore in locked state with explicit paths.
49 pub fn locked_with_paths(roster: Roster, paths: CertmeshPaths) -> Self {
50 let rate_limiter = load_rate_limiter(&paths);
51 let posture_tx = initial_posture_tx(&paths);
52 Self {
53 state: Arc::new(CertmeshState {
54 paths,
55 ca: tokio::sync::Mutex::new(None),
56 roster: tokio::sync::Mutex::new(roster),
57 auth: tokio::sync::Mutex::new(None),
58 pending_challenge: tokio::sync::Mutex::new(None),
59 rate_limiter: tokio::sync::Mutex::new(rate_limiter),
60 approval_tx: tokio::sync::Mutex::new(None),
61 event_tx: koi_common::events::event_channel().0,
62 posture_tx,
63 renewal_failure_count: std::sync::atomic::AtomicU32::new(0),
64 }),
65 }
66 }
67
68 /// Create a CertmeshCore in uninitialized state with explicit paths.
69 ///
70 /// HTTP routes are still mounted so `/create` is reachable on a fresh install.
71 /// All operations that require an initialized CA will return `CaNotInitialized`.
72 pub fn uninitialized_with_paths(paths: CertmeshPaths) -> Self {
73 let rate_limiter = load_rate_limiter(&paths);
74 let posture_tx = initial_posture_tx(&paths);
75 Self {
76 state: Arc::new(CertmeshState {
77 paths,
78 ca: tokio::sync::Mutex::new(None),
79 roster: tokio::sync::Mutex::new(Roster::empty()),
80 auth: tokio::sync::Mutex::new(None),
81 pending_challenge: tokio::sync::Mutex::new(None),
82 rate_limiter: tokio::sync::Mutex::new(rate_limiter),
83 approval_tx: tokio::sync::Mutex::new(None),
84 event_tx: koi_common::events::event_channel().0,
85 posture_tx,
86 renewal_failure_count: std::sync::atomic::AtomicU32::new(0),
87 }),
88 }
89 }
90
91 /// Build the HTTP router for this domain.
92 ///
93 /// The binary crate mounts this at `/v1/certmesh/`.
94 pub fn routes(&self) -> Router {
95 http::routes(Arc::clone(&self.state))
96 }
97
98 /// Build the HTTP router for external embedding.
99 ///
100 /// This mirrors `routes()` but avoids exposing CertmeshState.
101 pub fn http_routes(&self) -> Router {
102 http::routes(Arc::clone(&self.state))
103 }
104
105 /// Build the inter-node router for the mTLS listener.
106 ///
107 /// Contains only routes that require mutual TLS between mesh members:
108 /// promote, health, renew, roster, set-hook.
109 pub fn inter_node_routes(&self) -> Router {
110 http::inter_node_routes(Arc::clone(&self.state))
111 }
112
113 /// Set the approval channel used for enrollment approvals.
114 pub async fn set_approval_channel(&self, tx: mpsc::Sender<ApprovalRequest>) {
115 *self.state.approval_tx.lock().await = Some(tx);
116 }
117
118 /// Subscribe to certmesh events.
119 pub fn subscribe(&self) -> broadcast::Receiver<CertmeshEvent> {
120 self.state.event_tx.subscribe()
121 }
122
123 /// Watch this node's posture (ADR-020 §5). The receiver always holds the
124 /// current [`Posture`] (so a new subscriber reads it immediately) and is
125 /// notified on every Open↔Authenticated transition — the signal a listener
126 /// supervisor uses to flip plain↔mTLS without polling. Transitions are also
127 /// surfaced as `KoiEvent::PostureChanged` by the embedded facade.
128 pub fn watch_posture(&self) -> watch::Receiver<Posture> {
129 self.state.posture_tx.subscribe()
130 }
131
132 /// Build the RFC 8555 ACME server state over this CA.
133 ///
134 /// The binary calls this when starting the dedicated server-auth TLS
135 /// listener, passing the ACME base URL, the Koi DNS zone, and the
136 /// `AcmeDnsSolver` bridge. The returned `AcmeState` shares this core's CA and
137 /// roster (so ACME issuance lands in the same roster as TOTP enrollment), and
138 /// is mounted via [`acme::routes`].
139 pub fn acme_state(&self, config: acme::AcmeStateConfig) -> std::sync::Arc<acme::AcmeState> {
140 acme::AcmeState::new(Arc::clone(&self.state), config)
141 }
142}