{"record":{"id":"b91a603a03ec67c3","repo":"astrid-runtime/astrid","slug":"signed-distro-lock-contains-undeclared-capsule","errorCode":null,"errorMessage":"signed Distro.lock contains undeclared capsule '{}'","messagePattern":"signed Distro\\.lock contains undeclared capsule '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/init_signed_source.rs","lineNumber":354,"sourceCode":"    {\n        bail!(\"Distro.lock identity does not match the signed Distro.toml\");\n    }\n\n    let declared: HashMap<&str, &DistroCapsule> = manifest\n        .capsules\n        .iter()\n        .map(|capsule| (capsule.name.as_str(), capsule))\n        .collect();\n    anyhow::ensure!(\n        declared.len() == manifest.capsules.len() && lock.capsules.len() == declared.len(),\n        \"signed Distro.lock members do not match Distro.toml declarations\"\n    );\n    for capsule in &lock.capsules {\n        let declared_capsule = declared\n            .get(capsule.name.as_str())\n            .copied()\n            .ok_or_else(|| {\n                anyhow::anyhow!(\n                    \"signed Distro.lock contains undeclared capsule '{}'\",\n                    capsule.name\n                )\n            })?;\n        if capsule.source != declared_capsule.source || capsule.version != declared_capsule.version\n        {\n            bail!(\n                \"signed Distro.lock entry '{}' does not match Distro.toml\",\n                capsule.name\n            );\n        }\n        anyhow::ensure!(\n            !capsule.hash.is_empty(),\n            \"signed Distro.lock entry '{}' has no capsule hash\",\n            capsule.name\n        );\n    }\n    Ok(())","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/init_signed_source.rs#L336-L372","documentation":"While walking the signed Distro.lock, every lock entry must correspond to a capsule declared in Distro.toml. If a lock capsule's name has no declared counterpart, the lock references something the signed manifest never authorized — a supply-chain red flag — so validation aborts with this message naming the offending capsule.","triggerScenarios":"fetch_signed_manifest -> verify_signed_manifest -> validate_signed_member_sets when Distro.lock contains a capsule name that Distro.toml does not declare (e.g. lock left over from a removed capsule, or a tampered lock).","commonSituations":"A capsule was deleted from Distro.toml but Distro.lock was not regenerated before re-sealing/publishing; restoring an old lock file over a newer manifest; manual lock editing.","solutions":["Regenerate and re-sign Distro.lock from the current Distro.toml so both member sets match","If the capsule should still exist, re-add its declaration to Distro.toml and re-seal","Remove the stale capsule entry from Distro.lock and republish"],"exampleFix":"# before: lock has 'legacy' but toml doesn't\ncapsules = [\"alpha\", \"legacy\"]   # lock\ncapsules = [\"alpha\"]             # toml\n# after: regenerate lock\ncapsules = [\"alpha\"]             # lock (regenerated & re-signed)","handlingStrategy":"validation","validationCode":"let declared: HashSet<&str> = manifest.capsules.iter().map(|c| c.name.as_str()).collect();\nlet extra: Vec<_> = lock.capsules.iter().map(|c| c.name.as_str()).filter(|n| !declared.contains(n)).collect();\nif !extra.is_empty() { eprintln!(\"lock has undeclared capsules: {extra:?}\"); }","typeGuard":null,"tryCatchPattern":"match validate_signed_member_sets(manifest, lock) {\n    Err(e) if e.to_string().contains(\"undeclared capsule\") => {\n        eprintln!(\"Lock references a capsule removed from Distro.toml; regenerate + re-seal the lock\");\n    }\n    r => r?,\n}","preventionTips":["Treat Distro.lock as generated output only — always rebuild after TOML edits","Re-seal and republish manifest+lock atomically","Verify lock/manifest pair freshness before consuming a distro"],"tags":["signing","consistency","supply-chain"],"backgroundTag":"schema-validation-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}