diff --git a/PLAN.md b/PLAN.md index 16ceddb..d6206c3 100644 --- a/PLAN.md +++ b/PLAN.md @@ -372,6 +372,32 @@ something long in one, switch away and back, refresh — it's all still there. a thin line and a name strip on the first level — instead of nested boxes with headers, which on Linux's single-child folder chains was a staircase of headers around nothing. Not done: delete from here, a size-over-time history, per-user column choices. +- **Updater** — **shipped 2026-09-23** (`internal/selfupdate`, `internal/api/update.go`, + the Updates card in `Settings.tsx`, `scripts/gitea-release.sh`). A Ham Lab asks one URL + what the newest build is and installs it on request. `update.url` in `config.json` is + either a Gitea/Forgejo repo API base (`…/api/v1/repos//`, whose newest + release names the version and carries the binaries as assets) or any static directory + holding `VERSION`, `SHA256SUMS` and `hamlab-linux-` — which is exactly what + `make release` writes and what `install.sh` already downloads, so the demo site, a + folder on a NAS and a Gitea release are all the same thing to it. `"off"` never looks. + `update.token` is sent when the source is behind a login, so a **private** Gitea serves + its own fleet without the instance being opened to the world. The binary is verified + against `SHA256SUMS` before it goes anywhere near the running one, then takes the same + path `POST /api/self-update` always did (runs `version`, runs `check` against this + machine's config, atomic replace keeping `hamlab.prev`, re-exec in place) — that path is + now one shared method, `installBinary`. Version ordering is `install.sh`'s `vnum()` rule + ported to Go and tested against it, so the daemon and the installer never disagree about + which of two builds is newer; the commit count since the tag counts, the hash does not. + Checked ~90 s after boot and daily (`update.check: "never"` switches that off), never + installed unasked — an update restarts the daemon and drops open terminals, and the + dialog says so. Settings → **Updates**: installed, published, when it last looked, the + source, the release notes, Check for updates, Install; the rail badges Settings when a + newer build is out (`update_available` rides on `/api/me`). Publishing is + `make gitea-release` → `scripts/gitea-release.sh`, which creates the release and uploads + the five architectures, `SHA256SUMS`, `install.sh` and `VERSION`; it is deliberately + **not** part of `make deploy`, because deploy pushes this build to the machines this hub + owns while publishing offers it to every install anywhere, and one tag per deploy would + be noise. Re-publishing a version needs `--force`. - **Auth hardening** — WebAuthn passkeys (phone Face ID), TOTP fallback, optional NPM access list restricting to WireGuard/LAN. - **Files tab** — **shipped 2026-09-18** (`internal/files`, `Files.tsx`): browse from @@ -400,32 +426,27 @@ something long in one, switch away and back, refresh — it's all still there. ## To do -- **A public download page, and an updater that uses it** — asked for 2026-09-22, not - started. Today a release is *published* but not *offered*: `make publish` writes five - architectures, `SHA256SUMS`, `VERSION` and `install.sh` into - `/home/core/apps/hamlab-demo/site/dl/` (public at `https://hamlab-demo.bigham.dev`), and - the only way in is `curl -fsSL https://hamlab-demo.bigham.dev/install.sh | sh`. Updating - an existing install is a *hub* pushing its own binary to machines it manages - (`POST /api/self-update`, `hamlab push-nodes`, the Update buttons on the Machines page) — - which does nothing for an install that no hub of mine owns. Two halves: - - **The page.** A human-facing page at the download site: current version and when it was - built, a row per architecture with its size and checksum, the one-line install, and how - to update. Today `/dl/` is a bare directory listing. It should be generated by - `make publish` from the same VERSION and SHA256SUMS the release already writes, so it - cannot drift from the binaries beside it, and it should say plainly that the binaries - are unsigned and served over TLS from my own box. - - **The updater.** A Ham Lab anywhere asks the download site what the current version is, - says so in the UI when it is behind (the About card, and probably a rail badge like - Alerts has), and updates itself on request: fetch the binary for its own architecture, - check it against `SHA256SUMS`, then the same path `POST /api/self-update` already - takes — verify it runs, install atomically, re-exec in place. Decisions to make when it - is built: where the check lives (a `update_url` in `config.json`, defaulting to the - public site, so someone else's install can point elsewhere or switch it off); how often - it asks (daily is plenty, and it must be possible to say never); whether it ever updates - unattended (default no — an update restarts the daemon and drops open terminals); and - whether a hub offers to update its machines from the site when it is itself behind. - Downgrades are already refused by the installer's `vnum()` comparison; the updater - should use the same rule. +- **Somewhere public to download from** — asked for 2026-09-22, half done. The **updater** + shipped 2026-09-23 (above) and reads releases from Gitea, but `git.bigham.dev` runs with + `REQUIRE_SIGNIN_VIEW=true`, so those releases are reachable only with a token. That is + fine for this fleet — each machine carries a read-only `read:repository` token — and no + use at all to a stranger running the `install.sh` one-liner, who still gets the rolling + copy on `hamlab-demo.bigham.dev`. What is left is deciding where a public download + actually lives: + - **Lower the wall on Gitea.** `REQUIRE_SIGNIN_VIEW=false` and per-repo visibility does + the rest: every repo there is private and stays hidden, and only a repo marked public + is visible. That is how Gitea is meant to be configured, and it is the one change that + makes "releases on my Gitea" literally true. It is an instance-wide posture change on + a public hostname, so it is Mitchell's call — the same trade was declined once before + (`docs/docker.md`, the demo site's `install.sh` is hosted outside Gitea for exactly + this reason). + - **Or keep the wall and keep the demo site** as the public face: `make publish` already + writes the rolling `dl/` directory there, and a generated index page (version, built + when, a row per architecture with size and checksum, the one-line install, and that + the binaries are unsigned and served from his own box) would finish the "page" half + without touching Gitea at all. + - Either way the release notes and version history live on Gitea, and the updater does + not care which of the two it is pointed at. - **Deprecate copyparty** — asked for 2026-09-23, not started. `copyparty` on `grid` (`copyparty.service`, `:3923`, `/etc/copyparty.conf`, serving all of `/tank` as user