fix(release): latest.json entry per installer type — .deb booths could never self-update
Release desktop / bundle (push) Successful in 5m26s
Release desktop / bundle (push) Successful in 5m26s
tauri-plugin-updater resolves the download target as {os}-{arch}-{installer}
first (linux-x86_64-deb — the bundler stamps the installer type into the
binary, verified with `strings` on a local .deb) and only then bare
linux-x86_64. Our manifest carried only the bare key, pointing at the
AppImage. A .deb install therefore downloaded the AppImage, verified its
signature, then failed install_deb()'s is_deb check with
InvalidUpdaterFormat — after the download, before any relaunch. This, not
version drift or swallowed errors, is why v0.1.0→v0.1.6 never self-updated.
latest.json now carries linux-x86_64-deb, linux-x86_64-rpm (when built) and
linux-x86_64 (AppImage), each with its own .sig. A .deb update ends in a
polkit password prompt (pkexec dpkg -i) — the intended admin gate on a
root-installed package. README + wiki updated; wiki also records the v0.1.6
LIVE field verification.
Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
@@ -399,7 +399,8 @@ a fresh handshake every 10 s (use-live-feed's capped backoff), i.e. every connec
|
||||
it as `Authorization: Bearer` (fastify-jwt would accept it) — that puts the session token in JS,
|
||||
which HttpOnly exists to prevent; the ticket keeps it out. Verified locally with an 11-case
|
||||
handshake script: ticket/no-cookie → 101 + hello; reused/bogus/absent → 401; ticket + bad Origin
|
||||
→ 403; cookie path unchanged.
|
||||
→ 403; cookie path unchanged. **Field-verified 2026-09-04:** v0.1.6 on the park-2 booth against
|
||||
image `stage-8fa66c9` shows **LIVE** — the first desktop build to do so.
|
||||
- **Desktop client logs had never reached `app_logs`.** `logger.ts`'s flush read the CSRF token
|
||||
from `document.cookie` (null on desktop — the same jar split as above), so every
|
||||
`POST /api/logs` from the desktop 403'd under `requireAuth`→`assertCsrf`, and the flush drops
|
||||
@@ -420,3 +421,37 @@ a fresh handshake every 10 s (use-live-feed's capped backoff), i.e. every connec
|
||||
cookie-jar split *cannot* reproduce in dev mode. The pre-tag gate is now: build the bundle
|
||||
locally, run the AppImage against a local server, log in, confirm **LIVE**, do one mutation,
|
||||
and confirm a desktop-sourced row appears in the Logs viewer (`apps/desktop/README.md`).
|
||||
|
||||
### In-app update never worked: the manifest only described the AppImage, the booths run the .deb (2026-09-04)
|
||||
|
||||
Every self-update attempt from v0.1.0 through v0.1.6 ended the same way — prompt, download
|
||||
traffic, then nothing, and the same prompt again next launch. The version-sync (v0.1.2) and
|
||||
error-logging fixes were real but not the cause. **Root cause:** `tauri-plugin-updater` resolves
|
||||
the download target as `{os}-{arch}-{installer}` **first** (`linux-x86_64-deb` here — the
|
||||
bundler stamps `__TAURI_BUNDLE_TYPE_VAR_DEB` into the `.deb`'s binary, verified with `strings`
|
||||
on a local build), then falls back to bare `{os}-{arch}`. `release.yml`'s `latest.json` carried
|
||||
**only** `linux-x86_64`, pointing at the **AppImage**. So a `.deb` install found the update,
|
||||
downloaded the AppImage, verified its signature (which was correct — for the AppImage), then
|
||||
handed the bytes to `install_deb()`, whose first line checks `infer::archive::is_deb(bytes)` and
|
||||
returns `InvalidUpdaterFormat`. Before v0.1.6 that error never reached the server (the desktop
|
||||
log channel was itself broken — see the previous section), so it looked like a silent no-op.
|
||||
Sources: `tauri-plugin-updater-2.10.1/src/updater.rs` (`get_urls`, `install_inner`,
|
||||
`install_deb`), `tauri-utils/src/platform.rs` (`bundle_type`).
|
||||
|
||||
- **Fix:** `latest.json` now carries one signed entry per installer — `linux-x86_64-deb`,
|
||||
`linux-x86_64-rpm` (when built), and bare `linux-x86_64` for the AppImage — assembled by a
|
||||
small Node script in the workflow (the `.sig` files for `.deb`/`.rpm` were already being
|
||||
produced and uploaded, just never referenced).
|
||||
- **What a booth update now looks like:** prompt → download → **polkit password dialog**
|
||||
(`pkexec dpkg -i`) → relaunch into the new version. The prompt is deliberate, not a wart: the
|
||||
package is root-owned in `/usr/bin`, and under the [[threat-model]] the operator must not be
|
||||
able to replace the app silently; whoever brings the box online for an update is the admin.
|
||||
Cancelling the dialog leaves the old version running and logs
|
||||
`desktop_update_install_failed` to `app_logs`.
|
||||
- **Rejected:** switching booths to the AppImage so updates need no privilege. It would work
|
||||
(the updater rewrites the AppImage in place), but the binary would then be operator-writable,
|
||||
it needs FUSE on the appliance image, and launcher/autostart integration becomes manual —
|
||||
three regressions to avoid one password prompt.
|
||||
- **Judgment note for the retrospective:** three fixes were shipped against this symptom
|
||||
without reading the updater's install path once. The whole chain is ~60 lines of vendored
|
||||
Rust in `~/.cargo/registry`; it names the exact failure (`InvalidUpdaterFormat`).
|
||||
|
||||
Reference in New Issue
Block a user