liminal-server 0.28.6

Standalone server for the liminal messaging bus
Documentation
//! The one construction installer (ACCESS-015 R1): the Lys boot and an open
//! exchange's boot install construction through the same function, differing
//! only in their effects and approval owner.

use std::sync::Arc;

use super::{LiminalConnectionServices, SiteDraftHolder};
use crate::services::lifecycle::{Lifecycle, site};
use site::SiteError;

impl LiminalConnectionServices {
    /// Installs the lifecycle projection once, using the same channel scheduler
    /// and the configured agent watcher budgets. No second scheduler is built.
    ///
    /// # Errors
    /// Missing construction bounds, machine pass, agent registry or issuer.
    pub fn install_construction(
        &self,
        lifecycle: Arc<Lifecycle>,
        lys: &crate::config::types::LysConfig,
        http: Arc<dyn crate::services::lys_http::LysHttp>,
        drafts: Arc<dyn SiteDraftHolder>,
    ) -> Result<(), SiteError> {
        let config = lys
            .construction
            .as_ref()
            .ok_or(SiteError::Invalid("[auth.lys.construction]"))?;
        let agents = self
            .agents
            .as_ref()
            .ok_or(SiteError::Invalid("[auth.lys.agents]"))?;
        let pass = lys
            .machine
            .as_ref()
            .map(crate::link_pass::MachinePass::configured)
            .transpose()
            .map_err(|_| SiteError::Invalid("construction machine pass"))?;
        let effects = Arc::new(site::effects::LysSiteEffects::new(
            http,
            pass,
            Arc::clone(agents),
            site::effects::GrantSources {
                placement: lys
                    .app_credential_path
                    .clone()
                    .map(|path| (path, lys.network_id.clone())),
                ..config.grants.clone()
            },
            config.limits.text_bytes,
        ));
        self.install_sites(
            lifecycle,
            config.limits,
            lys.templates.policy.clone(),
            effects,
            drafts,
        )
    }

    /// Installs an open exchange's construction (ACCESS-015 R1): the same
    /// installer, with every act its declared holder's own and no effect in Lys.
    ///
    /// # Errors
    /// No top-level `[agents]`, or the installer's own refusal.
    pub fn install_open_construction(
        &self,
        lifecycle: Arc<Lifecycle>,
        limits: site::SiteLimits,
        templates: Option<crate::services::lifecycle::templates::TemplatePolicy>,
    ) -> Result<(), SiteError> {
        let agents = self.agents.as_ref().ok_or(SiteError::Invalid(
            "[open.construction] requires top-level [agents]",
        ))?;
        self.install_sites(
            lifecycle,
            limits,
            templates,
            Arc::new(site::declared::OpenSiteEffects::new(Arc::clone(agents))),
            Arc::new(site::declared::DeclaredActs),
        )
    }

    /// The one construction installer: the retained plan stream on the
    /// existing scheduler with the agent registry's bounds, the registry's
    /// construction bindings, the console signal, the approval owner and the
    /// lifecycle the site routes serve.
    fn install_sites(
        &self,
        lifecycle: Arc<Lifecycle>,
        limits: site::SiteLimits,
        templates: Option<crate::services::lifecycle::templates::TemplatePolicy>,
        effects: Arc<dyn site::effects::SiteEffects>,
        drafts: Arc<dyn SiteDraftHolder>,
    ) -> Result<(), SiteError> {
        let agents = self.agents.as_ref().ok_or(SiteError::Invalid(
            "construction requires an agent registry",
        ))?;
        let mut bounds = *agents.bounds();
        bounds.max_event_bytes = limits.request_bytes.get();
        let retained = crate::agents::retained::Retained::new(
            bounds,
            self.cluster.supervisor().clone(),
            self.console.device(),
        );
        lifecycle.configure_sites(limits, self.durable_store(), templates, effects, retained)?;
        lifecycle.validate_site_storage()?;
        lifecycle.bind_site_console(&self.console)?;
        self.site_drafts
            .set(drafts)
            .map_err(|_| SiteError::Invalid("construction drafts already installed"))?;
        self.site_lifecycle
            .set(lifecycle)
            .map_err(|_| SiteError::Invalid("construction already installed"))?;
        Ok(())
    }
}