astrid-runtime/astrid · error

signed Distro.toml does not match Distro.lock manifest_hash;

Error message

signed Distro.toml does not match Distro.lock manifest_hash; refusing to resolve members

What it means

verify_signed_manifest compares the manifest_hash recorded in the local Distro.lock against the hash of the signed Distro.toml that was just fetched and authenticated. If they differ, it refuses to resolve members because the signed manifest being used is not the one the lock file was generated from — proceeding could install capsules that were never covered by the lock's signature. This is an integrity check protecting the supply chain during signed-source init.

Source

Thrown at crates/astrid-cli/src/commands/init_signed_source.rs:296

    let mut response = response;
    while let Some(chunk) = response.chunk().await? {
        bytes.extend_from_slice(&chunk);
        anyhow::ensure!(bytes.len() <= limit, "{name} exceeds size limit");
    }
    Ok(bytes)
}

/// Bind exact TOML bytes into the signed lock, then verify that lock.
fn verify_signed_manifest(
    home: &AstridHome,
    manifest: &DistroManifest,
    manifest_hash: &str,
    lock: &DistroLock,
    sig_hex: &str,
    accept_new_key: bool,
) -> anyhow::Result<HashMap<String, String>> {
    if lock.manifest_hash.as_deref() != Some(manifest_hash) {
        bail!(
            "signed Distro.toml does not match Distro.lock manifest_hash; refusing to resolve members"
        );
    }
    validate_signed_member_sets(manifest, lock)?;
    let signing = manifest.distro.signing.as_ref().ok_or_else(|| {
        anyhow::anyhow!("signed Distro.toml has no [distro.signing] configuration")
    })?;
    let outcome = trust::verify_and_pin(
        home,
        &manifest.distro.id,
        &signing.pubkey,
        sig_hex,
        lock,
        accept_new_key,
        trust::TrustPolicy::RequireExistingPin,
    )?;
    tracing::info!(
        distro = %manifest.distro.id,

View on GitHub (pinned to affd8760f4)

Solutions

  1. Regenerate/update Distro.lock from the current signed Distro.toml (re-run the init/lock workflow for the new distro version) so manifest_hash matches.
  2. Pin the fetched manifest: point the init command at the signed Distro.toml URL/tag that corresponds to the existing Distro.lock.
  3. Verify you didn't hand-edit Distro.lock; restore it from version control or regenerate it.
  4. Confirm the upstream distro actually intends the change (review the new signed manifest) before accepting the new state — don't bypass the check.

Example fix

// before: stale lock
// Distro.lock: manifest_hash = "sha256:aaaa..." but signed Distro.toml hashes to "sha256:bbbb..."
// after: regenerate the lock against the new signed manifest
$ astrid init --signed-source https://example.com/distros/mydistro/Distro.toml --regenerate-lock
Defensive patterns

Strategy: validation

Validate before calling

if let Some(lock_hash) = &lock.manifest_hash {
    let current = sha256_hex(signed_manifest_bytes);
    if current != *lock_hash {
        eprintln!("signed Distro.toml drifted from Distro.lock; re-lock before init");
    }
}

Try / catch

match fetch_signed_manifest(&client, &url, &lock).await {
    Err(e) if e.to_string().contains("manifest_hash") => {
        eprintln!("Lock is stale relative to the signed Distro.toml; regenerate Distro.lock or pin the old manifest URL");
        regenerate_lock_and_retry().await?
    }
    Ok(m) => m,
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: fetch_signed_manifest calls verify_signed_manifest with the hash of the fetched signed Distro.toml and the local Distro.lock; lock.manifest_hash is None or differs from that hash — i.e. the signed Distro.toml was updated upstream after Distro.lock was created, or the lock file was edited/regenerated against a different manifest.

Common situations: Upstream distro published a new signed Distro.toml (new release) while the user's checkout still has an old Distro.lock; user hand-edited Distro.lock; lock generated from a different distro version than the signed manifest URL points to; hash computed over a locally modified manifest.

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


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/e5fe16e06272dc99. Report an issue: GitHub.