Skip to content

feat(self-update): verify packslips against the mise repository ID - #13785

Merged
jdx merged 4 commits into
mainfrom
codex/packslip-self-update
Sep 28, 2026
Merged

jdx merged 4 commits into
mainfrom
codex/packslip-self-update

Conversation

@jdx

@jdx jdx commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

https://entire.io/gh/jdx/mise/trails/314

mise self-update now verifies the release's signed packslip before replacing the executable. Releases from v2026.9.3 onward must provide a valid manifest: verification checks the archive digest and size, the selected version, the release workflow, and transparency-log evidence. The existing embedded archive signature remains required too.

The trust anchor is mise's immutable GitHub repository ID, 586920414, checked against the signing certificate. The same repository can move between organizations or change its name; a different repository taking over its former name is rejected. Custom mirrors must preserve the original signed manifest and archives rather than supplying their own signer.

The minimum release age also applies to the verified log timestamp, including automatic updates. Explicit version pins bypass the delay. Older releases without a packslip remain installable with the existing signature verification. Missing or invalid manifests for modern releases fail without replacing mise; download progress is retained.

Adds real replacement coverage on Linux, macOS, and Windows: a shared integration test starts each case from a disposable copy of the CI build, installs current, first-packslip, and legacy releases, checks the binary changed, and executes the result. The CI binary itself remains untouched. Offline tests verify a published mise manifest and cover tampering, version/age mismatches, repository renames/transfers, wrong or missing repository IDs, and the legacy boundary.

Stacked on #13783 (native updater), which is stacked on #13782 (release-age policy).

