# Auto-update for haul — plan Status: **planned, not started.** Blocked on four decisions (bottom of this file). No code has been written. The model is the self-updater in [ShippingTracker](https://gitea.apointless.space/bsncubed/ShippingTracker) (`HelloWorld/Updater.vb`), which haul should behave like. ## Research already done — don't redo this Verified 2026-07-29 against the live gitea instance: - `https://gitea.apointless.space/api/v1/repos/bsncubed/ShippingTracker/releases/latest` returns **HTTP 200 anonymously**. No token needed. - `.../repos/bsncubed/haul` also returns **200 anonymously**, and `.../repos/bsncubed/haul/releases` returns `[]`. So haul's repo is public; the 404 on `releases/latest` is simply "no releases exist yet", not a permissions problem. The updater will work unauthenticated. - Release JSON exposes `tag_name`, `draft`, `prerelease`, and an `assets` array whose entries carry `name` and `browser_download_url`. ShippingTracker's latest (`1.1.0.2`) attaches exactly two assets: `Shipping Tracker.exe` and `checksums.txt`. ## What ShippingTracker's updater does 1. GET `/api/v1/repos/{owner}/{repo}/releases/latest`; take `tag_name`, match assets by exact filename. 2. Parse the tag into a version — tolerates a leading `v`, pads missing components with `0` (an unset `System.Version` component is `-1`, which sorts wrong). 3. **Silent vs. loud.** A startup check swallows every failure (offline, DNS, timeout, non-200, malformed JSON) with no UI at all. A user-clicked check reports "you're up to date" and surfaces errors. 4. Prompts with current vs. available before doing anything. 5. **Pre-flights write permission** next to the executable, so a read-only install directory fails early rather than half-applying an update. 6. Streams the download to `.new` behind a small progress window. 7. Verifies SHA256 against a `checksums.txt` asset in `sha256sum` format (` `). If that asset is absent the update still installs, unverified. 8. **Swaps by rename**: running exe → `.old`, `.new` → exe, rolling back if the second rename fails. `.old` is deleted on the next launch. 9. On any failure, offers to open the releases page in a browser instead. ## Ports over unchanged The API shape, the silent/loud split, tag parsing, checksum verification, the rename-swap with `.old` cleanup, and the manual-download fallback. The rename dance exists because Windows won't delete a *running* binary but will rename one. Linux and macOS also allow renaming a running executable (the inode stays live), so a single code path covers all three platforms. ## Has to change for haul 1. **haul has no version at all.** ShippingTracker compares against `AssemblyVersion`; `cmd/haul` has no equivalent, which is why the Makefile deliberately omits `-X main.version` (a `-X` against a non-existent symbol is silently a no-op). Adding one is a prerequisite — without it every check reports "up to date". 2. **One asset becomes many.** ShippingTracker is Windows-only, so its `AssetName` is a single constant. haul must select an asset by `runtime.GOOS`/`runtime.GOARCH` (`haul-linux-amd64`, `haul-darwin-arm64`, `haul-windows-amd64.exe`). 3. **The executable bit.** On Unix the downloaded file needs `chmod 0755`. Nothing in the VB version corresponds to this. 4. **Fyne threading.** `internal/ui` only touches widgets from the UI thread and returns from I/O via `fyne.Do`. The updater must follow the same shape as `session.transfer` and `pane.navigateKeepMarks`. ## Blockers — read before starting **The release assets can't be built yet.** This gates everything. haul is cgo, so `make dist` produces the native binary only; there is currently no way to produce macOS or Windows assets from the Linux box. An updater with nothing to download is pointless, so **the release pipeline must come first** (`fyne-cross` over Docker, which is installed, or real machines). **Linux binaries don't travel.** haul links dynamically against `libGL`, `libX11`, `libc` and ten others. A binary built on Ubuntu 24.04 may not start on a different or older distro — it would download, swap itself in, and then fail to launch, which is the worst failure mode an updater has. ShippingTracker never hit this (.NET Framework, stable Windows ABI). Options: build against the oldest glibc worth supporting, ship Linux without auto-update, or accept it. **`checksums.txt` is not a signature.** It lives on the same server as the binary, so it catches corruption and truncation but not a compromised gitea — anyone who can replace one can replace the other. ShippingTracker accepts this. Closing the gap means a minisign/ed25519 signature with the public key compiled into haul, roughly 40 extra lines. ## Proposed shape New package `internal/selfupdate`, UI-free so it can be tested against a fake HTTP server: | Piece | Responsibility | | --- | --- | | `Check(ctx) (*Release, error)` | Fetch latest, parse tag, pick this platform's asset | | `Release.Newer(current)` | Version comparison | | `Download(ctx, rel, progress)` | Stream to `.new`, report progress | | `Verify(path, checksumURL)` | SHA256 against `checksums.txt` | | `Apply(newPath)` | Rename dance, rollback, `chmod` | | `CleanupOld()` | Delete `.old`; never errors | Plus a thin `internal/ui/update.go` for dialogs, reusing `confirmDialog` from `dialogs.go` and a progress dialog modelled on the existing transfer bar. Integration points, mirroring ShippingTracker's `Form1_Load` and `Form3`: - `CleanupOld()` and a silent check when the connect window opens (`newConnectWindow`). - A manual "Check for updates" button. The connect window is the natural home — haul has no settings screen. Release process: a `make release VERSION=x.y.z` target that builds, writes `checksums.txt`, and prints the tag/upload steps. Tag as `1.2.0` or `v1.2.0`, and don't mark it pre-release — `/releases/latest` skips drafts and pre-releases. ## Phasing | Phase | Work | | --- | --- | | 0 | Version stamping — `var version` in `cmd/haul`, `-X` in the Makefile, `haul --version` | | 1 | **Release pipeline** — cross-builds via fyne-cross, checksums, first tagged release | | 2 | `internal/selfupdate` with tests against a fake HTTP server | | 3 | UI wiring — startup silent check, manual button, progress and error dialogs | | 4 | README: how updating works, and how to publish a release | Phases 0 and 1 are worth doing regardless — there is currently no way to ship haul to anyone at all. ## Open decisions — needed before building 1. **Auto-check on startup, or manual only?** ShippingTracker does both. Silent startup checks mean haul contacts gitea on every launch. 2. **Which platforms get auto-update?** Suggestion: exclude Linux at first, for the dynamic-linking reason above. 3. **Checksums only, or add signatures?** 4. **Do phase 1 first, or write the updater against ShippingTracker's existing releases as a test fixture?**