jcp-wire: a caller-signed challenge, and endpoint evidence bound to the answer #394
Merged
dcharlot
merged 1 commit from 2026-10-21 12:41:10 -04:00
feat/jcp-wire-evidence into main 2026-10-21
jcp-wire: a caller-signed challenge, and endpoint evidence bound to the answer
All checks were successful
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 48s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 5s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m15s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m38s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m12s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m17s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 2m53s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 1m1s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 4m36s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 9m2s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 2m53s
Sweep 2026-10-19 item #5, wire side. A WireRequest had no challenge slot and a WireResponse carried no endpoint evidence. Freshness came only from the caller's `now` stamp, so a caller had no way to ask the platform an endpoint runs on to vouch for an answer. jcp-wire (SPEC §11): - WireRequest.challenge: 32 caller-chosen bytes, covered by the caller's signature (challenged_signable_bytes, sign_challenged). It is omitted when absent, so a request without one keeps its bytes and its signature. - WireResponse.evidence { kind, report }, omitted when absent. An endpoint configured with_attester answers an authenticated challenged request with a report over evidence_report_data = BLAKE3-XOF-64(derive_key "jcp/1 wire evidence v1", challenge ‖ BLAKE3(receipt)). The challenge makes it the caller's; the receipt makes it this answer's. - No evidence for a request denied bad-signature, so a forgery cannot make the platform produce reports on demand. A repeat gets its first evidence, and a restart rebuilds it through respond(), so the attester is configured before with_journal. - WireResponse::verify_evidence checks against the challenge the caller SENT, never one read back, through a caller-supplied EvidenceVerifier. jcp-wire stays free of any TEE crate, and a transport's own evidence (an exported authenticator on TLS) is a profile of the same slot. SPEC §11: message shapes, the evidence binding and rules, and conformance class C28 with a C28 check. Tests: 4 new, plus C28. A challenged request gets evidence bound to its answer; evidence comes only where asked and able, with an unchallenged request byte-identical and signed over the old bytes; the challenge is signed, so swapping or stripping it is refused with no evidence; a repeat gets its first evidence and a restart rebuilds it. Mutation testing: 9 mutants, all killed. Verified on the build host: jcp-rs 579 tests (--locked) and every conformance class; clippy -D warnings (jcp-wire, -conformance, -threshold) and rustdoc -D warnings (jcp-wire) clean. Repo checks pass, including lockfiles. Co-Authored-By: Claude Opus 5.5 <[email protected]>