{"record":{"id":"2605eecf93a8cdb7","repo":"astrid-runtime/astrid","slug":"capsule-hash-mismatch-signed-lock-has-re","errorCode":null,"errorMessage":"capsule '{}' hash mismatch: signed lock has {}, resolved artifact has {actual}","messagePattern":"capsule '(.+?)' hash mismatch: signed lock has (.+?), resolved artifact has (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/astrid-cli/src/commands/init_signed_source.rs","lineNumber":136,"sourceCode":"            }\n            let _ = Some(\n                super::super::capsule::install::resolve_capsule_to_file(\n                    &capsule.source,\n                    (!capsule.version.is_empty()).then_some(capsule.version.as_str()),\n                    pinned_tag,\n                    Some(&capsule.name),\n                    &archive_path,\n                )\n                .await?,\n            );\n        }\n        let bytes = std::fs::read(&archive_path)\n            .with_context(|| format!(\"read resolved capsule {}\", capsule.name))?;\n        let actual = manifest_hash(&bytes);\n        if signed.hash != actual {\n            std::fs::remove_file(&archive_path)\n                .with_context(|| format!(\"discard hash-mismatched capsule {}\", capsule.name))?;\n            anyhow::bail!(\n                \"capsule '{}' hash mismatch: signed lock has {}, resolved artifact has {actual}\",\n                capsule.name,\n                signed.hash\n            );\n        }\n        let mut member = capsule.clone();\n        member.source = archive_path.to_string_lossy().into_owned();\n        resolved.push(member);\n    }\n    Ok(resolved)\n}\n\n/// Resolve a distro source to its exact bytes and parse those bytes once.\nasync fn fetch_manifest_bytes(\n    source: &str,\n    offline: bool,\n) -> anyhow::Result<(Vec<u8>, DistroManifest)> {\n    let path = Path::new(source);","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/init_signed_source.rs#L118-L154","documentation":"After resolving a signed Distro member to an archive, resolve_signed_capsules hashes the artifact (manifest_hash) and compares it with the hash recorded in the signed lock. On mismatch the artifact is deleted (quarantined) and this error is thrown, since a differing hash means the archive was tampered with, rebuilt, or the signed lock is stale.","triggerScenarios":"resolve_signed_capsules reads the resolved .capsule archive bytes, computes manifest_hash(&bytes), and it differs from signed.hash; the mismatched file is removed and the bail fires with both hashes.","commonSituations":"Rebuilding a capsule locally without updating the signed lock; a registry serving a different artifact version than the one that was signed; corrupted download; intentional supply-chain tampering attempt.","solutions":["Rebuild the capsule and regenerate/re-sign the Distro lock so signed.hash matches the new artifact.","Re-download the .capsule archive from the trusted source in case the local artifact is corrupted.","Compare the two printed hashes to determine whether the lock or the artifact is outdated, and update the stale one."],"exampleFix":"// before: lock signed against old artifact\nsigned.hash = \"abc123...\"; artifact hash = \"def456...\"  // bail\n\n// after: re-sign against rebuilt artifact\nastrid distro sign --lock Distro.lock.toml  # updates signed.hash to def456...","handlingStrategy":"validation","validationCode":"# verify artifact hash against signed lock before install\nsha256sum dist/my-tool.capsule\ngrep 'hash' Distro.lock.toml  # must match the signed value","typeGuard":null,"tryCatchPattern":"// shell\nif ! astrid init --signed ./Distro.toml; then\n  case $? in\n    *) echo \"hash mismatch: re-download artifact or re-sign lock, never bypass\";;\n  esac\nfi","preventionTips":["Regenerate the signed lock every time a capsule is rebuilt.","Pin exact capsule versions in Distro.toml so artifacts stay reproducible.","Never ignore or work around a hash mismatch — treat it as a possible tampering signal."],"tags":["integrity","hash","supply-chain","capsules"],"backgroundTag":"checksum-mismatch","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}