kkernel 0.9.0

khive kernel — stdio MCP server binary and admin CLI (sync, pack introspection, db ops)
Documentation
#![cfg(unix)]

use khive_runtime::daemon::{
    read_frame, write_frame, DaemonRequestFrame, DaemonResponseFrame, PROTOCOL_VERSION,
};
use serde_json::{json, Value};
use std::time::Duration;
use tempfile::TempDir;
use tokio::net::UnixListener;
use tokio::process::Command;

const DISCLOSURE: &str = "execution: answered by daemon; --log and KHIVE_LOG set the client process log level only; the daemon log level is fixed at startup";
const WATCHDOG: Duration = Duration::from_secs(20);

fn fixture() -> TempDir {
    tempfile::Builder::new()
        .prefix("exec-log-")
        .tempdir_in("/tmp")
        .expect("short isolated socket directory")
}

fn exec_command(root: &TempDir) -> Command {
    let config = root.path().join("khive.toml");
    std::fs::write(&config, "[packs.kg]\nbackend = \"main\"\nno_embed = true\n").unwrap();
    let mut command = Command::new(env!("CARGO_BIN_EXE_kkernel"));
    for (name, _) in std::env::vars_os() {
        if name.to_string_lossy().starts_with("KHIVE_") {
            command.env_remove(name);
        }
    }
    command
        .args(["exec", "whoami()", "--actor", "actor:exec-log"])
        .arg("--config")
        .arg(config)
        .arg("--db")
        .arg(root.path().join("fixture.db"))
        .current_dir(root.path())
        .env("HOME", root.path())
        .env("KHIVE_PACKS", "kg")
        .env("KHIVE_EVENTS_SPLIT", "0")
        .env("KHIVE_SOCKET", root.path().join("s"))
        .env("KHIVE_PID", root.path().join("p"))
        .env("KHIVE_LOCK", root.path().join("l"))
        .kill_on_drop(true);
    command
}

#[tokio::test]
async fn forwarded_exec_discloses_daemon_logging_scope() {
    let root = fixture();
    let listener = UnixListener::bind(root.path().join("s")).unwrap();
    let daemon = async {
        let (mut stream, _) = listener.accept().await.unwrap();
        let bytes = read_frame(&mut stream).await.unwrap();
        let frame: DaemonRequestFrame = serde_json::from_slice(&bytes).unwrap();
        assert_eq!(frame.ops, "whoami()");
        assert_eq!(frame.actor_id.as_deref(), Some("actor:exec-log"));
        let response = DaemonResponseFrame {
            ok: true,
            result: Some(json!({"daemon_disclosure_fixture": true}).to_string()),
            error: None,
            error_detail: None,
            namespace_mismatch: false,
            config_mismatch: false,
            served_config_id: Some(frame.config_id),
            version_mismatch: false,
            daemon_protocol_version: PROTOCOL_VERSION,
            metrics: None,
            request_id: frame.request_id,
        };
        write_frame(&mut stream, &serde_json::to_vec(&response).unwrap())
            .await
            .unwrap();
    };
    let mut command = exec_command(&root);
    let (served, output) = tokio::join!(tokio::time::timeout(WATCHDOG, daemon), async {
        let output = tokio::time::timeout(WATCHDOG, command.output())
            .await
            .expect("exec must finish")
            .expect("run exec");
        let stderr = String::from_utf8_lossy(&output.stderr);
        assert!(output.status.success(), "{stderr}");
        output
    },);
    served.expect("client must reach the fake daemon");
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert_eq!(
        serde_json::from_slice::<Value>(&output.stdout).unwrap(),
        json!({"daemon_disclosure_fixture": true}),
        "disclosure must not replace or decorate the daemon's stdout payload"
    );
    assert_eq!(
        stderr
            .lines()
            .filter(|line| line.starts_with("execution:"))
            .collect::<Vec<_>>(),
        vec![DISCLOSURE],
        "stderr={stderr}"
    );
}

#[tokio::test]
async fn in_process_exec_omits_daemon_logging_disclosure() {
    let root = fixture();
    let output = tokio::time::timeout(
        WATCHDOG,
        exec_command(&root).env("KHIVE_NO_DAEMON", "1").output(),
    )
    .await
    .expect("local exec must finish")
    .expect("run local exec");
    let stderr = String::from_utf8_lossy(&output.stderr);
    assert!(output.status.success(), "{stderr}");
    let response: Value = serde_json::from_slice(&output.stdout).unwrap();
    assert_eq!(response["results"][0]["ok"], true);
    assert_eq!(
        response["results"][0]["result"]["actor_id"],
        "actor:exec-log"
    );
    assert!(!stderr.contains("answered by daemon"), "{stderr}");
    assert!(
        !stderr.contains("client process log level only"),
        "{stderr}"
    );
}