chio-kernel 0.1.0

Chio runtime kernel: capability validation, guard evaluation, receipt signing
Documentation
// Phase 20.3 cross-kernel federation bilateral co-signing tests.
//
// Included by `src/kernel/tests.rs`; shares helpers (`make_config`,
// `make_keypair`, `make_scope`, `make_grant`, `make_capability`,
// `make_request_with_arguments`, `EchoServer`) with the sibling
// test files.
//
// Acceptance coverage:
//   * post-sign hook fires on federated requests and persists a
//     DualSignedReceipt that verifies against both pinned peer keys,
//   * non-federated requests still work and leave no dual-signed
//     artifact behind,
//   * missing peer pin fails closed.

use chio_federation::{
    FederationPeer, InProcessCoSigner, KernelTrustExchange, PeerHandshakeEnvelope,
};

fn handshake_and_pin(
    local: &KernelTrustExchange,
    remote_kernel_id: &str,
    remote_keypair: &Keypair,
    now: u64,
) -> FederationPeer {
    let envelope = PeerHandshakeEnvelope::sign(
        remote_kernel_id,
        local.local_kernel_id(),
        "nonce-cosign",
        now,
        remote_keypair,
    )
    .expect("remote envelope signs");
    local
        .accept_envelope(&envelope, remote_kernel_id, now)
        .expect("local accepts envelope and pins peer")
}

#[test]
fn federated_request_produces_dual_signed_receipt_verifiable_by_both_orgs() {
    // Org A holds the origin kernel; Org B hosts the tool.
    let origin_kp = Keypair::generate(); // Org A (origin) kernel key
    let origin_kernel_id = "kernel.org-a";

    // Build the tool-host kernel (Org B) on the test-local keypair.
    let mut kernel = ChioKernel::new(make_config());
    let tool_host_public_key = kernel.config.keypair.public_key();
    let tool_host_kernel_id = "kernel.org-b";
    kernel.set_federation_local_kernel_id(tool_host_kernel_id);

    kernel.register_tool_server(Box::new(EchoServer::new(
        "srv-fed",
        vec!["file_read"],
    )));

    // Pin Org A as a trusted peer on Org B's side. Use wall-clock now so
    // the freshness window stays open when the kernel's post-sign hook
    // queries `current_unix_timestamp()` during evaluation.
    let trust = KernelTrustExchange::new(tool_host_kernel_id, kernel.config.keypair.clone())
        .with_trusted_peer(origin_kernel_id, origin_kp.public_key());
    let now = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);
    let peer = handshake_and_pin(&trust, origin_kernel_id, &origin_kp, now);
    let kernel = kernel.with_federation_peers(vec![peer.clone()]);

    // Install the in-process bilateral cosigner: the test holds Org A's
    // signing key directly so we can exercise the full cryptographic
    // path without an actual mTLS transport.
    let mut kernel = kernel;
    kernel.set_federation_cosigner(std::sync::Arc::new(InProcessCoSigner::new(
        origin_kernel_id,
        origin_kp.clone(),
        tool_host_public_key.clone(),
    )));

    // Build a federated tool call request (agent in Org A calling a tool
    // hosted by Org B).
    let agent_kp = make_keypair();
    let cap = make_capability(
        &kernel,
        &agent_kp,
        make_scope(vec![make_grant("srv-fed", "file_read")]),
        300,
    );
    let mut request = make_request_with_arguments(
        "req-fed-1",
        &cap,
        "file_read",
        "srv-fed",
        serde_json::json!({ "path": "/data/fed.txt" }),
    );
    request.federated_origin_kernel_id = Some(origin_kernel_id.to_string());

    let response = kernel.evaluate_tool_call_blocking(&request).unwrap();
    assert_eq!(response.verdict, Verdict::Allow);

    // The post-sign hook fired and a DualSignedReceipt was stashed.
    let dual = kernel
        .dual_signed_receipt(&response.receipt.id)
        .expect("dual-signed receipt must exist for federated request");
    assert_eq!(dual.org_a_kernel_id, origin_kernel_id);
    assert_eq!(dual.org_b_kernel_id, tool_host_kernel_id);
    assert_eq!(dual.body.id, response.receipt.id);

    // Either org can independently verify the receipt chain.
    dual.verify(&origin_kp.public_key(), &tool_host_public_key)
        .expect("dual-signed receipt must verify against both pinned peer keys");
}

