liminal_server/server/connection/supervisor/supervisor_config.rs
1#[cfg(any(test, feature = "test-support"))]
2use super::LiminalConnectionServices;
3#[cfg(feature = "test-support")]
4use super::install_lys;
5use super::{
6 AdmissionReadiness, Arc, ConnectionNotifier, ConnectionServices, ConnectionSupervisor,
7 ConnectionSupervisorBuilder, LimitsConfig, LysInstall, PassVerifier, ServerConfig, ServerError,
8 ServerSignals, ShutdownHandle, SupervisorInner, build_connection_services,
9 configured_authentication, configured_lys,
10};
11#[cfg(test)]
12use super::{SubsystemFactory, build_connection_services_via};
13
14impl ConnectionSupervisor {
15 pub(in crate::server) fn install_signals(
16 &self,
17 signals: ServerSignals,
18 ) -> Result<(), ServerError> {
19 self.inner
20 .runtime
21 .signals
22 .set(signals)
23 .map_err(|_| ServerError::ListenerAccept {
24 message: "connection signals were already installed".to_owned(),
25 })
26 }
27
28 #[cfg(test)]
29 pub(in crate::server) fn signals_installed(&self) -> bool {
30 self.inner.runtime.signals.get().is_some()
31 }
32
33 pub(crate) fn current_server_incarnation(&self) -> Result<Option<u64>, ServerError> {
34 self.inner
35 .incarnations
36 .as_ref()
37 .map(|authority| authority.current_server_incarnation())
38 .transpose()
39 }
40
41 /// Creates a connection supervisor backed by the services the config's
42 /// `[services]` profile selects: the full liminal channel/conversation stack
43 /// (the default) or the capability-scoped worker front door. Profile
44 /// enforcement is [`build_connection_services`](super::services::build_connection_services)'s,
45 /// so this constructor can never build full services for a worker-front-door
46 /// config.
47 ///
48 /// # Errors
49 /// Returns [`ServerError`] when service construction or scheduler startup fails.
50 pub fn from_config(config: &ServerConfig) -> Result<Self, ServerError> {
51 Self::from_config_with_services(config, build_connection_services(config)?)
52 }
53
54 /// Installs a fixture's verified Lys authority and machine before binding.
55 ///
56 /// # Errors
57 /// A Lys authority or machine was already installed on this supervisor.
58 #[cfg(feature = "test-support")]
59 pub fn install_lys_for_tests(
60 &self,
61 admitter: Arc<dyn crate::auth_lys::session::LysAdmitter>,
62 network_id: String,
63 link_host: Arc<crate::link::LinkHost>,
64 ) -> Result<(), ServerError> {
65 install_lys(
66 &self.inner.runtime,
67 LysInstall {
68 admitter,
69 network_id,
70 link_host: Some(link_host),
71 services: None,
72 membership: None,
73 },
74 )
75 }
76
77 /// The installed host used by the connection routes.
78 #[cfg(feature = "test-support")]
79 #[must_use]
80 pub fn link_host_for_tests(&self) -> Option<&Arc<crate::link::LinkHost>> {
81 self.inner.runtime.link_host()
82 }
83
84 /// The installed admission seam used by the connection routes.
85 #[cfg(feature = "test-support")]
86 #[must_use]
87 pub fn lys_admitter_for_tests(
88 &self,
89 ) -> Option<&Arc<dyn crate::auth_lys::session::LysAdmitter>> {
90 self.inner
91 .runtime
92 .lys_admitter()
93 .map(|(admitter, _)| admitter)
94 }
95
96 /// [`Self::from_config`] with the §9 D2 subsystem factory injected.
97 ///
98 /// The factory is the only route to every scheduler-owning subsystem the
99 /// services construction builds, so a recording factory observes exactly what
100 /// was constructed; the connection scheduler itself (built below for BOTH
101 /// profiles) is the census baseline, not a census entry.
102 #[cfg(test)]
103 pub(super) fn from_config_via(
104 config: &ServerConfig,
105 subsystems: &dyn SubsystemFactory,
106 ) -> Result<Self, ServerError> {
107 Self::from_config_with_services(config, build_connection_services_via(config, subsystems)?)
108 }
109
110 fn from_config_with_services(
111 config: &ServerConfig,
112 services: Arc<dyn ConnectionServices>,
113 ) -> Result<Self, ServerError> {
114 // Absent `[auth]` leaves both `None`, so the connection stays open-access.
115 let authentication = config
116 .auth
117 .as_ref()
118 .map(configured_authentication)
119 .transpose()?;
120 let (auth_token, pass_verifier) =
121 authentication.map_or((None, None), |(token, verifier)| (Some(token), verifier));
122 SupervisorInner::new(
123 services,
124 None,
125 auth_token,
126 pass_verifier,
127 config
128 .auth
129 .as_ref()
130 .map(|auth| configured_lys(auth, tokio::runtime::Handle::try_current().ok()))
131 .transpose()?
132 .flatten(),
133 config.limits,
134 None,
135 )
136 .map(|inner| Self {
137 inner: Arc::new(inner),
138 })
139 }
140
141 /// Starts composing a connection supervisor over services the caller built
142 /// and keeps: the embedder's constructor.
143 ///
144 /// Every other public constructor fixes one of the three optional parts —
145 /// [`Self::from_config`] installs the configured `[auth]` (bearer AND pass
146 /// verifier) but builds its own services, [`Self::with_services_and_auth`]
147 /// carries a bearer and never a pass verifier, and
148 /// [`Self::with_services_and_notifier`] carries a notifier and no auth at
149 /// all. An application that embeds liminal — holding the
150 /// `Arc<LiminalConnectionServices>` to publish in-process, verifying
151 /// registry passes at Connect, and observing connection lifecycle through a
152 /// notifier — needs all three at once, and this is the one route to them.
153 /// Nothing set on the builder defaults exactly as the non-config
154 /// constructors default it: open access, no notifier, signed default
155 /// limits.
156 #[must_use]
157 pub fn builder(services: Arc<dyn ConnectionServices>) -> ConnectionSupervisorBuilder {
158 ConnectionSupervisorBuilder {
159 services,
160 notifier: None,
161 auth_token: None,
162 pass_verifier: None,
163 lys: None,
164 limits: LimitsConfig::default(),
165 }
166 }
167
168 /// Creates a connection supervisor with no configured channels.
169 ///
170 /// # Errors
171 /// Returns [`ServerError`] when scheduler startup fails.
172 #[cfg(any(test, feature = "test-support"))]
173 pub fn new() -> Result<Self, ServerError> {
174 Self::with_services(Arc::new(LiminalConnectionServices::empty()?))
175 }
176
177 /// Creates a connection supervisor using an explicit service adapter.
178 ///
179 /// # Errors
180 /// Returns [`ServerError`] when scheduler startup fails.
181 pub fn with_services(services: Arc<dyn ConnectionServices>) -> Result<Self, ServerError> {
182 SupervisorInner::new(
183 services,
184 None,
185 None,
186 None,
187 None,
188 LimitsConfig::default(),
189 None,
190 )
191 .map(|inner| Self {
192 inner: Arc::new(inner),
193 })
194 }
195
196 /// Creates a connection supervisor with an explicit service adapter and the
197 /// configured connection auth token.
198 ///
199 /// This is the production constructor for callers that build services
200 /// themselves (the runtime needs the shared channel cluster before the
201 /// supervisor takes ownership) and therefore cannot use
202 /// [`Self::from_config`]: without it the configured `[auth]` token would be
203 /// silently dropped and the server would run open-access.
204 ///
205 /// # Errors
206 /// Returns [`ServerError`] when scheduler startup fails.
207 pub fn with_services_and_auth(
208 services: Arc<dyn ConnectionServices>,
209 auth_token: Option<Vec<u8>>,
210 ) -> Result<Self, ServerError> {
211 Self::with_services_auth_and_limits(services, auth_token, LimitsConfig::default())
212 }
213
214 /// Creates a connection supervisor with explicit services, authentication,
215 /// and operational limits.
216 ///
217 /// Production runtime construction uses this form so the durable
218 /// incarnation stream's complete-reference bound is the same signed
219 /// `max_connections` bound enforced by connection admission.
220 ///
221 /// # Errors
222 /// Returns [`ServerError`] when incarnation startup or scheduler startup fails.
223 pub fn with_services_auth_and_limits(
224 services: Arc<dyn ConnectionServices>,
225 auth_token: Option<Vec<u8>>,
226 limits: LimitsConfig,
227 ) -> Result<Self, ServerError> {
228 Self::with_services_auth_limits_and_fatal_shutdown(services, auth_token, limits, None)
229 }
230
231 /// Production composition with the process-wide shutdown activation that a
232 /// post-Open participant fatal must join, without a Lys install: the
233 /// fixtures' constructor.
234 #[cfg(test)]
235 pub(crate) fn with_fatal_shutdown(
236 services: Arc<dyn ConnectionServices>,
237 auth_token: Option<Vec<u8>>,
238 pass_verifier: Option<PassVerifier>,
239 limits: LimitsConfig,
240 fatal_shutdown: ShutdownHandle,
241 ) -> Result<Self, ServerError> {
242 Self::with_lys_and_fatal_shutdown(
243 services,
244 auth_token,
245 pass_verifier,
246 None,
247 limits,
248 fatal_shutdown,
249 )
250 }
251
252 /// The production constructor: `[auth]` whole, the Lys install
253 /// (`[auth.lys]`: admitter, link host, service bindings) included, with
254 /// the fatal shutdown handle. The server's `run` builds through here so
255 /// Lys reaches every connection it admits (ACCESS-003 R1 FOUND: the
256 /// path without `lys` installed none in production).
257 pub(crate) fn with_lys_and_fatal_shutdown(
258 services: Arc<dyn ConnectionServices>,
259 auth_token: Option<Vec<u8>>,
260 pass_verifier: Option<PassVerifier>,
261 lys: Option<LysInstall>,
262 limits: LimitsConfig,
263 fatal_shutdown: ShutdownHandle,
264 ) -> Result<Self, ServerError> {
265 SupervisorInner::new(
266 services,
267 None,
268 auth_token,
269 pass_verifier,
270 lys,
271 limits,
272 Some(fatal_shutdown),
273 )
274 .map(|inner| Self {
275 inner: Arc::new(inner),
276 })
277 }
278
279 fn with_services_auth_limits_and_fatal_shutdown(
280 services: Arc<dyn ConnectionServices>,
281 auth_token: Option<Vec<u8>>,
282 limits: LimitsConfig,
283 fatal_shutdown: Option<ShutdownHandle>,
284 ) -> Result<Self, ServerError> {
285 SupervisorInner::new(
286 services,
287 None,
288 auth_token,
289 None,
290 None,
291 limits,
292 fatal_shutdown,
293 )
294 .map(|inner| Self {
295 inner: Arc::new(inner),
296 })
297 }
298
299 /// Creates a connection supervisor with an explicit service adapter and a
300 /// connection-keyed worker-registration notifier.
301 ///
302 /// The `notifier` is invoked when a worker registers on a connection and when
303 /// such a connection closes, and when a pass-stamped connection attaches
304 /// and closes. Supervisors built via [`Self::with_services`],
305 /// [`Self::from_config`], or [`Self::new`] carry no notifier, so liminal still
306 /// runs standalone; a `WorkerRegister` frame is then accepted without any
307 /// application callback.
308 ///
309 /// # Errors
310 /// Returns [`ServerError`] when scheduler startup fails.
311 pub fn with_services_and_notifier(
312 services: Arc<dyn ConnectionServices>,
313 notifier: Arc<dyn ConnectionNotifier>,
314 ) -> Result<Self, ServerError> {
315 SupervisorInner::new(
316 services,
317 Some(notifier),
318 None,
319 None,
320 None,
321 LimitsConfig::default(),
322 None,
323 )
324 .map(|inner| Self {
325 inner: Arc::new(inner),
326 })
327 }
328
329 /// A handle on whether this server can admit a connection, for the
330 /// readiness probe (P0 #56 R4).
331 ///
332 /// Handed to `SharedReadinessState::track_admission` once the supervisor
333 /// exists. The health endpoint binds BEFORE the supervisor is built —
334 /// liveness has to be answerable while the rest of the server is still
335 /// coming up — so this cannot be wired at readiness construction and is
336 /// installed afterwards instead.
337 #[must_use]
338 pub fn admission_readiness(&self) -> AdmissionReadiness {
339 self.inner.admission_readiness.clone()
340 }
341
342 /// The channel membership gate `[auth.lys.membership]` installed
343 /// (ACCESS-006 R2), for the ACCESS-005 grant owner: it installs itself
344 /// with [`crate::auth_membership::MembershipGate::install_authority`] and
345 /// drives each applied change and resume into the gate through
346 /// [`crate::auth_membership::MembershipListener`]. `None` without the
347 /// section.
348 #[must_use]
349 pub fn membership_gate(&self) -> Option<Arc<crate::auth_membership::MembershipGate>> {
350 self.inner.runtime.membership_gate().cloned()
351 }
352}