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() {
let origin_kp = Keypair::generate(); let origin_kernel_id = "kernel.org-a";
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"],
)));
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()]);
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(),
)));
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);
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);
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"],
)));
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"],
)));
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:?}"),
}
}