liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
use std::net::SocketAddr;

/// `[auth.lys]`: the Lys issuer whose passes are admitted (ACCESS-001 R2).
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LysConfig {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub drafts: Option<crate::auth_lys::live::drafts::DraftConfig>,
    /// The issuer URL exactly as the pass's `iss` names it.
    pub issuer: String,
    /// The Lys network liminal's subjects are placed under.
    pub network_id: String,
    /// Path to the issuer's published key set as JSON, read once at start.
    pub keys_path: std::path::PathBuf,
    /// Ask the issuer live for deliberate decisions (opening a link,
    /// deploying a service, an administrator's act) with the holder's own
    /// pass. Off, Lys cannot be asked and every deliberate route refuses by
    /// that name; it never falls back to the pass.
    #[serde(default)]
    pub live: bool,
    /// This liminal as a machine Lys knows, for links (ACCESS-002 R1).
    #[serde(default)]
    pub machine: Option<MachineConfig>,
    /// Argus's functions as liminal services, bound to Lys and the server's
    /// publish path (ACCESS-003 R1).
    #[serde(default)]
    pub services: Option<LysServicesConfig>,
    /// Address-only broker and its observed-address UDP echo.
    #[serde(default)]
    pub broker: Option<BrokerConfig>,
    /// Blind forwarding of marked relay streams.
    #[serde(default)]
    pub relay: Option<crate::services::relay::RelayConfig>,
    /// Channel membership from Lys's one shared membership contract
    /// (ACCESS-006 R1): the explicit product mapping of liminal channels to
    /// placed Lys resources, and the provider route it is asked through.
    /// Absent, no channel is membership-protected and every channel keeps
    /// its prior contract.
    #[serde(default)]
    pub membership: Option<MembershipConfig>,
    /// One bounded reader for this log; required beside membership protection.
    #[serde(default)]
    pub grant_stream: Option<super::GrantStreamConfig>,
    /// Agent station bounds; required when any agent is placed.
    #[serde(default)]
    pub agents: Option<crate::agents::config::AgentsConfig>,
    /// The templates services are created from (SRV-028 R1): the only
    /// command lines this server will ever run for a seat.
    #[serde(default)]
    pub templates: crate::services::lifecycle::templates::TemplateSources,
    /// Required operator bounds and machine-held builder delegation sources.
    #[serde(default)]
    pub construction: Option<crate::services::lifecycle::site::ConstructionConfig>,
    /// The base of Lys's API routes (`.../api/`) for live asks, placements
    /// and the service routes, when they are not served at the issuer
    /// itself; absent, the issuer is the base, as before.
    #[serde(default)]
    pub api_url: Option<String>,
    /// Path to this liminal's Lys app credential, read once at start; a new
    /// service and its subjects are placed under the network with it
    /// (SRV-028 R3). Absent, every create is refused by name.
    #[serde(default)]
    pub app_credential_path: Option<std::path::PathBuf>,
}

/// `[[auth.lys.templates]]`: one command a seat may create services from
/// (SRV-028 R1). Read-only at run time.
#[derive(Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ServiceTemplateConfig {
    /// The template's name, one segment.
    pub name: String,
    /// The program, an absolute path to an executable file.
    pub command: std::path::PathBuf,
    /// Its arguments, passed as written.
    #[serde(default)]
    pub args: Vec<String>,
    /// The subject roots a service of this template serves; each service
    /// serves `<root>.<service name>`.
    pub subjects: Vec<String>,
    /// Names of variables passed through from the server's environment;
    /// nothing else of it reaches the process.
    #[serde(default)]
    pub env: Vec<String>,
    /// How long a stop waits after SIGTERM before SIGKILL; positive.
    pub stop_timeout_ms: u64,
}

