jdx/mise · critical

packslip:{project}: this release {}. If the vendor announce

Error message

packslip:{project}: this release {}.

If the vendor announced the change, run `mise packslip forget {project}` and install again; the next release accepted sets the pin.

What it means

packslip pins record the signer, attestation source, and provenance of the first accepted release of a project. check_against compares a new release's observed signing metadata against that pin: a different signer or scheme, a repackager attestation where the vendor previously attested, or dropped build provenance all constitute a downgrade and are refused. The error lists all problems and explains how to reset the pin if the vendor legitimately changed its signing setup.

Source

Thrown at src/packslip_pins.rs:153

fn check_against(pin: &Pin, project: &str, observed: Observed<'_>) -> Result<()> {
    let signer = signer_of(observed.scheme, observed.key_id);
    let mut problems = Vec::new();
    if pin.scheme != observed.scheme || pin.signer != signer {
        problems.push(format!(
            "is signed by {signer} ({}), but {} ({}) signed what mise accepted before",
            observed.scheme, pin.signer, pin.scheme
        ));
    }
    if pin.attested_by == "vendor" && observed.attested_by == "repackager" {
        problems.push(
            "is attested by a repackager, but the vendor's own packslip was accepted before".into(),
        );
    }
    if pin.provenance && !observed.provenance {
        problems.push("drops the build provenance every artifact linked before".into());
    }
    if !problems.is_empty() {
        bail!(
            "packslip:{project}: this release {}.\n\nIf the vendor announced the change, run `mise packslip forget {project}` and install again; the next release accepted sets the pin.",
            problems.join(", and ")
        );
    }
    Ok(())
}

/// Set the project's pin from an accepted release, or strengthen it: what
/// got stronger is remembered, what stayed the same is left alone. Checks
/// again under the lock, since the file may have changed since [`check`].
pub(crate) fn record(project: &str, observed: Observed<'_>) -> Result<Pin> {
    record_at(&pins_file(), project, observed)
}

pub(crate) fn record_at(path: &Path, project: &str, observed: Observed<'_>) -> Result<Pin> {
    let _lock = locked(path)?;
    let mut pins = load(path)?;
    let signer = signer_of(observed.scheme, observed.key_id);

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Verify the vendor actually announced a signing/provenance change; check the project's release notes before acting.
  2. If legitimate, reset the pin: `mise packslip forget <project>`, then install again — the next accepted release sets a fresh pin.
  3. If not announced, do not reset the pin; treat the release as potentially compromised and verify artifacts out-of-band.
  4. Compare the reported signer/scheme in the message with the vendor's documented signing identity (e.g. expected workflow OIDC identity).

Example fix

// before: vendor rotated keys, pin refuses the release
mise install gh  # error: signed by new-workflow, but old-workflow signed before
// after confirming the rotation is legitimate
mise packslip forget gh && mise install gh  # new pin set from accepted release
Defensive patterns

Strategy: validation

Validate before calling

let pinned = mise_packslip_pin(project)?; // signer/scheme/provenance
let observed = observe_release(release)?;
if pinned.signer != signer_of(&observed.scheme, &observed.key_id)
    || (pinned.provenance && !observed.provenance) {
    eprintln!("release would downgrade pin for {project}; investigate before forgetting the pin");
}

Type guard

fn is_downgrade(pin: &Pin, observed: &Observed) -> bool {
    pin.scheme != observed.scheme
        || pin.signer != signer_of(observed.scheme, observed.key_id)
        || (pin.attested_by == "vendor" && observed.attested_by == "repackager")
        || (pin.provenance && !observed.provenance)
}

Try / catch

match install(project) {
    Err(e) if e.to_string().starts_with("packslip:") => {
        // refuse to auto-reset; require explicit human decision
        eprintln!("{e}");
        Err(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: Installing or verifying a packslip release for a pinned project where the observed Observed{scheme, key_id, attested_by, provenance} mismatches the stored Pin: new signing key/workflow, different signature scheme, repackager instead of vendor attestation, or missing provenance.

Common situations: A vendor rotating signing keys or switching CI workflows (workflow ref changed identity); a release now repackaged by a third party; a release built without provenance attestations; mirror serving substituted artifacts.

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 jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/b8b34e27e2512de7. Report an issue: GitHub.