Skip to main content

liminal_server/services/site/
install.rs

1//! The one construction installer (ACCESS-015 R1): the Lys boot and an open
2//! exchange's boot install construction through the same function, differing
3//! only in their effects and approval owner.
4
5use std::sync::Arc;
6
7use super::{LiminalConnectionServices, SiteDraftHolder};
8use crate::services::lifecycle::{Lifecycle, site};
9use site::SiteError;
10
11impl LiminalConnectionServices {
12    /// Installs the lifecycle projection once, using the same channel scheduler
13    /// and the configured agent watcher budgets. No second scheduler is built.
14    ///
15    /// # Errors
16    /// Missing construction bounds, machine pass, agent registry or issuer.
17    pub fn install_construction(
18        &self,
19        lifecycle: Arc<Lifecycle>,
20        lys: &crate::config::types::LysConfig,
21        http: Arc<dyn crate::services::lys_http::LysHttp>,
22        drafts: Arc<dyn SiteDraftHolder>,
23    ) -> Result<(), SiteError> {
24        let config = lys
25            .construction
26            .as_ref()
27            .ok_or(SiteError::Invalid("[auth.lys.construction]"))?;
28        let agents = self
29            .agents
30            .as_ref()
31            .ok_or(SiteError::Invalid("[auth.lys.agents]"))?;
32        let pass = lys
33            .machine
34            .as_ref()
35            .map(crate::link_pass::MachinePass::configured)
36            .transpose()
37            .map_err(|_| SiteError::Invalid("construction machine pass"))?;
38        let effects = Arc::new(site::effects::LysSiteEffects::new(
39            http,
40            pass,
41            Arc::clone(agents),
42            site::effects::GrantSources {
43                placement: lys
44                    .app_credential_path
45                    .clone()
46                    .map(|path| (path, lys.network_id.clone())),
47                ..config.grants.clone()
48            },
49            config.limits.text_bytes,
50        ));
51        self.install_sites(
52            lifecycle,
53            config.limits,
54            lys.templates.policy.clone(),
55            effects,
56            drafts,
57        )
58    }
59
60    /// Installs an open exchange's construction (ACCESS-015 R1): the same
61    /// installer, with every act its declared holder's own and no effect in Lys.
62    ///
63    /// # Errors
64    /// No top-level `[agents]`, or the installer's own refusal.
65    pub fn install_open_construction(
66        &self,
67        lifecycle: Arc<Lifecycle>,
68        limits: site::SiteLimits,
69        templates: Option<crate::services::lifecycle::templates::TemplatePolicy>,
70    ) -> Result<(), SiteError> {
71        let agents = self.agents.as_ref().ok_or(SiteError::Invalid(
72            "[open.construction] requires top-level [agents]",
73        ))?;
74        self.install_sites(
75            lifecycle,
76            limits,
77            templates,
78            Arc::new(site::declared::OpenSiteEffects::new(Arc::clone(agents))),
79            Arc::new(site::declared::DeclaredActs),
80        )
81    }
82
83    /// The one construction installer: the retained plan stream on the
84    /// existing scheduler with the agent registry's bounds, the registry's
85    /// construction bindings, the console signal, the approval owner and the
86    /// lifecycle the site routes serve.
87    fn install_sites(
88        &self,
89        lifecycle: Arc<Lifecycle>,
90        limits: site::SiteLimits,
91        templates: Option<crate::services::lifecycle::templates::TemplatePolicy>,
92        effects: Arc<dyn site::effects::SiteEffects>,
93        drafts: Arc<dyn SiteDraftHolder>,
94    ) -> Result<(), SiteError> {
95        let agents = self.agents.as_ref().ok_or(SiteError::Invalid(
96            "construction requires an agent registry",
97        ))?;
98        let mut bounds = *agents.bounds();
99        bounds.max_event_bytes = limits.request_bytes.get();
100        let retained = crate::agents::retained::Retained::new(
101            bounds,
102            self.cluster.supervisor().clone(),
103            self.console.device(),
104        );
105        lifecycle.configure_sites(limits, self.durable_store(), templates, effects, retained)?;
106        lifecycle.validate_site_storage()?;
107        lifecycle.bind_site_console(&self.console)?;
108        self.site_drafts
109            .set(drafts)
110            .map_err(|_| SiteError::Invalid("construction drafts already installed"))?;
111        self.site_lifecycle
112            .set(lifecycle)
113            .map_err(|_| SiteError::Invalid("construction already installed"))?;
114        Ok(())
115    }
116}