{"record":{"id":"50ff7054920846bb","repo":"astrid-runtime/astrid","slug":"durable-capsule-id-manifest-digest-differs-from","errorCode":null,"errorMessage":"durable capsule {id} manifest digest differs from authority receipt","messagePattern":"durable capsule (.+?) manifest digest differs from authority receipt","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/astrid-capsule-install/src/storage.rs","lineNumber":274,"sourceCode":"    manifest_bytes: &[u8],\n    verification: &ArtifactVerification,\n    archive_files: &std::collections::BTreeMap<String, Vec<u8>>,\n) -> anyhow::Result<()> {\n    if authority.schema_version != 1 {\n        bail!(\n            \"durable capsule {id} has unsupported authority schema {}\",\n            authority.schema_version\n        );\n    }\n    if authority.capsule_id != id || manifest.package.name != id {\n        bail!(\"durable capsule {id} identity differs across archive and authority\");\n    }\n    if authority.version != manifest.package.version || metadata.version != authority.version {\n        bail!(\"durable capsule {id} version differs across package records\");\n    }\n    let manifest_digest = crate::authority::digest_manifest(manifest_bytes);\n    if authority.manifest_digest != manifest_digest {\n        bail!(\"durable capsule {id} manifest digest differs from authority receipt\");\n    }\n    if authority.content_digest != verification.content_digest() {\n        bail!(\"durable capsule {id} content digest differs from authority receipt\");\n    }\n    let expected_imports = crate::wit::version_map_to_strings(&manifest.imports, |definition| {\n        definition.version.to_string()\n    });\n    if metadata.imports != expected_imports {\n        bail!(\"durable capsule {id} imports differ between metadata and archive\");\n    }\n    let expected_exports = crate::wit::version_map_to_strings(&manifest.exports, |definition| {\n        definition.version.to_string()\n    });\n    if metadata.exports != expected_exports {\n        bail!(\"durable capsule {id} exports differ between metadata and archive\");\n    }\n    if authority.wasm_hash_pinned && metadata.wasm_hash != authority.approved_wasm_hash {\n        bail!(\"durable capsule {id} metadata executable hash differs from authority receipt\");","sourceCodeStart":256,"sourceCodeEnd":292,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage.rs#L256-L292","documentation":"This error fires when digest_manifest(manifest_bytes) does not equal the manifest_digest recorded in the authority receipt. The authority receipt pins the exact bytes of the manifest it approved; a different digest means the manifest on disk was modified after signing, or the receipt belongs to a different manifest. The library refuses to load such a capsule because the approved manifest cannot be trusted.","triggerScenarios":"read_verified_durable_package_for_owner loads an archive whose manifest bytes hash to something other than InstalledAuthority.manifest_digest — typically after editing CapsuleManifest fields (name, version, imports, exports, capabilities) without re-running the authority approval step.","commonSituations":"Hand-editing capsule.toml/manifest to tweak dependencies or permissions post-install; a build pipeline regenerating the manifest with different formatting/field order after approval; replaying an old authority receipt over a newly built archive.","solutions":["Re-run the authority approval step to re-sign the current manifest bytes and update the receipt's manifest_digest.","Restore the original signed manifest bytes that match the receipt (e.g. from the publish artifact or VCS).","Reinstall the capsule from the originally published archive instead of a locally modified one.","Ensure the build pipeline does not rewrite the manifest between approval and install."],"exampleFix":"// before: editing the manifest after approval\n// manifest.capabilities.network added by hand -> digest differs from receipt\n// after: re-approve so the receipt covers the new bytes\nlet manifest_bytes = serialize_manifest(&edited_manifest)?;\nlet receipt = authority.approve(&manifest_bytes)?; // refreshes manifest_digest\nstore.write_authority(owner, id, &receipt)?;","handlingStrategy":"validation","validationCode":"let digest = crate::authority::digest_manifest(&manifest_bytes);\nif digest != authority.manifest_digest {\n    // re-approve or restore original manifest before loading\n}","typeGuard":"fn manifest_is_approved(bytes: &[u8], a: &InstalledAuthority) -> bool {\n    crate::authority::digest_manifest(bytes) == a.manifest_digest\n}","tryCatchPattern":"match store.read_verified_durable_package_for_owner(owner, id) {\n    Ok(pkg) => pkg,\n    Err(e) if e.to_string().contains(\"manifest digest differs from authority receipt\") => {\n        // restore signed manifest or re-run authority approval\n        authority.approve(&manifest_bytes)?;\n        store.read_verified_durable_package_for_owner(owner, id)?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Treat the manifest as immutable after authority approval; make changes via a republish flow.","Disable build steps that reformat or regenerate the manifest between approval and install.","Record and compare manifest digests in CI before deploying capsules.","Keep the originally published archive as the single source for reinstalls."],"tags":["integrity","capsule","checksum","tampering","rust"],"backgroundTag":"checksum-mismatch","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"}