{"record":{"id":"62d02790ada12aff","repo":"astrid-runtime/astrid","slug":"distro-lock-capsule-capsule-requires-a-canonic","errorCode":null,"errorMessage":"Distro.lock capsule '{capsule}' requires a canonical blake3:<hex> WASM hash","messagePattern":"Distro\\.lock capsule '(.+?)' requires a canonical blake3:<hex> WASM hash","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/init_grant.rs","lineNumber":426,"sourceCode":"        let blob_path = home.bin_dir().join(format!(\"{locked_hex}.wasm\"));\n        std::fs::read(&blob_path).with_context(|| {\n            format!(\n                \"Distro.lock capsule '{}' content blob is missing or unreadable at {}\",\n                capsule,\n                blob_path.display()\n            )\n        })?\n    };\n    let actual = blake3::hash(&bytes);\n    if actual != locked {\n        bail!(\"Distro.lock capsule '{capsule}' content blob bytes do not match hash {locked_hash}\");\n    }\n    Ok(())\n}\n\nfn parse_locked_blake3(capsule: &CapsuleId, value: &str) -> anyhow::Result<blake3::Hash> {\n    let Some(hex) = value.strip_prefix(\"blake3:\") else {\n        bail!(\"Distro.lock capsule '{capsule}' requires a canonical blake3:<hex> WASM hash\");\n    };\n    let hash = blake3::Hash::from_hex(hex).map_err(|_| {\n        anyhow::anyhow!(\"Distro.lock capsule '{capsule}' has an invalid BLAKE3 hash\")\n    })?;\n    if hex.len() != 64 || hash.to_hex().as_str() != hex {\n        bail!(\"Distro.lock capsule '{capsule}' requires a canonical lowercase BLAKE3 hash\");\n    }\n    Ok(hash)\n}\n\nfn manifest_declares_wasm(manifest: &CapsuleManifest) -> bool {\n    manifest\n        .components\n        .iter()\n        .any(|component| component.path.extension().and_then(|ext| ext.to_str()) == Some(\"wasm\"))\n}\n\n/// Apply capsule-access grants for the installed set (opt-in), or print","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/init_grant.rs#L408-L444","documentation":"Distro.lock WASM hashes must use the canonical 'blake3:<64 hex chars>' form. parse_locked_blake3 rejects any value without the blake3: prefix, since non-prefixed or foreign-algorithm hashes cannot be verified against the content store.","triggerScenarios":"parse_locked_blake3 (called from validate_locked_wasm) receives a locked_hash where strip_prefix(\"blake3:\") returns None — e.g. a bare hex digest, a sha256: prefix, or an empty string.","commonSituations":"Hand-edited lockfile dropping the prefix, lockfile written by an older tool using a different hash format, copy-pasting a digest from a non-blake3 tool.","solutions":["Rewrite the Distro.lock hash as blake3:<64-char lowercase hex>.","Regenerate Distro.lock with the current CLI so hashes are emitted canonically.","Recompute the artifact hash with blake3 (not sha256/xxhash) and store it with the prefix."],"exampleFix":"// before (Distro.lock)\nhash = \"9f86d081884c7d659a2f...\"\n// after\nhash = \"blake3:9f86d081884c7d659a2f...\"","handlingStrategy":"validation","validationCode":"if !locked_hash.starts_with(\"blake3:\") {\n    return Err(anyhow!(\"hash must be blake3:<hex>\"));\n}","typeGuard":"fn is_blake3_lock_hash(v: &str) -> bool {\n    v.strip_prefix(\"blake3:\")\n        .map(|h| h.len() == 64 && h.bytes().all(|b| b.is_ascii_hexdigit()))\n        .unwrap_or(false)\n}","tryCatchPattern":"match parse_locked_blake3(capsule, value) {\n    Err(e) if e.to_string().contains(\"canonical blake3:\") => fix_hash_prefix(capsule)?,\n    other => other?,\n}","preventionTips":["Always emit hashes as blake3:<64 lowercase hex> in tooling.","Lint the lockfile format in CI.","Never copy hashes from sha256 or other digest outputs."],"tags":["hash","format","lockfile","validation"],"backgroundTag":"invalid-argument-format","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"}