astrid-runtime/astrid · critical
durable capsule {id} WASM hash differs between authority and
Error message
durable capsule {id} WASM hash differs between authority and archive What it means
After locating the component in the archive and confirming it has a .wasm extension, verify_package_identity hashes the archived bytes with blake3 and compares the hash to the authority's approved_wasm_hash when a hash is pinned. This bail fires when the authority pins a WASM hash and the archived component's hash differs from it — the packaged binary is not the one the authority approved. It prevents installing an archive whose contents were substituted or rebuilt after approval.
Source
Thrown at crates/astrid-capsule-install/src/storage.rs:309
if authority.wasm_hash_pinned && metadata.wasm_hash != authority.approved_wasm_hash {
bail!("durable capsule {id} metadata executable hash differs from authority receipt");
}
if let Some(component) = manifest.components.first() {
let Some(relative) = component.path.to_str() else {
bail!("durable capsule {id} component path is not UTF-8");
};
let Some(bytes) = archive_files.get(relative) else {
bail!("durable capsule {id} component is missing from its archive");
};
if Path::new(relative)
.extension()
.is_some_and(|extension| extension.eq_ignore_ascii_case("wasm"))
{
let archive_hash = blake3::hash(bytes).to_hex().to_string();
if authority.wasm_hash_pinned
&& authority.approved_wasm_hash.as_deref() != Some(archive_hash.as_str())
{
bail!("durable capsule {id} WASM hash differs between authority and archive");
}
if metadata.wasm_hash.as_deref() != Some(archive_hash.as_str()) {
bail!("durable capsule {id} WASM hash differs between metadata and archive");
}
} else if metadata.wasm_hash.is_some() {
bail!("durable capsule {id} metadata names a hash for a non-WASM component");
}
} else if metadata.wasm_hash.is_some() {
bail!("durable capsule {id} metadata names a component absent from its archive");
}
let mut effective_capabilities = manifest.capabilities.clone();
for component in &manifest.components {
if let Some(capabilities) = &component.capabilities {
effective_capabilities.merge_from(capabilities);
}
}
if !effective_capabilities
.expansions_from(&authority.approved_capabilities)View on GitHub (pinned to affd8760f4)
Solutions
- Repack the archive with the exact approved component binary whose blake3 hash equals authority.approved_wasm_hash.
- Have the authority re-approve the archived build so approved_wasm_hash matches the actual archived bytes.
- Re-download the capsule from the trusted source — the local archive may be corrupted or tampered with.
- Verify the blake3 hash of the archived .wasm manually and compare against the receipt to confirm which binary was packaged.
Example fix
// before: repackaged with a rebuilt wasm
let bytes = std::fs::read("target/debug/component.wasm")?;
// after: package the approved artifact
let bytes = std::fs::read("approved/component.wasm")?;
debug_assert_eq!(blake3::hash(&bytes).to_string(), authority.approved_wasm_hash.unwrap()); Defensive patterns
Strategy: validation
Validate before calling
if authority.wasm_hash_pinned {
let bytes = archive_files.get(component_path).ok_or_else(|| anyhow!("missing component"))?;
let hash = blake3::hash(bytes).to_hex().to_string();
anyhow::ensure!(authority.approved_wasm_hash.as_deref() == Some(hash.as_str()), "archived wasm hash {} not approved", hash);
} Prevention
- Only package artifacts whose blake3 hash matches the approved receipt.
- Download capsules over verified channels and verify hashes before install.
- Re-run authority approval after any toolchain or flag change that rebuilds the wasm.
When it happens
Trigger: Calling read_verified_durable_package_for_owner on a capsule where authority.wasm_hash_pinned is true and blake3(archive component bytes) != authority.approved_wasm_hash — the archive's .wasm file differs from the approved binary even though metadata may agree with the receipt.
Common situations: Recompiling the WASM component (different toolchain/flags) and repacking without re-approval; a tampered or corrupted archive in transit or storage; accidentally packaging a debug build while the receipt pins the release build; pulling the capsule from an untrusted mirror.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- durable capsule {id} metadata executable hash differs from a
- WASM capsule has no BLAKE3 hash in meta.json
- durable capsule {} has malformed contracts pin
- durable capsule {} contracts blob digest mismatch
- refusing to seed canonical from a non-content-address contra
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/4744b8a511cbf0a3.
Report an issue: GitHub.