Skip to content

feat: clearer runtime vs CLI updates, default checkpoint path, self-update - #10

Merged
anhthii merged 1 commit into
mainfrom
feat/update-dx
Nov 10, 2026
Merged

anhthii merged 1 commit into
mainfrom
feat/update-dx

Conversation

@Azzurriii

Copy link
Copy Markdown
Collaborator

Why

  • update --backup-out was mandatory and had to be outside the instance. With --home defaulting to ., a path like ./backups/ was always rejected.
  • update only updates the runtime (container image), but nothing said so: not the help text, not the Update available: v1.2.3 → v1.3.0 notice, and not version, which printed only the CLI version. There was also no way to update the CLI apart from rerunning install.sh.

What changes

Checkpoint location

  • Defaults to <home>/backups/pre-update-<from>-to-<to>-<UTC time>.enc. COSIGNER_BACKUP_DIR or --backup-out override it.
  • Inside an instance, only backups/ may hold archives. Snapshots only read config, settings, state/ and secrets/, so archives are never captured into later ones. Symlinks are resolved first, so a backups/ link into state/ is refused.
  • The path is checked before anything is pulled. When an archive lands on the instance's disk, a warning says to copy it off-host.
  • backup --out is now optional, with the same default.
  • restore accepts a target directory that contains only backups/.

Runtime vs CLI

  • Help, progress messages and notices say "runtime". Notices are separate: Runtime update available … (update) and cosigner-cli update available … (self-update).
  • version prints both components. --check looks up the newest release of each, and --json is available. The first line is unchanged because install.sh parses it.
  • New self-update command. It downloads the platform binary and runs cosign verify-blob against this repo's release workflow for that tag, the same identity install.sh checks. It runs even when COSIGNER_SKIP_IMAGE_SIGNATURE_VERIFICATION=1 is set. Then it smoke-tests that version reports the expected tag, and only then renames the binary into place. On a root-owned directory, the error suggests running it with sudo.
  • A runtime whose contract is newer than the CLI supports now fails with runtime vX needs a newer cosigner-cli and a hint to run self-update. The existing contract fields cover this, so the runtime repo needs no change.

Update flow

  • Before stopping anything, update prints the plan: installed and candidate versions with digests, the verification result, the storage schema, the checkpoint path and the expected downtime.
  • It then asks for confirmation. It prints the restore command for the checkpoint after it succeeds, and on failure as well.

For scripts and agents

  • --json on update, self-update, version and doctor. The JSON goes to stdout and progress goes to stderr.
  • update --check and self-update --check exit with status 10 when an update exists.
  • Errors can carry a hint: line naming the next command or variable. Missing-password errors now name the right variable: the recovery password used to report COSIGNER_PASSWORD.
  • cosigner-cli help environment lists every COSIGNER_* variable.

⚠️ Behaviour change

Without a terminal, update now needs --yes. That's the same convention as callback remove. Scripts that run update --backup-out … unattended must add --yes.

Testing

  • make test (vet + race) passes. New tests are in update_test.go and selfupdate_test.go. They cover:
    • archive path rules, including the symlink case
    • the default checkpoint, then a restore from it into a directory that holds only backups/
    • --yes being required without a terminal
    • --check exit codes and --json output
    • the self-update happy path, a bad signature, a wrong version, the skip variable having no effect, and dev builds being refused
  • Checked by hand on a locally built binary: --help, version, version --json, update --help, and self-update --check against the real GitHub API.
  • Not tested end to end: a real self-update (it needs a published release newer than the binary) and a real runtime update against Docker.

🤖 Generated with Claude Code

…pdate

- update: checkpoint defaults to <home>/backups/ (or COSIGNER_BACKUP_DIR);
  backups/ is the only folder inside an instance that may hold archives
- update: shows the plan and asks before stopping Cosigner (--yes without a
  terminal), prints the restore command afterwards, adds --check (exit 10)
  and --json
- name the component everywhere: runtime update vs cosigner-cli update
  notices, version prints both (--check, --json)
- self-update: cosign-verified, atomic replacement of the CLI binary
- runtime needing a newer CLI points at self-update; errors carry hint: lines
- backup --out is optional; restore accepts a directory holding backups/
- doctor --json, help environment

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anhthii
anhthii self-requested a review November 10, 2026 10:41
@anhthii
anhthii merged commit 9da6d71 into main Nov 10, 2026
5 of 6 checks passed
@anhthii
anhthii deleted the feat/update-dx branch November 10, 2026 10:42
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.

2 participants