Skip to main content

clawft_kernel/
mesh_mode.rs

1//! Which mesh mode a kernel boots in (P3-K0, P3-U).
2//!
3//! The mesh runs either inside the kernel process (`Collapsed`), in the
4//! machine mesh service the daemon is a client of (`Service`), or not at all
5//! (`Off`). `kernel.mesh.service = auto | required | off` picks between the
6//! first two (ADR-103 D2).
7//!
8//! [`select`] is pure configuration: it never probes a socket, so a kernel
9//! booted without the daemon's service glue (one-shot CLI boots, tests) is
10//! never silently left without a mesh. A caller that has probed the service
11//! passes the outcome to [`decide`].
12
13use std::path::{Path, PathBuf};
14
15use clawft_types::config::{MeshConfig, MeshServicePolicy};
16
17/// Default socket of the machine mesh service.
18pub const DEFAULT_SERVICE_SOCKET: &str = "/var/run/weftos/mesh.sock";
19/// Environment override for the service socket (tests and probes).
20pub const SERVICE_SOCKET_ENV: &str = "WEFTOS_MESH_SOCKET";
21
22/// The mesh mode selected for this boot.
23#[derive(Debug, Clone, PartialEq, Eq)]
24#[non_exhaustive]
25pub enum MeshMode {
26    /// Mesh disabled: no listener, no runtime.
27    Off,
28    /// Mesh runs in this kernel (listener, seeds, `MeshService`).
29    Collapsed,
30    /// The machine mesh service owns the listener; this kernel is a client
31    /// reaching it over `sock`.
32    Service {
33        /// The service's mesh-local socket.
34        sock: PathBuf,
35    },
36}
37
38/// What probing the service socket found (`auto` and `required` only).
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum ServiceProbe {
41    /// A service answered and verified.
42    Present,
43    /// Nothing is there (no socket, connection refused).
44    Absent,
45    /// Something answered but failed verification or refused us; the text is
46    /// the reason. Fatal under every policy.
47    Refused(String),
48}
49
50/// The service socket: config override, else `WEFTOS_MESH_SOCKET`, else the
51/// default.
52pub fn service_socket(cfg: &MeshConfig) -> PathBuf {
53    resolve_socket(cfg, std::env::var(SERVICE_SOCKET_ENV).ok().as_deref())
54}
55
56fn resolve_socket(cfg: &MeshConfig, env: Option<&str>) -> PathBuf {
57    cfg.service_socket
58        .as_deref()
59        .filter(|s| !s.is_empty())
60        .or(env.filter(|s| !s.is_empty()))
61        .map_or_else(|| PathBuf::from(DEFAULT_SERVICE_SOCKET), PathBuf::from)
62}
63
64/// Choose the mesh mode from configuration alone (no probing).
65///
66/// Service mode is never returned here: it needs a verified service, which
67/// only [`decide`] can confirm.
68pub fn select(cfg: &MeshConfig) -> MeshMode {
69    if cfg.enabled {
70        MeshMode::Collapsed
71    } else {
72        MeshMode::Off
73    }
74}
75
76/// Whether the policy asks for a probe at all.
77pub fn wants_probe(cfg: &MeshConfig) -> bool {
78    match cfg.service {
79        MeshServicePolicy::Off => false,
80        MeshServicePolicy::Auto => cfg.enabled,
81        MeshServicePolicy::Required => true,
82    }
83}
84
85/// Combine configuration with a probe outcome. The probe is `None` under
86/// `off`. Errors carry the reason the boot must fail with.
87///
88/// Mesh disabled with `service = auto` stays off: a daemon that did not ask
89/// for a mesh does not register with one ([`wants_probe`] is false, so no
90/// probe is made). `required` always needs a service, whatever `enabled` says.
91pub fn decide(cfg: &MeshConfig, probe: Option<ServiceProbe>) -> Result<MeshMode, String> {
92    let sock = service_socket(cfg);
93    decide_at(cfg, probe, &sock)
94}
95
96fn decide_at(cfg: &MeshConfig, probe: Option<ServiceProbe>, sock: &Path) -> Result<MeshMode, String> {
97    match (cfg.service, probe) {
98        (MeshServicePolicy::Off, _) => Ok(select(cfg)),
99        (MeshServicePolicy::Auto, _) if !cfg.enabled => Ok(MeshMode::Off),
100        (_, Some(ServiceProbe::Refused(why))) => Err(format!(
101            "the machine mesh service at {} refused or failed verification: {why}",
102            sock.display()
103        )),
104        (_, Some(ServiceProbe::Present)) => Ok(MeshMode::Service { sock: sock.to_path_buf() }),
105        (MeshServicePolicy::Required, _) => Err(format!(
106            "kernel.mesh.service = \"required\" but no machine mesh service answers at {}; \
107             start it (`weaver mesh serve`, or the installed service) or set \
108             kernel.mesh.service = \"auto\"",
109            sock.display()
110        )),
111        (_, _) => Ok(select(cfg)),
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    fn cfg(policy: MeshServicePolicy, enabled: bool) -> MeshConfig {
120        MeshConfig { enabled, service: policy, ..MeshConfig::default() }
121    }
122
123    #[test]
124    fn disabled_is_off() {
125        assert_eq!(select(&MeshConfig::default()), MeshMode::Off);
126    }
127
128    #[test]
129    fn enabled_is_collapsed() {
130        assert_eq!(select(&cfg(MeshServicePolicy::Auto, true)), MeshMode::Collapsed);
131    }
132
133    #[test]
134    fn auto_uses_a_present_service_else_collapses() {
135        let c = cfg(MeshServicePolicy::Auto, true);
136        assert!(matches!(
137            decide(&c, Some(ServiceProbe::Present)),
138            Ok(MeshMode::Service { .. })
139        ));
140        assert_eq!(decide(&c, Some(ServiceProbe::Absent)), Ok(MeshMode::Collapsed));
141    }
142
143    #[test]
144    fn required_without_a_service_fails_with_the_reason() {
145        let c = cfg(MeshServicePolicy::Required, true);
146        let e = decide(&c, Some(ServiceProbe::Absent)).unwrap_err();
147        assert!(e.contains("required") && e.contains("mesh.sock"), "{e}");
148    }
149
150    #[test]
151    fn a_refusing_service_is_fatal_even_under_auto() {
152        let c = cfg(MeshServicePolicy::Auto, true);
153        let e = decide(&c, Some(ServiceProbe::Refused("machine_key_changed".into()))).unwrap_err();
154        assert!(e.contains("machine_key_changed"), "{e}");
155    }
156
157    #[test]
158    fn auto_with_the_mesh_disabled_stays_off_without_probing() {
159        let c = cfg(MeshServicePolicy::Auto, false);
160        assert!(!wants_probe(&c));
161        assert_eq!(decide(&c, Some(ServiceProbe::Present)), Ok(MeshMode::Off));
162    }
163
164    #[test]
165    fn off_never_uses_the_service() {
166        let c = cfg(MeshServicePolicy::Off, true);
167        assert_eq!(decide(&c, None), Ok(MeshMode::Collapsed));
168        assert!(!wants_probe(&c));
169    }
170
171    #[test]
172    fn socket_resolution_order() {
173        let mut c = MeshConfig::default();
174        assert_eq!(resolve_socket(&c, None), Path::new(DEFAULT_SERVICE_SOCKET));
175        assert_eq!(resolve_socket(&c, Some("/e.sock")), Path::new("/e.sock"));
176        c.service_socket = Some("/c.sock".into());
177        assert_eq!(resolve_socket(&c, Some("/e.sock")), Path::new("/c.sock"));
178    }
179}