{"record":{"id":"7e97491cc4a0801d","repo":"astrid-runtime/astrid","slug":"durable-capsule-id-authority-claims-provenance-a","errorCode":null,"errorMessage":"durable capsule {id} authority claims provenance absent from archive","messagePattern":"durable capsule (.+?) authority claims provenance absent from archive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":344,"sourceCode":"    if !effective_capabilities\n        .expansions_from(&authority.approved_capabilities)\n        .is_empty()\n    {\n        bail!(\"durable capsule {id} manifest exceeds its authority receipt\");\n    }\n    match verification {\n        ArtifactVerification::Signed(provenance) => {\n            let signer = provenance.signer.to_string();\n            let signature = provenance.signature.to_string();\n            if authority.signer.as_deref() != Some(signer.as_str())\n                || authority.signature.as_deref() != Some(signature.as_str())\n            {\n                bail!(\"durable capsule {id} provenance differs from authority receipt\");\n            }\n        },\n        ArtifactVerification::Unsigned { .. } => {\n            if authority.signer.is_some() || authority.signature.is_some() {\n                bail!(\"durable capsule {id} authority claims provenance absent from archive\");\n            }\n        },\n    }\n    Ok(())\n}\n\nstruct ArchiveInventory {\n    files: std::collections::BTreeMap<String, Vec<u8>>,\n    directories: std::collections::BTreeSet<String>,\n}\n\nfn read_archive_files(archive_bytes: &[u8]) -> anyhow::Result<ArchiveInventory> {\n    let decoder = flate2::read::GzDecoder::new(Cursor::new(archive_bytes));\n    let mut archive = tar::Archive::new(decoder);\n    let mut files = std::collections::BTreeMap::new();\n    let mut directories = std::collections::BTreeSet::new();\n    for entry in archive.entries().context(\"read durable capsule archive\")? {\n        let mut entry = entry.context(\"read durable capsule archive entry\")?;","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L326-L362","documentation":"The archive is unsigned (ArtifactVerification::Unsigned) but the authority receipt records a signer and/or signature. The library throws this because the receipt claims provenance that the artifact itself does not carry; installing it would let an unsigned artifact pass under a signed identity. Verification fails for the durable read.","triggerScenarios":"read_verified_durable_package_for_owner where verification is Unsigned { .. } and authority.signer.is_some() || authority.signature.is_some().","commonSituations":"Signing step dropped during republish (e.g. CI built an unsigned artifact) while the receipt was preserved; receipt copied from a signed capsule; artifact re-exported without its signature.","solutions":["Sign the archive with the expected key so the artifact matches the receipt's provenance.","Clear authority.signer/authority.signature if the capsule is intentionally unsigned and re-approve it.","Republish the capsule so receipt and artifact are generated in one consistent step.","Check the build pipeline for a signing step that was skipped or failed silently."],"exampleFix":"// before: unsigned artifact with receipt provenance\nArtifactVerification::Unsigned { .. } + authority.signer = Some(\"pub\")\n// after: sign the artifact before installing\nlet provenance = sign_artifact(&archive_bytes, &signing_key)?;\nArtifactVerification::Signed(provenance)","handlingStrategy":"validation","validationCode":"if let ArtifactVerification::Unsigned { .. } = verification {\n    if authority.signer.is_some() || authority.signature.is_some() {\n        return Err(\"receipt claims provenance but artifact is unsigned\");\n    }\n}","typeGuard":"fn receipt_provenance_consistent(v: &ArtifactVerification, authority: &InstalledAuthority) -> bool {\n    match v {\n        ArtifactVerification::Signed(_) => true,\n        ArtifactVerification::Unsigned { .. } => authority.signer.is_none() && authority.signature.is_none(),\n    }\n}","tryCatchPattern":"match read_verified_durable_package_for_owner(&store, owner, id).await {\n    Ok(pkg) => pkg,\n    Err(e) if e.to_string().contains(\"provenance absent from archive\") => {\n        // sign the artifact or clear receipt provenance, then republish\n    },\n    Err(e) => return Err(e),\n}","preventionTips":["Make signing a mandatory, non-skippable CI step for signed capsules.","Fail the build if signing produces no signature rather than publishing unsigned.","Keep receipt and artifact generation in a single publish transaction."],"tags":["signature","provenance","unsigned","security"],"backgroundTag":"authentication-required","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}