astrid-runtime/astrid · error
distro signature verification failed
Error message
distro signature verification failed
What it means
verify_lock signs the distro lock over a canonical digest (lock_signing_digest) and checks the ed25519-style signature from Distro.sig against the distro's declared public key. This error is the blanket rejection from pubkey.verify: the signature bytes parsed fine (they were valid 64-byte hex) but do not match the digest under that key. It deliberately returns no cryptographic detail to avoid oracle leaks.
Solutions
- Re-sign the lock with the private key matching the manifest pubkey and redeploy Distro.sig.
- Verify you have the exact lock the sig was produced from (diff lock contents/hashes; re-fetch the distro).
- Check the manifest's [distro.signing].pubkey matches the key that produced the sig (key rotation?).
- Inspect Distro.sig for corruption (must be 64-byte hex, no wrapping/encoding changes); re-download it.
Example fix
// before: lock edited after signing $ astrid distro sign --lock Distro.lock # regenerate Distro.sig from the edited lock // after $ astrid distro apply --distro my-distro # verify now passes
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check before trusting: hex decode length only; actual verify needs the key.
let sig_bytes = hex::decode(sig_hex.trim())?;
if sig_bytes.len() != 64 { return Err(anyhow!("Distro.sig must be 64-byte hex")); } Type guard
fn is_wellformed_sig(sig_hex: &str) -> bool {
hex::decode(sig_hex.trim()).map(|b| b.len() == 64).unwrap_or(false)
} Try / catch
match sign::verify_lock(&lock, sig_hex, &pubkey) {
Ok(()) => install(),
Err(e) if e.to_string().contains("verification failed") => {
eprintln!("signature mismatch: re-sign the lock or re-fetch the artifact");
},
Err(e) => return Err(e),
} Prevention
- Always regenerate Distro.sig in the same CI step that produces the lock.
- Verify signatures in CI before publishing a distro release.
- Keep the signing key and manifest pubkey in sync; rotate both together.
- Never hand-edit a signed lock file.
When it happens
Trigger: verify_lock(lock, sig_hex, pubkey) when: the Distro.sig was made over a different lock file/revision; the lock was edited after signing; the sig was generated with a different private key than the manifest's [distro.signing].pubkey; or the sig hex was corrupted/transcoded (e.g. re-saved via a tool that altered whitespace-to-content).
Common situations: Distro maintainers re-signing after a lock edit but shipping the old .sig; shipping a sig from a stale branch; CI building a lock deterministically but signing a different serialization; malicious tampering with the artifact in transit.
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
- distro ' ' is pinned to but this artifact is signed by —…
- capsule ' ' signed ref did not match installed ref
- capsule ' ' hash mismatch: signed lock has , resolved…
- capsule ' ' is ; explicit local approval is required
- capsule provenance changed after authority decision
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/c90c46d16c7ae5e8.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-cli/src/commands/distro/sign.rs:105
}
/// Verify a hex `Distro.sig` against a lock and a public key.
///
/// # Errors
///
/// Returns an error if the signature is malformed (not 64 hex bytes) or
/// does not verify against the lock's signing digest under `pubkey`.
pub(crate) fn verify_lock(
lock: &DistroLock,
sig_hex: &str,
pubkey: &PublicKey,
) -> anyhow::Result<()> {
let sig = Signature::from_hex(sig_hex.trim())
.map_err(|e| anyhow::anyhow!("malformed Distro.sig (expected 64-byte hex): {e}"))?;
let digest = lock_signing_digest(lock)?;
pubkey
.verify(&digest, &sig)
.map_err(|_| anyhow::anyhow!("distro signature verification failed"))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::distro::lock::{DistroLock, DistroLockMeta, LockedCapsule};
fn sample_lock() -> DistroLock {
DistroLock {
schema_version: 1,
distro: DistroLockMeta {
id: "test".into(),
version: "0.1.0".into(),
resolved_at: "2026-01-01T00:00:00Z".into(),
},
capsules: vec![LockedCapsule {
name: "astrid-capsule-cli".into(),
version: "0.1.0".into(),View on GitHub (pinned to affd8760f4)