open-standards/joule-code/joule-code-rs/crates/joule-code-release/src/lib.rs
Transaction Science 9eec1a3e35 joule-code-release: expose audit() for keyless ship-readiness checks
A consumer (an editor surfacing whether a release would certify) needs to
ask 'which gate blocks?' without holding the signing key. audit() is now
public; audit().is_clear() is exactly certify()'s success condition.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-10 23:54:52 -04:00

471 lines
19 KiB
Rust

//! The **release certificate**: the single, total door to production.
//!
//! Mature engineering disciplines do not have *unaccounted* failure as an
//! option. A bridge does not ship because it "looks fine"; it ships because
//! its load path is proven, its margin is stamped, and the artifact carries
//! the evidence. Software has always kept a silent-ship lane open — "looks
//! right, push it." This crate closes it.
//!
//! The family already produces four independent verdicts over a unit of work:
//!
//! - **spec coverage** (`joule-code-spec::Coverage`) — every requirement and
//! constraint is implemented, no non-goal is cited;
//! - **contract discharge** (`joulecontract-proof`) — every declared
//! obligation is machine-proved;
//! - **two-stage review** (`joule-code-review::Approval`) — spec-compliance
//! and quality both approved by independent reviewers;
//! - **attested build** (`sandbox`) — the unit ran, isolated and metered, and
//! its build receipt verified.
//!
//! Separately, each is a gate an integrator *may* run. The lane between them
//! is the failure: nothing forces all four, so an artifact can reach
//! production having passed three and skipped one. The [`ReleaseCertificate`]
//! is the one object that makes the gate **total** — it certifies a release
//! **iff** all four gates are present and satisfied, and the only other
//! terminal state is a [`Refusal`] that names exactly which gates are missing
//! or failed. There is no third lane: no warning, no "mostly", no override.
//!
//! ## The irreducible residue, named on purpose
//!
//! No system eliminates the spec↔intent gap — you can prove the code meets
//! the spec, never that the spec is what was meant. That gap is not papered
//! over here: the certificate binds to the exact `spec` it was released
//! against (by content address) and to the spec-coverage gate, so the one
//! place a human must look — *is this the right spec?* — is named, signed,
//! and isolated. Everything else carries a proof.
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use joule_code_review::Approval;
use joule_code_schema::ContentId;
use joule_code_spec::Coverage;
use serde::{Deserialize, Serialize};
/// Signature algorithm id for Ed25519 (matches `joule-code-schema`).
pub const ALG_ED25519: u8 = 0x01;
/// The four mandatory gates. A release names exactly these, each once.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Gate {
/// Every requirement and constraint of the spec is implemented; no
/// non-goal is cited; no citation is invalid (`Coverage::is_complete`).
SpecCoverage,
/// Every declared contract obligation is machine-proved
/// (`ContractDischarge::discharged`).
ContractDischarge,
/// Two-stage review approved by independent reviewers
/// (`Approval::is_approved`).
Review,
/// The unit ran isolated + metered and its build receipt verified.
AttestedBuild,
}
impl Gate {
/// The canonical set, in order. A release must cover exactly this set.
pub fn all() -> [Gate; 4] {
[Gate::SpecCoverage, Gate::ContractDischarge, Gate::Review, Gate::AttestedBuild]
}
/// A stable human label.
pub fn label(self) -> &'static str {
match self {
Gate::SpecCoverage => "spec-coverage",
Gate::ContractDischarge => "contract-discharge",
Gate::Review => "review",
Gate::AttestedBuild => "attested-build",
}
}
}
/// One gate's verdict: whether it passed, and the content address of the
/// underlying artifact (in that standard's own scheme) so an auditor can
/// re-derive and re-check it with that standard's verifier.
///
/// `evidence` is an opaque address string because the gates span standards
/// with different hash families (joule-code uses BLAKE3; joulecontract uses
/// SHA-256). The certificate binds to each evidence string; it does not
/// re-hash across schemes.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GateOutcome {
/// Which gate this is.
pub gate: Gate,
/// Whether the gate is satisfied. For the same-repo gates this is
/// *derived* from the real verdict type (see [`GateOutcome::from_coverage`]
/// / [`GateOutcome::from_approval`]) — it cannot be asserted. For the
/// cross-standard gates the caller supplies the verdict from that
/// standard's own check, bound to `evidence`.
pub satisfied: bool,
/// The underlying artifact's content address in its native scheme.
pub evidence: String,
}
impl GateOutcome {
/// Derive the spec-coverage outcome from a real [`Coverage`]. `satisfied`
/// is `coverage.is_complete()` — it cannot be claimed without the verdict.
pub fn from_coverage(coverage: &Coverage, spec_id: impl Into<String>) -> Self {
Self {
gate: Gate::SpecCoverage,
satisfied: coverage.is_complete(),
evidence: spec_id.into(),
}
}
/// Derive the review outcome from a real [`Approval`]. `satisfied` is
/// `approval.is_approved()`; `evidence` is the reviewed op's id.
pub fn from_approval(approval: &Approval, op_id: impl Into<String>) -> Self {
Self {
gate: Gate::Review,
satisfied: approval.is_approved(),
evidence: op_id.into(),
}
}
/// The contract-discharge outcome. `discharged` MUST come from
/// `joulecontract_proof::ContractDischarge::discharged()`; `evidence` is
/// the contract's content address. (Kept as a verdict+evidence pair rather
/// than a typed dependency because that standard hashes in a different
/// scheme — the certificate binds the address, not the type.)
pub fn contract(discharged: bool, contract_addr: impl Into<String>) -> Self {
Self { gate: Gate::ContractDischarge, satisfied: discharged, evidence: contract_addr.into() }
}
/// The attested-build outcome. `verified` MUST come from a re-verified
/// sandbox build receipt; `evidence` is the build id.
pub fn attested_build(verified: bool, build_id: impl Into<String>) -> Self {
Self { gate: Gate::AttestedBuild, satisfied: verified, evidence: build_id.into() }
}
}
/// A request to certify a release: what is being released, the spec it is
/// released against, and the four gate outcomes.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseRequest {
/// Signature algorithm id (`0x01` = Ed25519).
pub alg: u8,
/// Human label for the unit of work.
pub subject: String,
/// The released artifact's content address (e.g. the final build id or
/// op id) — the thing the certificate vouches for.
pub artifact: String,
/// The spec content address this release is judged against — the one
/// place a human's "is this the right spec?" lives.
pub spec: String,
/// The gate outcomes. Must cover exactly [`Gate::all`], each once.
pub outcomes: Vec<GateOutcome>,
/// The certifier's Ed25519 verifying key.
pub certifier: [u8; 32],
}
impl ReleaseRequest {
/// The release id: content address of the canonical request.
pub fn release_id(&self) -> Result<ContentId, ReleaseError> {
Ok(ContentId::of_canonical(self)?)
}
/// Partition the outcomes against the mandatory set: which gates are
/// missing, which are present-but-failed, and which appear more than once
/// (or are otherwise not in the canonical set). All three are grounds for
/// refusal — the gate is total, so a duplicate review cannot stand in for
/// a missing build.
///
/// Public so a consumer (e.g. an editor showing ship-readiness) can ask
/// "would this certify, and if not which gate blocks?" *without* holding
/// the signing key. `audit().is_clear()` is exactly the condition under
/// which [`certify`] would succeed.
pub fn audit(&self) -> Refusal {
let mut missing = Vec::new();
let mut failed = Vec::new();
let mut duplicated = Vec::new();
for gate in Gate::all() {
let hits: Vec<&GateOutcome> = self.outcomes.iter().filter(|o| o.gate == gate).collect();
match hits.len() {
0 => missing.push(gate),
1 => {
if !hits[0].satisfied {
failed.push(gate);
}
}
_ => duplicated.push(gate),
}
}
Refusal { missing, failed, duplicated }
}
}
/// The refusal terminal state: the release does NOT exist, and exactly why.
/// Empty vectors mean the request is certifiable.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Refusal {
/// Mandatory gates with no outcome in the request.
pub missing: Vec<Gate>,
/// Gates present but not satisfied.
pub failed: Vec<Gate>,
/// Gates appearing more than once (a gate may not be doubled to mask a
/// missing one).
pub duplicated: Vec<Gate>,
}
impl Refusal {
/// Whether this audit clears (nothing missing, failed, or duplicated).
pub fn is_clear(&self) -> bool {
self.missing.is_empty() && self.failed.is_empty() && self.duplicated.is_empty()
}
}
impl std::fmt::Display for Refusal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let names = |gs: &[Gate]| gs.iter().map(|g| g.label()).collect::<Vec<_>>().join(", ");
write!(f, "release refused —")?;
if !self.missing.is_empty() {
write!(f, " missing: [{}]", names(&self.missing))?;
}
if !self.failed.is_empty() {
write!(f, " failed: [{}]", names(&self.failed))?;
}
if !self.duplicated.is_empty() {
write!(f, " duplicated: [{}]", names(&self.duplicated))?;
}
Ok(())
}
}
/// Errors distinct from a refusal: a refusal is a *valid* "no", these are
/// malformed inputs or bad signatures.
#[derive(Debug, thiserror::Error)]
pub enum ReleaseError {
#[error("unsupported algorithm id {0:#04x}")]
BadAlg(u8),
#[error("signature verification failed")]
BadSignature,
#[error("schema: {0}")]
Schema(#[from] joule_code_schema::SchemaError),
}
/// A signed certificate that a release passed every gate. It exists only when
/// the audit cleared — there is no representation of a partial release.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseCertificate {
/// The certified request.
pub request: ReleaseRequest,
/// Ed25519 signature (64 bytes) over `release_id ‖ artifact ‖ spec`.
pub sig: Vec<u8>,
}
impl ReleaseCertificate {
/// Preimage: `release_id ‖ artifact ‖ spec` — the certificate is bound to
/// the exact artifact and the exact spec it was judged against.
fn preimage(request: &ReleaseRequest) -> Result<Vec<u8>, ReleaseError> {
let id = request.release_id()?;
let mut pre = id.as_str().as_bytes().to_vec();
pre.extend_from_slice(request.artifact.as_bytes());
pre.extend_from_slice(request.spec.as_bytes());
Ok(pre)
}
/// Re-verify: signature valid AND the audit still clears. A certificate
/// whose outcomes were tampered to flip a gate fails here even if the
/// signature somehow held — totality is checked, not trusted.
pub fn verify(&self) -> Result<ContentId, ReleaseError> {
if self.request.alg != ALG_ED25519 {
return Err(ReleaseError::BadAlg(self.request.alg));
}
if !self.request.audit().is_clear() {
return Err(ReleaseError::BadSignature);
}
let pre = Self::preimage(&self.request)?;
let key = VerifyingKey::from_bytes(&self.request.certifier)
.map_err(|_| ReleaseError::BadSignature)?;
let sig_bytes: [u8; 64] =
self.sig.as_slice().try_into().map_err(|_| ReleaseError::BadSignature)?;
key.verify(&pre, &Signature::from_bytes(&sig_bytes))
.map_err(|_| ReleaseError::BadSignature)?;
self.request.release_id()
}
}
/// The total door. Certify a release **iff** every gate is present and
/// satisfied; otherwise return the [`Refusal`] that names exactly what is
/// missing, failed, or duplicated. Two terminal states, no third lane.
pub fn certify(
request: ReleaseRequest,
key: &SigningKey,
) -> Result<Result<ReleaseCertificate, Refusal>, ReleaseError> {
if request.alg != ALG_ED25519 {
return Err(ReleaseError::BadAlg(request.alg));
}
if request.certifier != key.verifying_key().to_bytes() {
return Err(ReleaseError::BadSignature);
}
let audit = request.audit();
if !audit.is_clear() {
return Ok(Err(audit));
}
let sig = key.sign(&ReleaseCertificate::preimage(&request)?);
Ok(Ok(ReleaseCertificate { request, sig: sig.to_bytes().to_vec() }))
}
#[cfg(test)]
mod tests {
use super::*;
use joule_code_cargo::{Anchor, CodingOp, Edit};
use joule_code_review::{approval, Review, ReviewStage, ReviewVerdict, ALG_ED25519 as RALG};
use joule_code_schema::Tier;
use joule_code_spec::{cite, coverage, Clause, ClauseKind, Spec, ALG_ED25519 as SALG};
fn sk(s: u8) -> SigningKey {
SigningKey::from_bytes(&[s; 32])
}
/// A fully-satisfied request built from REAL spec/review verdicts.
fn satisfied_request(certifier: &SigningKey) -> ReleaseRequest {
let author = sk(1);
let reviewer = sk(2);
// a spec with one requirement, covered → complete
let spec = Spec {
alg: SALG,
intent: "persist".into(),
clauses: vec![Clause {
id: "C1".into(),
text: "todos persist".into(),
kind: ClauseKind::Requirement,
}],
parent: None,
author: author.verifying_key().to_bytes(),
}
.sign(&author)
.unwrap();
let spec_id = spec.verify().unwrap();
let op = CodingOp {
alg: 0x01,
prior: ContentId::of_bytes(b"t"),
edits: vec![Edit {
path: "s.rs".into(),
anchor: Anchor::ByteRange { start: 0, end: 0 },
replacement: vec![1],
}],
author: author.verifying_key().to_bytes(),
intent: "persist".into(),
tier: Tier::L3,
}
.sign(&author)
.unwrap();
let op_id = op.op.op_id().unwrap();
let citation = cite(&op, &spec, &["C1"], &author).unwrap();
let cov = coverage(&spec, std::slice::from_ref(&citation)).unwrap();
assert!(cov.is_complete());
// a real two-stage approval by an independent reviewer
let mk = |stage| {
Review {
alg: RALG,
op_id: op_id.clone(),
stage,
spec_id: Some(spec_id.clone()),
verdict: ReviewVerdict::Approve,
findings: vec![],
round: 1,
reviewer: reviewer.verifying_key().to_bytes(),
}
.sign(&reviewer)
.unwrap()
};
let reviews = [mk(ReviewStage::SpecCompliance), mk(ReviewStage::Quality)];
let ap = approval(&op, &reviews);
assert!(ap.is_approved());
ReleaseRequest {
alg: ALG_ED25519,
subject: "todo store".into(),
artifact: "build:abc".into(),
spec: spec_id.as_str().to_string(),
outcomes: vec![
GateOutcome::from_coverage(&cov, spec_id.as_str()),
GateOutcome::from_approval(&ap, op_id.as_str()),
GateOutcome::contract(true, "sha2-256:deadbeef"),
GateOutcome::attested_build(true, "build:abc"),
],
certifier: certifier.verifying_key().to_bytes(),
}
}
#[test]
fn all_four_gates_satisfied_certifies_and_verifies() {
let key = sk(9);
let req = satisfied_request(&key);
let cert = certify(req, &key).unwrap().expect("all gates pass");
cert.verify().expect("certificate verifies");
}
#[test]
fn a_failed_gate_is_a_refusal_naming_it_not_a_certificate() {
let key = sk(9);
let mut req = satisfied_request(&key);
// flip the build gate to failed
req.outcomes.retain(|o| o.gate != Gate::AttestedBuild);
req.outcomes.push(GateOutcome::attested_build(false, "build:abc"));
let refusal = certify(req, &key).unwrap().expect_err("must refuse");
assert_eq!(refusal.failed, vec![Gate::AttestedBuild]);
assert!(refusal.to_string().contains("attested-build"));
}
#[test]
fn a_missing_gate_cannot_be_certified() {
let key = sk(9);
let mut req = satisfied_request(&key);
req.outcomes.retain(|o| o.gate != Gate::ContractDischarge);
let refusal = certify(req, &key).unwrap().expect_err("three gates is not enough");
assert_eq!(refusal.missing, vec![Gate::ContractDischarge]);
}
#[test]
fn a_doubled_gate_cannot_mask_a_missing_one() {
let key = sk(9);
let mut req = satisfied_request(&key);
// drop the contract gate, add a second review in its place
req.outcomes.retain(|o| o.gate != Gate::ContractDischarge);
req.outcomes.push(GateOutcome::from_approval(
&Approval { spec_compliance: None, quality: None, void: 0, rounds: 0 },
"op",
));
// the extra review is unsatisfied AND doubled; contract is missing
let refusal = certify(req, &key).unwrap().expect_err("doubling cannot mask");
assert!(refusal.missing.contains(&Gate::ContractDischarge));
assert!(refusal.duplicated.contains(&Gate::Review));
}
#[test]
fn a_tampered_certificate_fails_totality_recheck() {
let key = sk(9);
let req = satisfied_request(&key);
let mut cert = certify(req, &key).unwrap().unwrap();
// flip a satisfied gate to failed AFTER signing
for o in &mut cert.request.outcomes {
if o.gate == Gate::ContractDischarge {
o.satisfied = false;
}
}
assert!(cert.verify().is_err()); // audit no longer clears
}
#[test]
fn coverage_that_is_incomplete_does_not_satisfy_the_spec_gate() {
let author = sk(1);
// an uncovered requirement → coverage incomplete → gate fails
let spec = Spec {
alg: SALG,
intent: "x".into(),
clauses: vec![Clause {
id: "C1".into(),
text: "unimplemented".into(),
kind: ClauseKind::Requirement,
}],
parent: None,
author: author.verifying_key().to_bytes(),
}
.sign(&author)
.unwrap();
let cov = coverage(&spec, &[]).unwrap();
let outcome = GateOutcome::from_coverage(&cov, spec.verify().unwrap().as_str());
assert!(!outcome.satisfied); // derived from the real verdict, not asserted
}
}