liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
//! The machine pass's first issue, asked of Lys after the listener binds
//! (SRV-041 R4), so a boot never waits on a round trip to the issuer.
//!
//! A link open that comes first waits on the authority's lock and is served
//! by this issue; one that comes after finds the pass held and asks nothing.
use super::{Authority, LysConfig, MachineConfig};
use crate::ServerError;
use std::sync::Arc;
use std::thread::JoinHandle;

/// Whether the first issue can wait for the bind: only a dialled pass that
/// nothing on the start path reads. The services, the broker and the relay
/// bind with the pass held at their build, a peers block cannot serve
/// without it, and the configured links make their first attempts on the
/// start thread, so each keeps the issue before the bind.
pub(super) fn defers(lys: &LysConfig, machine: &MachineConfig) -> bool {
    !machine.agent_id.is_empty()
        && machine.carrier_choice.peers.is_none()
        && machine.links.is_empty()
        && machine.carrier_choice.broker_echo.is_none()
        && lys.services.is_none()
        && lys.broker.is_none()
        && !lys.relay.as_ref().is_some_and(|relay| relay.enabled)
}

/// The first issue a boot left for after its bind.
#[derive(Debug)]
pub struct StartIssue {
    authority: Arc<Authority>,
    now: u64,
}

impl StartIssue {
    pub(super) const fn new(authority: Arc<Authority>, now: u64) -> Self {
        Self { authority, now }
    }

    /// Asks on its own thread. A refusal is named as the boot's and the next
    /// link open asks again (ACCESS-009 R1); the server keeps serving.
    ///
    /// # Errors
    /// The thread could not start.
    pub fn begin(self) -> Result<JoinHandle<()>, ServerError> {
        std::thread::Builder::new()
            .name("liminal-machine-pass".to_owned())
            .spawn(move || self.ask())
            .map_err(|error| ServerError::ConfigValidation {
                message: format!("auth.lys.machine: the pass issue could not start: {error}"),
            })
    }

    fn ask(&self) {
        let started = std::time::Instant::now();
        match self.authority.refresh(self.now, "boot") {
            Ok(()) => tracing::info!("machine pass issued after the bind"),
            // `refresh` named and counted it at warn; the next open asks again.
            Err(error) => {
                tracing::debug!(reason = %error, "machine pass issue refused after the bind");
            }
        }
        crate::server::boot_steps::finished("machine_pass_issue", started);
    }
}

#[cfg(test)]
#[path = "start_issue_tests.rs"]
mod tests;