//! 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) -> 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) -> 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) -> 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) -> 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, /// 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 { 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, /// Gates present but not satisfied. pub failed: Vec, /// Gates appearing more than once (a gate may not be doubled to mask a /// missing one). pub duplicated: Vec, } 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::>().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, } 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, 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 { 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, 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 } }