diff --git a/README.md b/README.md index 7be38b0..0eb277a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The Clawdie control plane core — a small, cross-platform (FreeBSD + Linux) Rus daemon that unifies coordination (task board, agent registry, skills catalog) with cache-first cost discipline (byte-stable prompt prefixes, cache-hit metering). -**Status:** 11 crates; workspace gates are expected to be fmt/clippy/test/release green. Avoid fixed test-count status here — run the gate commands below for the current count. Phase 3 (coordination core) is in progress. +**Status:** 10 crates; workspace gates are expected to be fmt/clippy/test/release green. Avoid fixed test-count status here — run the gate commands below for the current count. Phase 3 (coordination core) is in progress. Next ISO integration plan: `docs/ISO-INTEGRATION-PLAN.md`. ISO acceptance runbook: `docs/ISO-ACCEPTANCE-RUNBOOK.md`. diff --git a/crates/colibri-client/src/lib.rs b/crates/colibri-client/src/lib.rs index 9bd5da5..f0cd0a5 100644 --- a/crates/colibri-client/src/lib.rs +++ b/crates/colibri-client/src/lib.rs @@ -109,6 +109,7 @@ impl DaemonClient { session_id, system_prompt, local_args: None, + jail: None, }) .await } diff --git a/crates/colibri-daemon/src/lib.rs b/crates/colibri-daemon/src/lib.rs index ba90870..18131fb 100644 --- a/crates/colibri-daemon/src/lib.rs +++ b/crates/colibri-daemon/src/lib.rs @@ -47,6 +47,9 @@ pub enum ColibriCommand { /// to pass argv without a wrapper script). #[serde(default)] local_args: Option>, + /// Optional FreeBSD jail confinement for the spawned agent. + #[serde(default)] + jail: Option, }, #[serde(rename = "kill-agent")] KillAgent { agent_id: String }, diff --git a/crates/colibri-daemon/src/socket.rs b/crates/colibri-daemon/src/socket.rs index aad18c3..423cb5b 100644 --- a/crates/colibri-daemon/src/socket.rs +++ b/crates/colibri-daemon/src/socket.rs @@ -19,7 +19,7 @@ use tokio::select; use tokio::sync::broadcast; use tracing::{debug, error, info, trace, warn}; -use crate::spawner::{AgentSpawnConfig, Provider, Spawner}; +use crate::spawner::{AgentSpawnConfig, JailConfig, Provider, Spawner}; use crate::{ColibriCommand, ColibriResponse, SharedState}; // --------------------------------------------------------------------------- @@ -172,6 +172,7 @@ async fn dispatch(cmd: ColibriCommand, state: &SharedState) -> ColibriResponse { session_id, system_prompt, local_args, + jail, } => { cmd_spawn_agent( state, @@ -180,6 +181,7 @@ async fn dispatch(cmd: ColibriCommand, state: &SharedState) -> ColibriResponse { session_id, system_prompt, local_args, + jail, ) .await } @@ -331,6 +333,7 @@ async fn cmd_spawn_agent( session_id: Option, system_prompt: Option, local_args: Option>, + jail: Option, ) -> ColibriResponse { let provider = match provider_str.to_lowercase().as_str() { "deepseek" => Provider::DeepSeek, @@ -360,6 +363,7 @@ async fn cmd_spawn_agent( model, session_id: session_id.clone(), system_prompt, + jail, ..Default::default() }; diff --git a/docs/COLIBRI-JAILED-AGENT-SPAWN-DESIGN.md b/docs/COLIBRI-JAILED-AGENT-SPAWN-DESIGN.md index b7388f6..871cb44 100644 --- a/docs/COLIBRI-JAILED-AGENT-SPAWN-DESIGN.md +++ b/docs/COLIBRI-JAILED-AGENT-SPAWN-DESIGN.md @@ -122,9 +122,9 @@ there is no unprivileged path. But `colibri_daemon` runs as the unprivileged cross that line — and we pick **per deployment context**, matching the live-vs-deployed split. -The deciding fact: the ISO's mac*do rules are **identity** mappings, not command +The deciding fact: the ISO's `mac_do` rules are **identity** mappings, not command filters — `security.mac.do.rules=gid=0>uid=0` (clawdie-iso `build.sh:1274`) means -"wheel may become root." mac_do **cannot** restrict \_which* command runs as root. +"wheel may become root." `mac_do` **cannot** restrict _which_ command runs as root. | | `mdo -u root` | setuid/Capsicum helper | | ------------------------------------- | ------------------------ | ---------------------- | @@ -134,10 +134,10 @@ filters — `security.mac.do.rules=gid=0>uid=0` (clawdie-iso `build.sh:1274`) me | Root blast radius if daemon is popped | **full root** | **just jexec-pi** | | Extra setup | one mac_do rule | helper + install | -Because mac*do is command-blind, **wrapping mdo in a helper does NOT narrow it**: +Because `mac_do` is command-blind, **wrapping mdo in a helper does NOT narrow it**: once `colibri` may `mdo -u root`, a compromise just runs `mdo -u root sh`. The helper is hygiene, not a boundary. Only a setuid/Capsicum helper (where colibri -is \_not* granted general root) is a true boundary. +is _not_ granted general root) is a true boundary. ### Decision