Skip to content

feat: update notifier and asobi upgrade self-update#37

Merged
Taure merged 3 commits into
mainfrom
feat/self-update
Jul 14, 2026
Merged

feat: update notifier and asobi upgrade self-update#37
Taure merged 3 commits into
mainfrom
feat/self-update

Conversation

@Taure

@Taure Taure commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Adds the two-phase self-update flow requested after cutting v0.2.0.

Phase A — update notifier

After a command runs, prints a one-line stderr notice when a newer release exists:

A new asobi is available: v0.2.0 (you have 0.1.0).
Upgrade: asobi upgrade   (or: curl -fsSL https://raw.githubusercontent.com/widgrensit/asobi-cli/main/install.sh | sh)

Best-effort and non-blocking:

  • Consults ~/.asobi/version_check.json, refreshes at most once/24h (avoids the 60/hr unauthenticated GitHub API limit).
  • Stays silent on any error, ASOBI_NO_UPDATE_CHECK, in CI, for dev builds, and for version/upgrade/help.
  • Never blocks or fails a command.

Phase B — asobi upgrade

Downloads the platform asset, verifies it against the release checksums.txt (fail closed), and atomically replaces the running binary.

  • SHA-256 must match or the upgrade is refused (verified with a tamper test + a missing-entry test).
  • 64 MB download cap; HTTPS; extracts only the asobi binary.
  • Atomic replace via temp-in-same-dir + rename (Windows moves the running image aside first).
  • Refuses package-manager-managed installs (Homebrew/Scoop/Nix) and read-only locations, pointing at the right tool.

Verification

  • Unit tests: semver compare, asset selection, checksum verify (valid/tamper/missing), tar.gz + zip extraction, atomic replace, PM detection, and a download→verify→extract pipeline via httptest.
  • Live end-to-end against the real v0.2.0 release: notifier fires for a 0.1.0 build; opt-out/CI/version paths silent; a 0.1.0 binary self-upgraded to 0.2.0 after a real checksum-verified download.
  • gofmt/go vet/go test ./... clean.

Security note / follow-up

Trust anchor is checksums.txt over HTTPS from the GitHub release — same model as install.sh. Releases are not signed, so this doesn't defend against a GitHub/CDN compromise. Worth a follow-up to sign releases (cosign) and verify signatures here. A security-focused review of the download/replace path is welcome before merge.

Taure added 3 commits July 14, 2026 01:15
Phase A - notifier: after a command runs, print a one-line stderr notice
when a newer release exists. Best-effort and non-blocking - consults a
~/.asobi/version_check.json cache (refreshed at most once/24h), stays silent
on any error, on ASOBI_NO_UPDATE_CHECK, in CI, for dev builds, and for the
version/upgrade/help commands.

Phase B - `asobi upgrade`: download the release asset for this platform,
verify it against the release checksums (fail closed), and atomically
replace the running binary. Refuses package-manager-managed installs
(Homebrew/Scoop/Nix) and read-only locations, pointing at the right tool.

Verified end to end against the live v0.2.0 release: notifier fires for an
older build, opt-out/CI/version paths stay silent, and a 0.1.0 binary
self-upgrades to 0.2.0 after a real checksum-verified download.
From an adversarial review of the download/verify/replace path:

- fail closed on an oversized extracted binary instead of silently
  truncating it (the checksum covers the archive, not the extracted
  binary), via readCapped in both tar.gz and zip paths.
- refuse redirects to non-HTTPS URLs (no on-path downgrade to plaintext).
- validate the release tag as semver before trusting it in a notice or
  interpolating it into a download URL; ignore a non-semver cached value.
- on Windows, tell the user where the original binary is if rollback fails.

Adds tests for oversized-extract rejection, HTTP-downgrade refusal,
semver validation, and garbage-tag rejection. Live upgrade re-verified.
`asobi upgrade` now downloads checksums.txt.sig and verifies it against an
embedded ed25519 public key (Go stdlib crypto/ed25519, no new dependency)
before trusting checksums.txt. Chain of trust: signature -> checksums.txt
-> asset, rooted in the embedded key, so a GitHub or CDN compromise alone
can no longer forge an upgrade. Fails closed if the signature is missing,
malformed, or does not verify.

goreleaser signs checksums.txt via scripts/sign-checksums.sh using the
ASOBI_SIGNING_KEY secret (a PEM ed25519 private key). Verified end to end
with a local snapshot build: the produced signature verifies against the
embedded public key and tampering is rejected.
@Taure
Taure merged commit b91619f into main Jul 14, 2026
1 check passed
@Taure
Taure deleted the feat/self-update branch July 14, 2026 06:11
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