Auto-Update
Channel selection, manifest verification, per-platform asset mapping, and the v0.1.13–v0.1.22 macOS bug.
Mosaic ships its own auto-updater backed by creativeprojects/go-selfupdate against GitHub Releases. The updater runs as a background goroutine, performs an initial check shortly after launch, and re-checks every 24 hours; it never auto-installs — the install step is always user-initiated from the toast or the Settings → Updater pane.
Source: GitHub Releases
The release source is exec/mosaic. Each release publishes a set of platform-specific assets plus a SHA256SUMS manifest.
Channels
| Channel | Behavior |
|---|---|
| stable | Default. Skips pre-release tags. |
| beta | Accepts pre-release tags too. Useful for early access to upcoming changes. |
Configurable in Settings → Updater or via PUT /api/settings/updater ({"channel": "stable"|"beta"}).
Verification: SHA256SUMS
Every release publishes a SHA256SUMS file in the lib’s expected format — one <hex> <filename> line per asset. The lib’s ChecksumValidator fetches it during the install step, finds the row matching the asset filename, and verifies the SHA-256 before Updater.UpdateTo overwrites the running binary.
If verification fails, the install aborts and the running binary is untouched. There is no rollback path because there is no install step — verification gates the swap.
Limitation.
SHA256SUMSitself is unsigned. Anyone who can replace assets on a release can also rewrite the manifest. See Security → Audit follow-ups for the planned signed-manifest mitigation (H3).
Per-platform asset mapping
Per-platform regex filters in backend/updater/source.go pick the right asset for the running OS — the lib’s default runtime.GOOS/runtime.GOARCH matcher is deliberately bypassed via custom Filters regexes, because Mosaic’s release filenames don’t follow the lib’s expected naming convention. Mosaic publishes the following auto-updater targets:
| OS | Arch | Auto-update asset | Inner binary path |
|---|---|---|---|
| darwin | universal | Mosaic-vX.Y.Z-darwin-universal.tar.gz |
mosaic.app/Contents/MacOS/mosaic |
| linux | amd64 | Mosaic-vX.Y.Z-linux-amd64.AppImage |
(single-file) |
| windows | amd64 | Mosaic-vX.Y.Z-windows-amd64-portable.exe |
mosaic.exe (single-file) |
The macOS auto-updater target is the .tar.gz, not the .dmg — the go-selfupdate archive matcher cannot unwrap a .dmg. The .dmg is fresh-install only. Likewise on Windows, the auto-updater pulls the portable single-file .exe; the installer variant is for first-time installs.
The fresh-install assets (.dmg, .deb, .rpm, installer .exe) live alongside the auto-updater assets on every release and aren’t consumed by the in-place updater.
The v0.1.13–v0.1.22 macOS bug
Background. The auto-updater on macOS builds was broken from v0.1.13 through v0.1.22 inclusive. Symptom: the update toast appeared, the user clicked Install, and the app exited without ever swapping the binary — silently. No surfaced error, no logs.
Root cause. The release tarball’s inner directory used a capital-M (Mosaic.app/...), but go-selfupdate’s archive matcher in those versions was case-sensitive and looking for a lowercase tarball entry. The matcher couldn’t find the new binary inside the archive, so UpdateTo returned no error and exited without doing anything.
Fix. v0.1.23 forces the inner tarball entry to match the lowercase mosaic binary name the lib looks for, and adds a tarball_contract_test.go that asserts the convention so a future packaging tweak can’t silently regress it.
What this means for you. If you’re on a v0.1.13 through v0.1.22 macOS build, the auto-updater will not get you off it. Manually download v0.1.23 or later from the releases page once, and the in-place updater will work from there forward.
API surface
GET /api/settings/updater → UpdaterConfigDTO
PUT /api/settings/updater → 200 {ok: true}
POST /api/updater/check → UpdateInfoDTO (also persists last-checked + last-seen)
POST /api/updater/install → 200 {ok: true} | 500 (verification failure / network)
GET /api/version → {version: "v0.2.x"}
UpdateInfoDTO carries the available flag, the latest version tag, the asset URL + filename, and current_version (so the toast can render 0.2.7 → 0.2.8 without a second call).
When the background check finds a newer release it ALSO fires an update:available WebSocket frame to every connected client, so toasts in remote browser tabs surface immediately rather than waiting for the next manual check.
Disabling
Set the master toggle off:
curl -X PUT https://localhost:8080/api/settings/updater \
-H "Authorization: Bearer $MOSAIC_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled": false, "channel": "stable", "last_checked_at": 0, "last_seen_version": ""}'
The background goroutine still starts but bails out on the enabled check; manual POST /api/updater/check still works for ad-hoc queries.