Files
kandev/docs/decisions/0012-service-only-self-update.md
T
José Almeida 041a19f060 feat: service-managed UI self-update (#1210)
* feat: add service-managed self-update

* chore: add temporary self-update test scripts

* chore: simplify temporary script markdown fences

* chore: run frontend in temporary self-update scripts

* fix: show self-update job progress after apply

* chore: add real npm self-update test script

* fix: isolate npm self-update prefix

* fix: detect npm service installs from bin shim

* chore: reset service before real npm update test

* chore: expand real npm status diagnostics

* fix: preserve service home during self-update

* fix(cli): tee self-update helper output to a log file

* fix(cli): avoid launchd bootstrap race when reloading the macOS service

* fix(backend): run macOS self-update helper once via transient launchd plist

* fix(web): show self-update progress and lock the apply button until the update lands

* fix(cli): reinstall via the kandev wrapper on Homebrew self-update

* chore: remove temp self-update test scripts

* fix(web): drop unused apply path from useUpdates hook

* fix(backend): snapshot install state once in apply preflight

* docs: align self-update docs with transient LaunchAgent flow

* chore(release): run cli bundle from apps workspace root

* fix(backend): guard against concurrent self-update applies

* fix(backend): check scheme and helper paths in self-update apply

* fix(backend): drop duplicate binary name from npx manual command

* fix(cli): allow --help for service self-update without --intent

* fix(cli): harden self-update dir perms and darwin uid resolution

* fix(web): set required POST fields last in applyUpdate

* docs: use JSON body notation for the apply endpoint

* fix(backend): expire self-update guard so a stuck helper cannot wedge apply

* refactor(cli): collapse redundant writeSync branch in self-update log
2026-06-01 17:49:15 +01:00

2.3 KiB

0012: Service-only UI self-update

Status: accepted Date: 2026-05-29 Area: backend, frontend, cli

Context

Kandev can be installed through npm/npx or Homebrew and can also run interactively from a shell. Updating a running interactive process from the UI is brittle: the process may not have a stable service manager, restart semantics, or a durable place to hand work off before the backend exits.

The service installer already owns the OS-specific restart contract for systemd and launchd. That makes it the right boundary for UI-driven self-update.

Decision

UI self-update is supported only when the backend proves all of these are true:

  • It is running under kandev service install.
  • The unit/plist contains kandev's managed marker and KANDEV_SERVICE_* environment.
  • <KANDEV_HOME_DIR>/service/install.json matches the running service manager, mode, install kind, and service file.
  • The service is user-mode, not --system.
  • The install kind is homebrew, npm, or npx.

The backend returns this state from GET /api/v1/system/updates. The frontend renders Apply update only when the backend reports apply_supported=true; otherwise it shows manual update commands.

POST /api/v1/system/updates/apply writes an intent file and starts a helper outside the running service's lifetime:

  • Linux user services use systemd-run --user --collect.
  • macOS user agents use a one-shot transient LaunchAgent plist (RunAtLoad=true, KeepAlive=false) bootstrapped via launchctl bootstrap. (launchctl submit was rejected: its implicit KeepAlive re-ran the one-shot helper in a loop.)
  • E2E/dev tests can fake the helper with KANDEV_E2E_MOCK=true.

The CLI owns the helper planner through a hidden kandev service self-update --intent <path> command. It upgrades the package via Homebrew, npm, or npx, re-runs kandev service install, then restarts the service.

Consequences

  • The UI never offers a destructive update button for an unknown install.
  • System services remain explicit terminal operations because they need privilege and differ by host policy.
  • Tests can cover the backend, CLI planner, and UI gate without mutating a developer machine.
  • The service installer must keep metadata and KANDEV_SERVICE_* env vars current whenever it rewrites a unit/plist.