Make basecamp upgrade a real upgrade: native, verified self-update - #615
Conversation
Sensitive Change Detection (shadow mode)This PR modifies control-plane files:
|
cd479d9 to
27e616e
Compare
27e616e to
d0a39c3
Compare
d0a39c3 to
ecb7e83
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ecb7e8356f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
ecb7e83 to
f3a039a
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f3a039a2b5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
f3a039a to
93bd049
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 93bd04993a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
basecamp upgrade previously printed a release URL with ok:true for every install method it couldn't handle, and reported brew/scoop success on child exit code alone. Now: - Installer-script/tarball installs under the user's home self-update: download the platform asset, verify authenticity via sigstore-go (identity pinned to the release workflow at the version tag, SCT + tlog + observer thresholds, artifact-bound) and integrity via sha256, swap the executable transactionally (target path continuously occupied on unix; rename shuffle with rollback on Windows), and confirm the installed binary reports the new version. Backup restored on post-verify failure; sidecars reaped on every CLI invocation. - Fail-closed path policy: root, /nix/store, outside-home (Rel-based, no prefix matching), unwritable dir, and go-install provenance (ldflags vs debug.ReadBuildInfo) all refuse with method-specific hints. - Exit contract: exit 0 only for up_to_date/dev or applied-and- confirmed upgrades. upgrade_required / upgrade_incomplete / upgrade_unverified / upgrade_failed are ok:false and nonzero; legacy cask/manifest migration is now an error, not a success. - Brew/scoop delegation post-verifies via manager-derived paths (brew --prefix, scoop prefix), never os.Executable. - Installer cosign capability gate: v3 bare, v2.6+ needs --new-bundle-format=true, older warns and skips (checksums still verified). goreleaser pins the published bundle format explicitly. - Real published v0.8.1 checksums + protobuf bundle + TUF trusted root vendored as hermetic verification fixtures. - New 3-OS upgrade-smoke workflow exercises the full flow against the real latest release, plus install.sh/ps1 cosign tier legs.
93bd049 to
4df2e48
Compare
Problem
basecamp upgradewas a dispatcher, not an installer. For every install method except Homebrew/Scoop — including the front-pagecurl | bashinstall — it printed a release URL with"ok": trueand exit 0, which users (and agents) read as a successful upgrade. The brew/scoop paths reported "Upgraded" purely on child exit code (brew upgradeexits 0 on no-ops). Closes the loop on the #589 lineage.What this does
Native self-update for installer-script/tarball installs under the user's home:
release.yml@refs/tags/v<ver>+ GitHub OIDC issuer, SCT + transparency-log + observer-timestamp thresholds, artifact-bound to the actualchecksums.txtbytes; trusted root via TUF) and integrity via SHA-256..upgrade.lock(gofrs/flock) held across staging/replace/verify/rollback serializes concurrent upgrades and gates the sidecar cleanup that runs on every ordinary invocation./nix/store, outside-home (filepath.Rel, no prefix matching), unwritable dir, andgo installprovenance (ldflags vsdebug.ReadBuildInfo) all refuse with method-specific hints.Success/exit contract — exit 0 only for
up_to_date/devor applied-and-confirmed upgrades. Everything else isok:false+ nonzero:upgrade_required,upgrade_incomplete,upgrade_unverified,upgrade_failed. Legacy cask/manifest migration is now an error, not a success.Brew/Scoop delegation stays but post-verifies via manager-derived paths (
brew --prefix,scoop prefix) — neveros.Executable.Installer cosign compatibility: current releases publish the protobuf (v0.3+json) bundle, which cosign v2.x fails to verify bare. Both installers now gate on the cosign version: v3 bare, v2.6–v2.x adds
--new-bundle-format=true, older (or broken/unparseable) warns and skips — never aborts the install. The floor is empirical: v2.4 fails on the bundle'sPKIX_ED25519tlog key. goreleaser pins--new-bundle-format=trueso the published format no longer depends on the CI cosign default.Testing
checksums.txt+ bundle + TUF trusted root are vendored as fixtures: hermetic tests prove the exact verification policy against reality (and that tampered artifacts, wrong identities, and garbage bundles fail).upgrade-smoke.yml: 3-OS matrix builds a stale 0.0.1 binary under the runner's home and runs a real upgrade against the latest release (real download, real sigstore verification, real Windows locked-exe shuffle), plus install.sh/ps1 legs with digest-pinned cosign v3.0.5 / v2.6.0 / v2.4.0.bin/cigreen end-to-end.Note: the release-shape (stripped) binary grows 25.6 MB → 35.3 MB (+9.7 MB / 38%); gzip-compressed binary 8.3 MB → 11.6 MB (+3.2 MB / 39%, a proxy for archive size) — from the sigstore-go/TUF dependency tree. Accepted; a release size budget (38 MiB stripped binary / 13 MiB gzipped binary, increases need explicit review) is recorded in RELEASING.md.
Summary by cubic
Make
basecamp upgradea real, verified self-update that only exits 0 after the new version is installed and confirmed. Adds Sigstore verification, transactional swaps across OSes, startup cleanup of upgrade sidecars, and secure GitHub release lookups with strict token handling.New Features
github.com/sigstore/sigstore-go(identity pinned to the release workflow/tag; trusted via TUF). Release lookups attach a GitHub token only for api.github.com, withGH_TOKENpreferred overGITHUB_TOKEN.go installbuilds refuse with guidance). Homebrew/Scoop are delegated then verified via manager-derived prefixes (brew --prefix,scoop prefix).cosigncompatibility gate (v3 bare, v2.6+ adds--new-bundle-format=true, older warn/skip). New 3-OSupgrade-smoke.ymland hermetic fixtures validate the full flow. README and install docs explain upgrade behavior.Dependencies
github.com/sigstore/sigstore-go v1.3.0; pin--new-bundle-format=trueingoreleaser.Written for commit 4df2e48. Summary will update on new commits.