astrid-runtime/astrid · critical

capsule '{}' changed after authority review (approved {}, fo

Error message

capsule '{}' changed after authority review (approved {}, found {})

What it means

`ensure_bound_digest` verifies that the content digest recorded at authority review time matches the digest of the capsule actually being installed (`inspection.content_digest` vs `approved_digest`). Called from `authorize_install`, this error is thrown when the capsule changed between the interactive/programmatic review and final authorization — the last line of defense binding an approval to exact bytes.

Source

Thrown at crates/astrid-capsule-install/src/authority.rs:988

            Some(verified.signature.to_string()),
        ),
        ArtifactVerification::Unsigned { .. } => (None, None),
    }
}

pub(crate) fn digest_manifest(bytes: &[u8]) -> String {
    let mut hasher = blake3::Hasher::new();
    hasher.update(MANIFEST_DIGEST_DOMAIN);
    hasher.update(bytes);
    hasher.finalize().to_hex().to_string()
}

fn ensure_bound_digest(
    inspection: &InstallInspection,
    approved_digest: &str,
) -> anyhow::Result<()> {
    if inspection.content_digest != approved_digest {
        bail!(
            "capsule '{}' changed after authority review (approved {}, found {})",
            inspection.capsule_id,
            approved_digest,
            inspection.content_digest
        );
    }
    Ok(())
}

#[cfg(test)]
#[path = "authority/unit_tests.rs"]
mod tests;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Re-run inspection and approval against the current content so the approved digest matches, then authorize again
  2. Freeze the source (revert to the reviewed commit/artifact) and retry authorization without modifying it in between
  3. Ensure no concurrent builds/watchers write to the capsule directory while authorization is in flight

Example fix

// before
bail!("capsule '{}' changed after authority review (approved {}, found {})", inspection.capsule_id, approved_digest, inspection.content_digest);
// after: re-inspect current content and re-approve
// let inspection = inspect_directory_for_principal_in_workspace(...)?;
// let approved = prompt_authority_decision(&inspection)?;
// authorize_install(inspection, &approved)
Defensive patterns

Strategy: validation

Validate before calling

// Bind the approval to exact bytes before authorizing
if inspection.content_digest != approved_digest {
    // re-run inspection + approval; never authorize stale decisions
}

Type guard

fn bound_to_reviewed(inspection: &InstallInspection, approved_digest: &str) -> bool {
    inspection.content_digest == approved_digest
}

Try / catch

match authorize_install(&inspection, &approved_digest) {
    Err(e) if e.to_string().contains("changed after authority review") => {
        let fresh = inspect_and_review(source)?; authorize_install(&fresh, &fresh.approved_digest)
    },
    other => other,
}

Prevention

When it happens

Trigger: Calling `authorize_install` with an `InstallInspection` whose `content_digest` differs from the `approved_digest` captured during the review — i.e. the source directory or artifact was modified after the user approved it (including in `inspect_archive_for_principal_in_workspace` / `inspect_directory_for_principal_in_workspace` flows).

Common situations: Rebuilding the artifact while the approval prompt is open; editing source files between review and confirm; a concurrent process writing into the capsule directory during authorization.

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/f201fe3aa051f003. Report an issue: GitHub.