Skip to main content

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}