koi_compose/lib.rs
1//! Koi composition layer — the single place that constructs domain cores, installs the
2//! cross-domain integration bridges, runs the container orchestrator, assembles
3//! capability status, and tears everything down in order.
4//!
5//! Three consumers share it: the `koi` daemon (`daemon_mode`), the Windows service
6//! (`run_service`), and `koi-embedded`. Building the composition once makes Windows and
7//! embedded parity true *by construction* — the verified `koi install` defect (a weaker
8//! Windows daemon missing the orchestrator + certmesh background loops) cannot recur,
9//! because all three call the same code.
10//!
11//! This is a **composition crate**, not a domain crate: it depends on every domain it
12//! wires. Nothing depends on it except the top-level consumers, so the `koi-common`
13//! kernel and the domain crates keep clean dependency closures.
14
15/// The cross-domain integration-trait bridges (moved from the binary's `integrations.rs`).
16pub mod bridges;
17
18/// Certmesh role-driven background loops + the enrollment-approval pump (moved from the
19/// binary's `main.rs`). Shared so Windows-service and embedded daemons reach parity.
20pub mod certmesh;
21
22/// The container-runtime orchestrator: translates runtime lifecycle events into
23/// mDNS/DNS/health/proxy operations (moved from the binary's `orchestrator.rs`). Shared so
24/// Windows-service and embedded daemons can spawn it too.
25pub mod orchestrator;
26
27/// Daemon core composition: `build_cores` (the one core+bridge construction graph the
28/// daemon and the Windows service share), `init_certmesh_core`, and `ordered_shutdown`.
29pub mod cores;
30
31/// Unified capability-status assembly (`assemble_capabilities`) — the single capability
32/// ladder shared by `/v1/status`, the dashboard snapshot, and the embedded snapshot.
33pub mod status;
34
35#[cfg(test)]
36mod parity_tests {
37 //! Acceptance proof for the `koi install` parity fix (P07).
38 //!
39 //! The Windows service (`run_service`) and the foreground daemon (`daemon_mode`) now
40 //! spawn certmesh background tasks + the orchestrator through these exact composition
41 //! functions. Asserting the spawned-task inventory here — with no SCM and no network —
42 //! proves Windows gets the same task set the daemon does (the verified defect was a
43 //! structurally weaker Windows daemon missing precisely these tasks).
44
45 use std::sync::Arc;
46
47 use tokio::task::JoinHandle;
48 use tokio_util::sync::CancellationToken;
49
50 fn test_certmesh() -> Arc<koi_certmesh::CertmeshCore> {
51 let dir = std::env::temp_dir().join(format!("koi-compose-parity-{}", std::process::id()));
52 let paths = koi_certmesh::CertmeshPaths::with_data_dir(dir);
53 Arc::new(koi_certmesh::CertmeshCore::uninitialized_with_paths(paths))
54 }
55
56 fn test_runtime() -> Arc<koi_runtime::RuntimeCore> {
57 // Constructed but never `start_watching`'d — no backend connection, no network.
58 Arc::new(koi_runtime::RuntimeCore::new(koi_runtime::RuntimeConfig {
59 backend_kind: koi_runtime::RuntimeBackendKind::Auto,
60 socket_path: None,
61 }))
62 }
63
64 #[tokio::test]
65 async fn certmesh_role_loops_spawn_four_tasks_regardless_of_mdns() {
66 // renewal + roster sync + heartbeat + failover = 4, whether or not mDNS is present
67 // (failover is still spawned with mDNS=None; it exits early internally).
68 let certmesh = test_certmesh();
69 let cancel = CancellationToken::new();
70 let mut tasks: Vec<JoinHandle<()>> = Vec::new();
71
72 crate::certmesh::spawn_certmesh_background_tasks(
73 &certmesh, None, 5641, &cancel, &mut tasks,
74 );
75 assert_eq!(tasks.len(), 4, "expected the 4 certmesh role loops");
76
77 cancel.cancel();
78 for task in tasks {
79 let _ = task.await;
80 }
81 }
82
83 #[tokio::test]
84 async fn windows_parity_full_task_inventory() {
85 // Mirror the exact spawn sequence windows.rs run_service now uses with certmesh +
86 // runtime enabled: 1 approval pump + 4 certmesh role loops + 1 orchestrator = 6.
87 let certmesh = test_certmesh();
88 let runtime = test_runtime();
89 let cancel = CancellationToken::new();
90 let mut tasks: Vec<JoinHandle<()>> = Vec::new();
91
92 crate::certmesh::spawn_enrollment_approval(
93 &certmesh,
94 crate::certmesh::deny_and_log_decider(),
95 &cancel,
96 &mut tasks,
97 )
98 .await;
99 crate::certmesh::spawn_certmesh_background_tasks(
100 &certmesh, None, 5641, &cancel, &mut tasks,
101 );
102 tasks.push(crate::orchestrator::spawn_orchestrator(
103 &runtime,
104 crate::orchestrator::OrchestrationTargets {
105 mdns: None,
106 dns: None,
107 health: None,
108 proxy: None,
109 },
110 cancel.clone(),
111 ));
112
113 assert_eq!(
114 tasks.len(),
115 6,
116 "Windows parity: 1 approval + 4 certmesh loops + 1 orchestrator"
117 );
118
119 cancel.cancel();
120 for task in tasks {
121 let _ = task.await;
122 }
123 }
124}