{"record":{"id":"b8b34e27e2512de7","repo":"jdx/mise","slug":"packslip-project-this-release-if-the-vendo","errorCode":null,"errorMessage":"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.","messagePattern":"packslip:(.+?): this release (.+?)\\.\n\nIf the vendor announced the change, run `mise packslip forget (.+?)` and install again; the next release accepted sets the pin\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/packslip_pins.rs","lineNumber":153,"sourceCode":"fn check_against(pin: &Pin, project: &str, observed: Observed<'_>) -> Result<()> {\n    let signer = signer_of(observed.scheme, observed.key_id);\n    let mut problems = Vec::new();\n    if pin.scheme != observed.scheme || pin.signer != signer {\n        problems.push(format!(\n            \"is signed by {signer} ({}), but {} ({}) signed what mise accepted before\",\n            observed.scheme, pin.signer, pin.scheme\n        ));\n    }\n    if pin.attested_by == \"vendor\" && observed.attested_by == \"repackager\" {\n        problems.push(\n            \"is attested by a repackager, but the vendor's own packslip was accepted before\".into(),\n        );\n    }\n    if pin.provenance && !observed.provenance {\n        problems.push(\"drops the build provenance every artifact linked before\".into());\n    }\n    if !problems.is_empty() {\n        bail!(\n            \"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.\",\n            problems.join(\", and \")\n        );\n    }\n    Ok(())\n}\n\n/// Set the project's pin from an accepted release, or strengthen it: what\n/// got stronger is remembered, what stayed the same is left alone. Checks\n/// again under the lock, since the file may have changed since [`check`].\npub(crate) fn record(project: &str, observed: Observed<'_>) -> Result<Pin> {\n    record_at(&pins_file(), project, observed)\n}\n\npub(crate) fn record_at(path: &Path, project: &str, observed: Observed<'_>) -> Result<Pin> {\n    let _lock = locked(path)?;\n    let mut pins = load(path)?;\n    let signer = signer_of(observed.scheme, observed.key_id);","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/packslip_pins.rs#L135-L171","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the vendor actually announced a signing/provenance change; check the project's release notes before acting.","If legitimate, reset the pin: `mise packslip forget <project>`, then install again — the next accepted release sets a fresh pin.","If not announced, do not reset the pin; treat the release as potentially compromised and verify artifacts out-of-band.","Compare the reported signer/scheme in the message with the vendor's documented signing identity (e.g. expected workflow OIDC identity)."],"exampleFix":"// before: vendor rotated keys, pin refuses the release\nmise install gh  # error: signed by new-workflow, but old-workflow signed before\n// after confirming the rotation is legitimate\nmise packslip forget gh && mise install gh  # new pin set from accepted release","handlingStrategy":"validation","validationCode":"let pinned = mise_packslip_pin(project)?; // signer/scheme/provenance\nlet observed = observe_release(release)?;\nif pinned.signer != signer_of(&observed.scheme, &observed.key_id)\n    || (pinned.provenance && !observed.provenance) {\n    eprintln!(\"release would downgrade pin for {project}; investigate before forgetting the pin\");\n}","typeGuard":"fn is_downgrade(pin: &Pin, observed: &Observed) -> bool {\n    pin.scheme != observed.scheme\n        || pin.signer != signer_of(observed.scheme, observed.key_id)\n        || (pin.attested_by == \"vendor\" && observed.attested_by == \"repackager\")\n        || (pin.provenance && !observed.provenance)\n}","tryCatchPattern":"match install(project) {\n    Err(e) if e.to_string().starts_with(\"packslip:\") => {\n        // refuse to auto-reset; require explicit human decision\n        eprintln!(\"{e}\");\n        Err(e)\n    }\n    other => other,\n}","preventionTips":["Treat this error as a potential supply-chain signal; never reflexively run `packslip forget`.","Confirm any signer/key rotation against the vendor's official announcements first.","Record expected signing identities per project and audit them when pins are first set.","Keep mise updated so provenance and attestation checks use current verification logic."],"tags":["security","packslip","supply-chain","signing","pin-verification"],"backgroundTag":"checksum-mismatch","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}