{"record":{"id":"6c320ea175d954a5","repo":"astrid-runtime/astrid","slug":"signed-shuttle-for-distro-id-is-missing-its-ma","errorCode":null,"errorMessage":"signed shuttle for '{distro_id}' is missing its manifest_hash binding — refusing. The signature covers the lock, not Distro.toml; without manifest_hash the manifest (env/selection) is unauthenticated and could be swapped.","messagePattern":"signed shuttle for '(.+?)' is missing its manifest_hash binding — refusing\\. The signature covers the lock, not Distro\\.toml; without manifest_hash the manifest \\(env/selection\\) is unauthenticated and could be swapped\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/astrid-cli/src/commands/distro/shuttle_install.rs","lineNumber":285,"sourceCode":"/// Pure (no I/O) so the binding gate is unit-testable.\nfn check_manifest_binding(\n    distro_id: &str,\n    signed: bool,\n    lock: &DistroLock,\n    manifest_bytes: &[u8],\n) -> anyhow::Result<()> {\n    let actual = manifest_hash(manifest_bytes);\n    match &lock.manifest_hash {\n        Some(recorded) => {\n            if recorded != &actual {\n                bail!(\n                    \"manifest hash mismatch: lock records {recorded}, archive Distro.toml hashes \\\n                     to {actual} — the shuttle is inconsistent or tampered\"\n                );\n            }\n            Ok(())\n        },\n        None if signed => bail!(\n            \"signed shuttle for '{distro_id}' is missing its manifest_hash binding — refusing. \\\n             The signature covers the lock, not Distro.toml; without manifest_hash the manifest \\\n             (env/selection) is unauthenticated and could be swapped.\"\n        ),\n        None => Ok(()),\n    }\n}\n\n/// Verify the per-capsule blake3 of every lock entry against the bytes\n/// actually present in the mirror. Returns an error on the first\n/// mismatch or missing file. Pure (no install side effects) so the\n/// integrity gate is unit-testable.\nfn verify_capsule_hashes(mirror: &Path, lock: &DistroLock) -> anyhow::Result<()> {\n    for entry in &lock.capsules {\n        let file = shuttle::capsule_mirror_path(mirror, &entry.name);\n        if !file.is_file() {\n            bail!(\n                \"capsule '{}' is missing from the shuttle mirror\",","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/distro/shuttle_install.rs#L267-L303","documentation":"For a signed shuttle, check_manifest_binding requires lock.manifest_hash to be present. The signature covers the lock, not Distro.toml itself, so without the recorded manifest_hash the manifest (environment and capsule selection) is unauthenticated and could be silently swapped. Signed artifacts missing this binding are hard-refused rather than installed.","triggerScenarios":"install_from_shuttle with a signed lock that lacks a manifest_hash field — typically produced by an older packing tool version, a hand-crafted lock, or a lock stripped during editing.","commonSituations":"Old shuttles packed before manifest_hash binding was introduced; manually edited or stripped lock files; mixing a new signed lock format with an old tool or vice versa.","solutions":["Rebuild the shuttle with the current packing tool so the signed lock includes manifest_hash","Regenerate signatures with an up-to-date sealing workflow rather than hand-editing the lock","If the shuttle must remain unsigned, distribute it unsigned — do not sign a lock without the hash binding"],"exampleFix":"// before: signed lock without manifest_hash\n// after: repack with the current astrid version\n// astrid distro pack --sign <key> --output /media/shuttle","handlingStrategy":"validation","validationCode":"if lock.signature.is_some() && lock.manifest_hash.is_none() {\n    return Err(anyhow!(\"signed lock missing manifest_hash binding\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the current packing tool for signed shuttles","Reject hand-edited lock files in review","Test signed shuttles with a manifest-binding check before distribution"],"tags":["signature","security","manifest-binding","tamper-detection"],"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"}