/// `[auth.lys.membership]`: channel membership bound to the verified holder
/// and the placed resource (ACCESS-006 R1).
///
/// The mapping is explicit: a channel is membership-protected only when it is
/// listed here with its workspace and placed resource, and a participant
/// conversation is placed under a channel only when it is listed here. No
/// mapping is guessed from a subject string, and a readable parent is never
/// taken as membership of a child. Every bound is required and positive;
/// none has a default. Validated whole when the membership gate is built at
/// start ([`crate::auth_membership::MembershipGate::new`]).
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MembershipConfig {
    /// The membership contract version this liminal speaks; it must equal
    /// `lys_pass::membership::CONTRACT_VERSION`.
    pub contract: u32,
    /// The identity of the Lys grant log decisions are asked from.
    pub log_identity: String,
    /// The reset epoch of that grant log.
    pub log_epoch: u64,
    /// The product schema's workspace kind, as Lys places it.
    pub workspace_kind: String,
    /// The product schema's channel kind, as Lys places it.
    pub channel_kind: String,
    /// Path to this liminal's app credential for the Lys membership routes,
    /// read once at start. Its bytes never appear in a `Debug`.
    pub credential_path: std::path::PathBuf,
    /// Most rows one membership page carries; positive.
    pub page_rows: u32,
    /// Most encoded row bytes and HTTP answer bytes; positive. The same cap
    /// bounds reading the app credential before opening any protected route.
    pub page_bytes: u32,
    /// Most decisions the local membership index retains at once; positive.
    /// A decision past it is refused by name before anything is allocated
    /// for it, never evicted to make room.
    pub max_decisions: usize,
    /// The workspace a Lys-admitted `Connect` must hold a current grant in
    /// (scoped guest admission). Absent, `Connect` asks no admission and a
    /// protected channel is still decided per act.
    #[serde(default)]
    pub connect_workspace: Option<String>,
    /// The membership-protected channels.
    pub channels: Vec<MembershipChannelConfig>,
    /// Participant conversations placed under a protected channel.
    #[serde(default)]
    pub conversations: Vec<MembershipConversationConfig>,
}

/// One membership-protected channel: the liminal channel name, the
/// workspace it is placed under and the placed resource id.
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MembershipChannelConfig {
    /// The liminal channel (subject) name, matched exactly.
    pub channel: String,
    /// The workspace resource id the channel is placed under.
    pub workspace: String,
    /// The placed channel resource id.
    pub resource: String,
}

/// One participant conversation placed under a protected channel.
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MembershipConversationConfig {
    /// The participant conversation id.
    pub conversation: u64,
    /// The protected channel it is placed under, as listed in `channels`.
    pub channel: String,
}

/// `[auth.lys.services]`: the production bindings of the ACCESS-003
/// services. They ask Lys as this machine, so `[auth.lys.machine]` is
/// required beside it.
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LysServicesConfig {
    /// The machine id Lys records, on whose runner `seats.<name>` starts.
    pub machine: String,
    /// The host name service messages are published as (`liminal.<host>`).
    pub host: String,
    /// Run the context watch over the `context-watch` channel, reporting
    /// each reading to Lys.
    #[serde(default)]
    pub context_watch: bool,
}

/// `[auth.lys.broker]`: the address-only broker's UDP echo listener.
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BrokerConfig {
    /// The socket on which a device asks for its observed address.
    pub udp_listen: SocketAddr,
}

/// `[auth.lys.machine]`: this liminal's agent identity and pass.
///
/// Its links are keyed from the Ed25519 identity Lys recorded for it,
/// its own end of every crossing is decided from its own pass, and the
/// holders that may open links to it are named with their public keys.
/// The link identity is an agent's own key and pass. The machine section
/// name is retained so existing file-pass configurations still read.
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MachineConfig {
    /// Carrier order and message-scoped direct deadlines.
    #[serde(default)]
    pub carrier_choice: crate::link_open::ChoiceConfig,
    /// The device this machine is a district of, when configured.
    #[serde(default)]
    pub parent: Option<String>,
    /// Path to the agent's Ed25519 seed, 64 hexadecimal digits, read once
    /// at start. The link handshake keys from its X25519 derivation.
    pub identity_path: std::path::PathBuf,
    /// Path to the agent's own Lys pass, verified at start; absent when
    /// the signed agent dial is configured.
    #[serde(default)]
    pub pass_path: std::path::PathBuf,
    /// The agent whose capability certificate names this identity's key.
    /// With it set, the pass is asked of Lys at start and again at any link
    /// open while none is held or the held one is expired or due for
    /// renewal; a start whose ask Lys refuses is logged once and asks again
    /// at the next open, never by a restart (ACCESS-009).
    #[serde(default)]
    pub agent_id: String,
    /// The app the agent asks the issuer for a pass to.
    #[serde(default)]
    pub audience: String,
    /// The public key recorded for each holder that may open a link,
    /// holder id to Ed25519 public key as 64 hexadecimal digits.
    #[serde(default)]
    pub machines: std::collections::BTreeMap<String, String>,
    /// Outbound links keyed from this machine, each configured by name.
    #[serde(default)]
    pub links: Vec<OutboundLinkConfig>,
}

/// One exporting link; an absent wire address leaves carrier choice to the caller.
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct OutboundLinkConfig {
    /// The link id placed under the configured network.
    pub link_id: String,
    /// The holder whose Ed25519 key is recorded in this machine's directory mirror.
    pub peer_holder: String,
    /// The peer's wire listener as host:port, if this link uses the wire.
    #[serde(default)]
    pub peer_address: Option<String>,
    /// Placed subject prefixes exported over this link.
    pub exports: Vec<String>,
}