use chio_core::capability::features::CapabilityNegotiation;
use chio_federation::{
bilateral::BilateralCoSigningError, bilateral::BilateralCoSigningProtocol, bilateral::CoSigningRequest, bilateral::CoSigningResponse,
trust_establishment::FederationPeer, bilateral::InProcessCoSigner, trust_establishment::KernelTrustExchange, trust_establishment::PeerHandshakeEnvelope,
};
struct CountingRejectingCosigner {
calls: std::sync::Arc<AtomicU64>,
}
impl BilateralCoSigningProtocol for CountingRejectingCosigner {
fn request_cosignature(
&self,
_request: &CoSigningRequest,
) -> Result<CoSigningResponse, BilateralCoSigningError> {
self.calls.fetch_add(1, Ordering::SeqCst);
Err(BilateralCoSigningError::PeerRejected(
"test cosigner should not be called before local durability".to_string(),
))
}
}
struct TreatyDsseAdmissionHook;
impl TreatyDsseAdmissionHook {
fn federation_treaty_dsse(request_sha256: &str) -> serde_json::Value {
let capability_lease_ref = chio_federation::bilateral_dsse::CapabilityLeaseRef {
lease_id: "lease-bilateral".to_string(),
issuer: "kernel.org-a".to_string(),
expires_at_unix_ms: 4_102_444_800_000,
scope_digest: None,
};
let policy_evaluation_summary = chio_federation::bilateral_dsse::PolicyEvaluationSummary {
server_a_verdict: chio_federation::bilateral_dsse::PolicyVerdict {
verdict: "allow".to_string(),
policy_id: "policy-a".to_string(),
policy_version: "v1".to_string(),
rationale_code: None,
},
server_b_verdict: chio_federation::bilateral_dsse::PolicyVerdict {
verdict: "allow".to_string(),
policy_id: "policy-b".to_string(),
policy_version: "v1".to_string(),
rationale_code: None,
},
joint_disposition: Some("allow".to_string()),
};
let treaty_binding_ref = chio_federation::bilateral_dsse::TreatyBindingRef {
treaty_id: "treaty-buyer-vendor".to_string(),
treaty_scope_sha256: "1".repeat(64),
ladder_intersection_sha256: "2".repeat(64),
admission_report_sha256: "3".repeat(64),
continuation_sha256: "4".repeat(64),
lineage_bundle_sha256: "5".repeat(64),
action_class_id: "workflow.destructive.vendor_call".to_string(),
consistency_model: "totally_ordered".to_string(),
request_sha256: request_sha256.to_string(),
outcome_sha256: "6".repeat(64),
local_receipt_sha256: "7".repeat(64),
remote_receipt_sha256: "8".repeat(64),
lease_refs: vec!["lease-bilateral".to_string()],
governance_refs: Vec::new(),
signer_kernel_ids: vec!["kernel.org-a".to_string(), "kernel.org-b".to_string()],
};
serde_json::json!({
"capability_lease_ref": capability_lease_ref,
"policy_evaluation_summary": policy_evaluation_summary,
"consistency_anchor": "anchor-live",
"consistency_model": "totally_ordered",
"cross_org_visibility": "treaty_only",
"treaty_binding_ref": treaty_binding_ref,
})
}
}
impl RuntimeAdmissionHook for TreatyDsseAdmissionHook {
fn name(&self) -> &str {
"test-federation-treaty-dsse-admission"
}
fn evaluate(
&self,
context: &RuntimeAdmissionContext<'_>,
) -> Result<RuntimeAdmissionDecision, KernelError> {
let action = ToolCallAction::from_parameters(context.request.arguments.clone())
.map_err(|e| KernelError::Internal(format!("failed to hash request arguments: {e}")))?;
Ok(RuntimeAdmissionDecision::allow(Some(serde_json::json!({
"chio_runtime": {
"admission_id": context.request.request_id,
"accepted": true,
"failure_code": null,
"federation_treaty_dsse": Self::federation_treaty_dsse(&action.parameter_hash),
}
}))))
}
}
struct FailingAppendReceiptStore {
called: std::sync::Arc<AtomicBool>,
}
impl ReceiptStore for FailingAppendReceiptStore {
fn append_chio_receipt(&self, _receipt: &ChioReceipt) -> Result<(), ReceiptStoreError> {
self.called.store(true, Ordering::SeqCst);
Err(ReceiptStoreError::Conflict(
"receipt append failed".to_string(),
))
}
fn append_child_receipt(
&self,
_receipt: &ChildRequestReceipt,
) -> Result<(), ReceiptStoreError> {
Ok(())
}
}
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 = make_kernel(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);
let path = unique_receipt_db_path("federated-dual-signed-receipt");
kernel.set_receipt_store(Box::new(SqliteReceiptStore::open(&path).unwrap())).unwrap();
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(),
)));
kernel.set_runtime_admission_hook(std::sync::Arc::new(TreatyDsseAdmissionHook));
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");
let envelope = kernel
.federation_dsse_envelope(&response.receipt.id)
.expect("DSSE envelope must exist for federated request");
let statement = chio_federation::bilateral_dsse::verify_chio_bilateral_dsse_envelope(
&envelope,
&origin_kp.public_key(),
&tool_host_public_key,
)
.expect("treaty-bound DSSE envelope must verify against both pinned peer keys");
let treaty = statement
.predicate
.treaty_binding_ref
.expect("treaty-bound DSSE envelope must carry a treaty binding ref");
assert_eq!(treaty.request_sha256, response.receipt.action.parameter_hash);
assert_eq!(treaty.outcome_sha256, response.receipt.content_hash);
assert_eq!(
treaty.signer_kernel_ids,
vec![origin_kernel_id.to_string(), tool_host_kernel_id.to_string()]
);
}
#[test]
fn federation_cosigner_not_called_when_local_persistence_fails() {
let origin_kp = Keypair::generate();
let origin_kernel_id = "kernel.org-a";
let tool_host_kernel_id = "kernel.org-b";
let mut kernel = make_kernel(make_config());
kernel.set_federation_local_kernel_id(tool_host_kernel_id);
let receipt_append_called = std::sync::Arc::new(AtomicBool::new(false));
kernel.set_receipt_store(Box::new(FailingAppendReceiptStore {
called: std::sync::Arc::clone(&receipt_append_called),
})).unwrap();
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 mut peer = handshake_and_pin(&trust, origin_kernel_id, &origin_kp, now);
peer.capabilities = CapabilityNegotiation::t1_default();
let kernel = kernel.with_federation_peers(vec![peer]);
let mut kernel = kernel;
let cosigner_calls = std::sync::Arc::new(AtomicU64::new(0));
kernel.set_federation_cosigner(std::sync::Arc::new(CountingRejectingCosigner {
calls: std::sync::Arc::clone(&cosigner_calls),
}));
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-store-fails",
&cap,
"file_read",
"srv-fed",
serde_json::json!({ "path": "/data/fed.txt" }),
);
request.federated_origin_kernel_id = Some(origin_kernel_id.to_string());
let receipt = make_signed_receipt(&kernel.config.keypair, "rcpt-fed-store-fails");
let err = kernel
.record_chio_receipt_with_federation(&request, &receipt)
.expect_err("local persistence failure must abort before federation cosign");
assert!(
format!("{err}").contains("receipt append failed"),
"unexpected error: {err}"
);
assert_eq!(
cosigner_calls.load(Ordering::SeqCst),
0,
"cosigner must not be called before durable local receipt state exists"
);
assert!(
receipt_append_called.load(Ordering::SeqCst),
"receipt append must be attempted before federation cosign"
);
}
#[test]
fn federated_request_without_receipt_store_denies_before_dispatch_or_cosign() {
let origin_kp = Keypair::generate();
let origin_kernel_id = "kernel.org-a";
let tool_host_kernel_id = "kernel.org-b";
let mut kernel = make_kernel(make_config());
kernel.set_federation_local_kernel_id(tool_host_kernel_id);
let invocations = std::sync::Arc::new(AtomicU64::new(0));
kernel.register_tool_server(Box::new(SideEffectServer::new(
"srv-fed",
vec!["file_read"],
std::sync::Arc::clone(&invocations),
)));
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 mut peer = handshake_and_pin(&trust, origin_kernel_id, &origin_kp, now);
peer.capabilities = CapabilityNegotiation::v1_default();
let kernel = kernel.with_federation_peers(vec![peer]);
let mut kernel = kernel;
let cosigner_calls = std::sync::Arc::new(AtomicU64::new(0));
kernel.set_federation_cosigner(std::sync::Arc::new(CountingRejectingCosigner {
calls: std::sync::Arc::clone(&cosigner_calls),
}));
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-v1-no-store",
&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)
.expect("missing federated receipt persistence must produce a signed deny response");
assert_eq!(response.verdict, Verdict::Deny);
let reason = response.reason.unwrap_or_default();
assert!(
reason.contains("receipt persistence") && reason.contains("durable"),
"unexpected deny reason: {reason}"
);
assert_eq!(
invocations.load(Ordering::SeqCst),
0,
"tool must not run without durable federated receipt persistence"
);
assert_eq!(
cosigner_calls.load(Ordering::SeqCst),
0,
"cosigner must not run before durable local receipt state exists"
);
assert!(
kernel.dual_signed_receipt(&response.receipt.id).is_none(),
"dual-signed receipt must not be produced for a pre-dispatch denial"
);
assert!(
kernel.federation_dsse_envelope(&response.receipt.id).is_none(),
"DSSE envelope must not be produced for a pre-dispatch denial"
);
}
#[test]
fn non_federated_kernel_without_receipt_store_fails_closed_unless_ephemeral_enabled() {
let mut config = make_config();
config.allow_ephemeral_receipt_log = false;
let mut kernel = make_kernel(config);
let invocations = std::sync::Arc::new(AtomicU64::new(0));
kernel.register_tool_server(Box::new(SideEffectServer::new(
"srv-local",
vec!["file_read"],
std::sync::Arc::clone(&invocations),
)));
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-no-store-strict",
&cap,
"file_read",
"srv-local",
serde_json::json!({ "path": "/data/local.txt" }),
);
let response = kernel
.evaluate_tool_call_blocking(&request)
.expect("missing receipt persistence must produce a signed deny response");
assert_eq!(response.verdict, Verdict::Deny);
let reason = response.reason.unwrap_or_default();
assert!(
reason.contains("receipt persistence") && reason.contains("durable"),
"unexpected deny reason: {reason}"
);
assert_eq!(
invocations.load(Ordering::SeqCst),
0,
"tool must not run without durable receipt persistence when ephemeral logging is disabled"
);
let mut ephemeral_config = make_config();
ephemeral_config.allow_ephemeral_receipt_log = true;
let mut ephemeral_kernel = make_kernel(ephemeral_config);
let ephemeral_invocations = std::sync::Arc::new(AtomicU64::new(0));
ephemeral_kernel.register_tool_server(Box::new(SideEffectServer::new(
"srv-local",
vec!["file_read"],
std::sync::Arc::clone(&ephemeral_invocations),
)));
let ephemeral_agent_kp = make_keypair();
let ephemeral_cap = make_capability(
&ephemeral_kernel,
&ephemeral_agent_kp,
make_scope(vec![make_grant("srv-local", "file_read")]),
300,
);
let ephemeral_request = make_request_with_arguments(
"req-local-no-store-ephemeral",
&ephemeral_cap,
"file_read",
"srv-local",
serde_json::json!({ "path": "/data/local.txt" }),
);
let ephemeral_response = ephemeral_kernel
.evaluate_tool_call_blocking(&ephemeral_request)
.expect("ephemeral receipt logging must permit dispatch without a receipt store");
assert_eq!(ephemeral_response.verdict, Verdict::Allow);
assert_eq!(
ephemeral_invocations.load(Ordering::SeqCst),
1,
"tool must be invoked exactly once when ephemeral receipt logging is permitted"
);
}
#[test]
fn non_federated_request_leaves_no_dual_signed_artifact_behind() {
let mut kernel = make_kernel(make_config());
let path = unique_receipt_db_path("non-federated-no-dual-signed");
kernel.set_receipt_store(Box::new(SqliteReceiptStore::open(&path).unwrap())).unwrap();
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());
assert!(kernel.federation_dsse_envelope(&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 = make_kernel(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 response = kernel
.evaluate_tool_call_blocking(&request)
.expect("federated request with no pinned peer must produce a Deny response");
assert_eq!(response.verdict, Verdict::Deny);
let reason = response.reason.unwrap_or_default();
assert!(
reason.contains("not pinned") || reason.contains("stale") || reason.contains("downgrade"),
"unexpected deny reason: {reason}"
);
}
#[test]
fn federated_request_without_pinned_peer_fails_closed_pre_dispatch() {
let origin_kernel_id = "kernel.org-a";
let mut kernel = make_kernel(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-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 response = kernel
.evaluate_tool_call_blocking(&request)
.expect("federated request with no pinned peer must produce a Deny response");
assert_eq!(response.verdict, Verdict::Deny);
let reason = response.reason.unwrap_or_default();
assert!(
reason.contains("not pinned")
|| reason.contains("stale")
|| reason.contains("downgrade"),
"unexpected deny reason: {reason}"
);
}
#[test]
fn federated_request_with_fresh_peer_but_missing_cosigner_fails_closed_post_dispatch() {
let origin_kp = Keypair::generate();
let origin_kernel_id = "kernel.org-a";
let tool_host_kernel_id = "kernel.org-b";
let mut kernel = make_kernel(make_config());
kernel.set_federation_local_kernel_id(tool_host_kernel_id);
let path = unique_receipt_db_path("federated-missing-cosigner");
kernel.set_receipt_store(Box::new(SqliteReceiptStore::open(&path).unwrap())).unwrap();
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 mut kernel = kernel.with_federation_peers(vec![peer]);
kernel.set_runtime_admission_hook(std::sync::Arc::new(TreatyDsseAdmissionHook));
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 result = kernel.evaluate_tool_call_blocking(&request);
let (verdict, reason) = match result {
Ok(resp) => (resp.verdict, resp.reason.unwrap_or_default()),
Err(err) => (Verdict::Deny, err.to_string()),
};
assert_eq!(verdict, Verdict::Deny);
assert!(
reason.contains("federation cosigner missing")
|| reason.contains("cosigner")
|| reason.contains("federation")
|| reason.contains("treaty-bound")
|| reason.contains("admission context"),
"unexpected deny reason for missing-cosigner-with-fresh-peer scenario: {reason}"
);
}
#[test]
fn installed_artifact_store_preserves_cosign_evidence_across_cache_eviction() {
let origin_kp = Keypair::generate();
let origin_kernel_id = "kernel.org-a";
let mut config = make_config();
config.memory_budget.federation_cache_capacity = 1;
let mut kernel = make_kernel(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);
let path = unique_receipt_db_path("federation-artifact-store-eviction");
kernel
.set_receipt_store(Box::new(SqliteReceiptStore::open(&path).unwrap()))
.unwrap();
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 mut kernel = kernel.with_federation_peers(vec![peer]);
kernel.set_federation_cosigner(std::sync::Arc::new(InProcessCoSigner::new(
origin_kernel_id,
origin_kp.clone(),
tool_host_public_key.clone(),
)));
kernel.set_runtime_admission_hook(std::sync::Arc::new(TreatyDsseAdmissionHook));
kernel.set_federation_artifact_store(std::sync::Arc::new(
crate::federation_artifact_store::InMemoryFederationArtifactStore::default(),
));
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_a = make_request_with_arguments(
"req-fed-evict-a",
&cap,
"file_read",
"srv-fed",
serde_json::json!({ "path": "/data/fed-a.txt" }),
);
request_a.federated_origin_kernel_id = Some(origin_kernel_id.to_string());
let response_a = kernel.evaluate_tool_call_blocking(&request_a).unwrap();
assert_eq!(response_a.verdict, Verdict::Allow);
let receipt_a_id = response_a.receipt.id.clone();
let mut request_b = make_request_with_arguments(
"req-fed-evict-b",
&cap,
"file_read",
"srv-fed",
serde_json::json!({ "path": "/data/fed-b.txt" }),
);
request_b.federated_origin_kernel_id = Some(origin_kernel_id.to_string());
let response_b = kernel.evaluate_tool_call_blocking(&request_b).unwrap();
assert_eq!(response_b.verdict, Verdict::Allow);
let receipt_b_id = response_b.receipt.id.clone();
assert_ne!(receipt_a_id, receipt_b_id);
let dual_gauge = kernel
.bounded_structure_gauges()
.into_iter()
.find(|(label, _)| *label == "federation_dual_receipts")
.map(|(_, count)| count)
.expect("federation_dual_receipts gauge must exist");
assert_eq!(dual_gauge, 1, "cap-1 cache must hold only the newest artifact");
let dual_a = kernel
.dual_signed_receipt(&receipt_a_id)
.expect("evicted dual-signed receipt must resolve from the artifact store");
assert_eq!(dual_a.body.id, receipt_a_id);
dual_a
.verify(&origin_kp.public_key(), &tool_host_public_key)
.expect("store-served dual-signed receipt must still verify against both peers");
assert!(
kernel.federation_dsse_envelope(&receipt_a_id).is_some(),
"evicted DSSE envelope must resolve from the artifact store"
);
assert!(kernel.dual_signed_receipt(&receipt_b_id).is_some());
let _ = std::fs::remove_file(path);
}
#[test]
fn bounded_in_memory_artifact_store_loses_evidence_when_both_layers_evict() {
let origin_kp = Keypair::generate();
let origin_kernel_id = "kernel.org-a";
let mut config = make_config();
config.memory_budget.federation_cache_capacity = 1;
let mut kernel = make_kernel(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);
let path = unique_receipt_db_path("federation-artifact-store-bounded-loss");
kernel
.set_receipt_store(Box::new(SqliteReceiptStore::open(&path).unwrap()))
.unwrap();
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 mut kernel = kernel.with_federation_peers(vec![peer]);
kernel.set_federation_cosigner(std::sync::Arc::new(InProcessCoSigner::new(
origin_kernel_id,
origin_kp.clone(),
tool_host_public_key.clone(),
)));
kernel.set_runtime_admission_hook(std::sync::Arc::new(TreatyDsseAdmissionHook));
kernel.set_federation_artifact_store(std::sync::Arc::new(
crate::federation_artifact_store::InMemoryFederationArtifactStore::with_capacity(1, 3600),
));
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_a = make_request_with_arguments(
"req-fed-bounded-a",
&cap,
"file_read",
"srv-fed",
serde_json::json!({ "path": "/data/fed-a.txt" }),
);
request_a.federated_origin_kernel_id = Some(origin_kernel_id.to_string());
let response_a = kernel.evaluate_tool_call_blocking(&request_a).unwrap();
assert_eq!(response_a.verdict, Verdict::Allow);
let receipt_a_id = response_a.receipt.id.clone();
let mut request_b = make_request_with_arguments(
"req-fed-bounded-b",
&cap,
"file_read",
"srv-fed",
serde_json::json!({ "path": "/data/fed-b.txt" }),
);
request_b.federated_origin_kernel_id = Some(origin_kernel_id.to_string());
let response_b = kernel.evaluate_tool_call_blocking(&request_b).unwrap();
assert_eq!(response_b.verdict, Verdict::Allow);
let receipt_b_id = response_b.receipt.id.clone();
assert_ne!(receipt_a_id, receipt_b_id);
assert!(
kernel.dual_signed_receipt(&receipt_a_id).is_none(),
"a bounded in-memory store must not be relied on to durably retain evicted artifacts"
);
assert!(
kernel.federation_dsse_envelope(&receipt_a_id).is_none(),
"a bounded in-memory store must not be relied on to durably retain evicted envelopes"
);
assert!(kernel.dual_signed_receipt(&receipt_b_id).is_some());
let _ = std::fs::remove_file(path);
}