{"record":{"id":"347960a8e9627ee6","repo":"astrid-runtime/astrid","slug":"durable-capsule-id-provenance-differs-from-autho","errorCode":null,"errorMessage":"durable capsule {id} provenance differs from authority receipt","messagePattern":"durable capsule (.+?) provenance differs from authority receipt","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":339,"sourceCode":"    for component in &manifest.components {\n        if let Some(capabilities) = &component.capabilities {\n            effective_capabilities.merge_from(capabilities);\n        }\n    }\n    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));","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L321-L357","documentation":"The archive carries a signature (ArtifactVerification::Signed) whose signer or signature does not match the signer/signature recorded in the InstalledAuthority receipt. The library throws this because durable capsules must be provably signed by the same identity the authority receipt approved; a different signer or signature means the artifact provenance was replaced or the receipt is stale.","triggerScenarios":"read_verified_durable_package_for_owner where provenance.signer.to_string() != authority.signer or provenance.signature.to_string() != authority.signature for a Signed artifact.","commonSituations":"Re-signing an artifact with a new key without updating the authority receipt; importing a capsule signed by a different publisher; rotating signing keys; receipt pinned to an old signature of the same package.","solutions":["Re-issue the authority receipt with the current signer and signature values.","Re-sign the archive with the key matching authority.signer so provenance matches the receipt.","Restore the original signed artifact if the wrong one was installed.","Update the key-rotation workflow so receipts are refreshed whenever signing keys change."],"exampleFix":"// before: receipt holds old signer\nauthority.signer = Some(\"old-publisher\".into());\n// after: refresh receipt after re-signing\nauthority.signer = Some(provenance.signer.to_string());\nauthority.signature = Some(provenance.signature.to_string());","handlingStrategy":"validation","validationCode":"if let ArtifactVerification::Signed(p) = verification {\n    if authority.signer.as_deref() != Some(p.signer.to_string().as_str())\n        || authority.signature.as_deref() != Some(p.signature.to_string().as_str()) {\n        return Err(\"provenance does not match authority receipt\");\n    }\n}","typeGuard":"fn provenance_matches(p: &Provenance, authority: &InstalledAuthority) -> bool {\n    authority.signer.as_deref() == Some(p.signer.to_string().as_str())\n        && authority.signature.as_deref() == Some(p.signature.to_string().as_str())\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 differs from authority receipt\") => {\n        // re-sign with approved key or refresh the receipt\n    },\n    Err(e) => return Err(e),\n}","preventionTips":["Always sign with the key recorded in the authority receipt.","Refresh receipts as part of any key-rotation procedure.","Verify provenance against the receipt immediately after signing, before publishing."],"tags":["signature","provenance","authority","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"}