#[test]
fn non_federated_request_leaves_no_dual_signed_artifact_behind() {
    let mut kernel = ChioKernel::new(make_config());
    kernel.register_tool_server(Box::new(EchoServer::new(
        "srv-local",
        vec!["file_read"],
    )));
    // No peers declared; no cosigner installed.
    let agent_kp = make_keypair();
    let cap = make_capability(
        &kernel,
        &agent_kp,
        make_scope(vec![make_grant("srv-local", "file_read")]),
        300,
    );
    let request = make_request_with_arguments(
        "req-local-1",
        &cap,
        "file_read",
        "srv-local",
        serde_json::json!({ "path": "/data/local.txt" }),
    );
    let response = kernel.evaluate_tool_call_blocking(&request).unwrap();
    assert_eq!(response.verdict, Verdict::Allow);
    assert!(kernel.dual_signed_receipt(&response.receipt.id).is_none());
}

#[test]
fn federated_request_without_pinned_peer_fails_closed() {
    let origin_kp = Keypair::generate();
    let origin_kernel_id = "kernel.org-a";

    let mut kernel = ChioKernel::new(make_config());
    kernel.set_federation_local_kernel_id("kernel.org-b");
    kernel.register_tool_server(Box::new(EchoServer::new(
        "srv-fed",
        vec!["file_read"],
    )));
    // Cosigner is installed, but no peer is pinned -- must fail closed.
    kernel.set_federation_cosigner(std::sync::Arc::new(InProcessCoSigner::new(
        origin_kernel_id,
        origin_kp.clone(),
        kernel.config.keypair.public_key(),
    )));

    let agent_kp = make_keypair();
    let cap = make_capability(
        &kernel,
        &agent_kp,
        make_scope(vec![make_grant("srv-fed", "file_read")]),
        300,
    );
    let mut request = make_request_with_arguments(
        "req-fed-missing-peer",
        &cap,
        "file_read",
        "srv-fed",
        serde_json::json!({ "path": "/data/fed.txt" }),
    );
    request.federated_origin_kernel_id = Some(origin_kernel_id.to_string());

    let err = kernel
        .evaluate_tool_call_blocking(&request)
        .expect_err("federated request with no pinned peer must fail closed");
    match err {
        KernelError::Internal(msg) => {
            assert!(
                msg.contains("not pinned") || msg.contains("stale"),
                "unexpected error message: {msg}"
            );
        }
        other => panic!("expected Internal error, got {other:?}"),
    }
}

#[test]
fn federated_request_without_cosigner_fails_closed() {
    let origin_kernel_id = "kernel.org-a";
    let mut kernel = ChioKernel::new(make_config());
    kernel.set_federation_local_kernel_id("kernel.org-b");
    kernel.register_tool_server(Box::new(EchoServer::new(
        "srv-fed",
        vec!["file_read"],
    )));

    let agent_kp = make_keypair();
    let cap = make_capability(
        &kernel,
        &agent_kp,
        make_scope(vec![make_grant("srv-fed", "file_read")]),
        300,
    );
    let mut request = make_request_with_arguments(
        "req-fed-no-cosigner",
        &cap,
        "file_read",
        "srv-fed",
        serde_json::json!({ "path": "/data/fed.txt" }),
    );
    request.federated_origin_kernel_id = Some(origin_kernel_id.to_string());

    let err = kernel
        .evaluate_tool_call_blocking(&request)
        .expect_err("federated request with no cosigner must fail closed");
    match err {
        KernelError::Internal(msg) => {
            assert!(
                msg.contains("federation cosigner missing"),
                "unexpected error message: {msg}"
            );
        }
        other => panic!("expected Internal error, got {other:?}"),
    }
}