Validation: 26 focused self-update tests, strict workspace/all-feature/all-target Clippy, lint, build, and macOS end-to-end tests for real replacement and release-age selection passed. Linux and Windows replacement runs are wired into CI. The test workflow also runs for stacked PRs targeting codex/** branches.

AI-assisted β€” Tool: Codex; model: openai/unavailable; version: unavailable.


Note

High Risk
Changes the self-update trust path and binary replacement gate for all users on modern releases; misconfiguration or mirror issues could block updates, though failures are designed not to replace the running binary.

Overview
mise self-update now requires a valid packslip (packslip.sigstore.json) for releases v2026.9.3+, in addition to the existing zipsign archive check. Before replacing the running binary, it verifies the manifest against the downloaded archive, the selected version, the release workflow signer, and the Sigstore transparency-log timestamp. Trust is pinned to GitHub repository ID 586920414, so renames/transfers of the real repo still work while a name-squatter cannot substitute releases. Missing or invalid manifests for modern releases abort without touching the install; 2026.9.2 and older can still update with signature-only verification.

Minimum release age now uses the verified log time for unpinned and automatic updates (run_with_age_policy); pinning an explicit version skips that cutoff. Windows mise-shim refresh runs the same packslip check on the release zip.

Testing & CI: shared e2e/fixtures/self_update.py exercises real GitHub downloads against disposable copies (packslip, first packslip, and legacy releases) on Linux/macOS e2e and Windows Pester; macOS unit job runs cli/test_self_update_install. PR workflows also run for codex/** branches. Docs describe packslip behavior and mirror requirements.

Reviewed by Cursor Bugbot for commit 8da58f1. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features
    • Self-updates now verify release manifests and signatures for supported releases before replacing the installed version. Automatic updates also enforce the configured minimum release age; installing a specific version bypasses that delay.
    • Invalid or missing required release verification data prevents an update from replacing mise.
  • Documentation
    • Updated the installation guide with release verification details, minimum-age behavior, and mirror requirements.

@coderabbitai

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack β†’

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

βš™οΈ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Advanced

Run ID: 8e72e691-6817-4a09-949e-a052d609af84

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 78bae8a and 5a44da1.

πŸ“’ Files selected for processing (10)
  • .github/workflows/test-impl.yml
  • .github/workflows/test.yml
  • docs/installing-mise.md
  • e2e-win/self_update_install.Tests.ps1
  • e2e/cli/test_self_update_install
  • e2e/fixtures/self_update.py
  • src/cli/self_update.rs
  • src/cli/self_update/packslip.rs
  • test/fixtures/self-update/mise-v2026.9.16.sigstore.json
  • test/fixtures/self-update/mise-v2026.9.3.sigstore.json

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands.

@jdx
jdx force-pushed the codex/packslip-self-update branch from 1d1f093 to 6b7afeb Compare September 28, 2026 15:27
@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Critical risk] Adds cryptographic verification to the self-update mechanism.

The PR appears safe to merge based on the current review.

Summary

This PR adds signed-packslip verification to self-update while retaining archive-signature verification.

  • Requires packslips for releases from v2026.9.3 and checks repository identity, release workflow, archive, version, and transparency-log evidence.
  • Adds replacement tests and documents the verification and mirror requirements.

Reviews (3) Β· Last reviewed commit: "ci: name the self-update integration tes..."

Comment thread .github/workflows/test-impl.yml Resolved
@jdx
jdx force-pushed the codex/packslip-self-update branch from 6b7afeb to 5a44da1 Compare September 28, 2026 15:39
@jdx
jdx added this pull request to stack #13784 September 28, 2026 17:37
Base automatically changed from codex/native-self-update to main September 28, 2026 17:43
@jdx
jdx force-pushed the codex/packslip-self-update branch from 5a44da1 to 8da58f1 Compare September 28, 2026 17:43
@jdx
jdx merged commit fba28ac into main Sep 28, 2026
35 of 36 checks passed
@jdx
jdx deleted the codex/packslip-self-update branch September 28, 2026 17:43
jdx added a commit that referenced this pull request Sep 28, 2026
<!-- entire-trail-link-start -->
https://entire.io/gh/jdx/mise/trails/315
<!-- entire-trail-link-end -->

Stacked PRs targeting a feature branch can leave `release` and
`release-window` unreported because both workflows only accept PRs
targeting `main`. Remove the base-branch filters so those checks also
report for stacked PRs, including #13785.

Ordinary feature PRs still skip release builds and receive
`release-window: success`. The release-build gate now explicitly
requires a same-repository `release` PR targeting `main`, preserving the
restriction previously supplied by the trigger filter. The auto-merge
build gate, quiet-hours policy, and shared-release-commit protection
remain unchanged. Existing PRs need a fresh synchronize or reopen event
after the updated workflows are available to populate missing statuses.

Validated with `mise run lint-fix`, the CI-equivalent high-severity
zizmor scan, and local shell checks covering ordinary PRs, armed/unarmed
release builds, an armed `release` PR targeting a feature branch, quiet
hours, fork branches named `release`, and shared release commits.

*AI-assisted β€” Tool: Codex; model: openai/unavailable; version:
unavailable.*

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes release CI triggers and gating logic; mistaken gate conditions
could skip checks or trigger unintended release runs, though behavior
for real release PRs is intentionally preserved.
> 
> **Overview**
> **Release and release-window workflows** now run on pull requests
against **any base branch**, so stacked PRs (e.g. targeting a feature
branch) get the required `release` and `release-window` check statuses
instead of leaving them missing.
> 
> The **`release` workflow gate** adds an explicit `github.base_ref ==
'main'` requirement alongside the existing same-repo `release` head
branch rule, so expensive tarball builds and publish paths still only
run for **release β†’ main** PRsβ€”the behavior that used to come from the
`branches: ["main"]` trigger filter. Comments in both workflows document
the stacked-PR motivation.
> 
> **`release-window.yml`** drops its `pull_request_target` `branches:
["main"]` filter only; quiet-hours and release-commit status logic are
unchanged per the PR description.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2af19a6. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Release-window checks now run for pull requests targeting any branch.
* The release workflow accepts pull requests targeting any branch, but
its release gate still requires pull requests to target `main` and
originate from the `release` branch. Non-pull-request runs remain
eligible.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
jdx pushed a commit that referenced this pull request Sep 29, 2026
<!-- entire-trail-link-start -->
https://entire.io/gh/jdx/mise/trails/302
<!-- entire-trail-link-end -->

### πŸš€ Features

- **(config)** support global local miserc overrides by @jdx in
[#13778](#13778)
- **(config)** auto-install unconfigured registry bins on command not
found by @jdx in [#13781](#13781)
- **(dotfiles)** identify history commits by machine by @jdx in
[#13791](#13791)
- **(install)** allow tool postinstall on every install by @jdx in
[#13789](#13789)
- **(lock)** warn when old lockfile formats are overdue for upgrade by
@jdx in [#13779](#13779)
- **(self-update)** apply minimum release age to mise updates and
installs by @jdx in [#13782](#13782)
- **(self-update)** verify packslips against the mise repository ID by
@jdx in [#13785](#13785)

### πŸ› Bug Fixes

- **(backend)** preserve Go install errors for resolved versions by @jdx
in [#13794](#13794)
- **(brew)** prepend the shim's `Pathname#write` override by
@jacobbednarz in [#13760](#13760)
- **(brew)** define Homebrew's Language::* mixins in both shims by
@waynehoover in [#13328](#13328)
- **(brew)** unpack source archives whose URL names no extension by
@jacobbednarz in [#13750](#13750)
- **(config)** keep edited configs untrusted with --yes in paranoid mode
by @jdx in [#13796](#13796)
- **(npm)** pass minimum release age in kebab-case for pnpm 12 by
@Nagato-Yuzuru in [#13764](#13764)
- **(upgrade)** bump same-prefix exact releases by @ryoikarashi in
[#13759](#13759)

### 🚜 Refactor

- **(self-update)** use native downloads and signature verification by
@jdx in [#13783](#13783)

### πŸ“š Documentation

- replace the theme song with the mise run music video by @jdx in
[#13799](#13799)

### πŸ§ͺ Testing

- **(dotfiles)** fix flaky timeout in retrack prompt test by @jdx in
[#13772](#13772)
- **(pipx)** exercise extras with a smaller CLI fixture by @jdx in
[#13775](#13775)
- **(upgrade)** cover --bump keeping short versions short by @jdx in
[#13773](#13773)

### πŸ“¦οΈ Dependency Updates

- refresh markdownlint-cli's locked dependencies by @jdx in
[#13769](#13769)
- update gix to 0.88 by @jdx in
[#13771](#13771)
- lock file maintenance by @renovate[bot] in
[#13767](#13767)
- update jdx crates to v6.12.0 by @renovate[bot] in
[#13790](#13790)

### πŸ“¦ Registry

- prefer signed packslips for timoni and worktrunk by @jdx in
[#13780](#13780)

### Chore

- **(entire)** store mise checkpoints in a private repository by @jdx in
[e5c1488](e5c1488)
- **(entire)** commit codex session hooks by @jdx in
[7300964](7300964)
- **(entire)** commit claude session hooks by @jdx in
[ad720ea](ad720ea)
- **(entire)** restore lower-cost trail findings by @jdx in
[b979550](b979550)

### Ci

- enable mr-boxington caching for unit-macos by @jdx in
[#13776](#13776)
- report release checks for stacked pull requests by @jdx in
[#13786](#13786)
- allow publishing docs via workflow_dispatch by @jdx in
[#13800](#13800)

### New Contributors

- @ryoikarashi made their first contribution in
[#13759](#13759)

## πŸ“¦ Aqua Registry Updates

### New Packages (1)

- [`jdx/packslip`](https://github.com/jdx/packslip)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant