fix(desktop): mirror signed releases to public repo for the updater
The updater endpoint pointed at mca/parking_solution's own Gitea "latest release" redirect, but that repo is private and field appliances have no Gitea credentials — every update check was silently failing. release.yml now mirrors signed installers to mca/public_releases (public, installers only) under a fixed desktop-latest tag; tauri.conf.json points there. Rejected embedding a read token in the app instead, given the booth-operator threat model. Also: make the appliance-provisioning root_directory gotcha impossible to skim past (boxed callout + explicit next-step pointers), after it caused a second missed step on the park-2 install.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
type: decision
|
||||
tags: [parking, decisions, desktop, frontend]
|
||||
sources: []
|
||||
updated: 2026-06-21
|
||||
updated: 2026-09-03
|
||||
status: settled
|
||||
---
|
||||
|
||||
@@ -149,11 +149,7 @@ Per the user's choices — the operator **keeps OS access** (no fullscreen lockd
|
||||
offline), prompts the operator (i18n `update.prompt`), then `downloadAndInstall()` + `relaunch()`.
|
||||
Accepts that the appliance may be **offline** day-to-day and brought online (phone hotspot) only
|
||||
when an update is wanted — consistent with [[offline-first]] (no network dependency in *core*
|
||||
operation; updates are out-of-band). Endpoint is the **self-hosted Gitea** "latest release"
|
||||
path — `https://git.infra.msai.al/mca/parking_solution/releases/latest/download/latest.json`
|
||||
— which redirects to the newest tag's `latest.json` (published by `.gitea/workflows/release.yml`).
|
||||
The updater GETs it (200 + manifest, or 204 = up-to-date), reads `platforms.linux-x86_64.
|
||||
{signature,url}`, and downloads the signed installer. **WS origin:** the desktop window's origin
|
||||
operation; updates are out-of-band). **WS origin:** the desktop window's origin
|
||||
is `tauri://localhost` (Linux may also send `http://tauri.localhost`), so the backend's
|
||||
`WS_ALLOWED_ORIGINS` must include both or the live feed won't connect (documented in
|
||||
`apps/server/.env.example`).
|
||||
@@ -165,8 +161,27 @@ Per the user's choices — the operator **keeps OS access** (no fullscreen lockd
|
||||
produced `.deb`/`.rpm`/`.AppImage` **plus their `.sig` updater signatures**; full `turbo run build
|
||||
lint` 14/14 green. *(This is the **updater** signing — distinct from OS-installer signing for
|
||||
Windows/macOS "unknown publisher", and from the [[atecc608]]/[[tpm]] **event** signing.)*
|
||||
- **Still deferred:** the actual update-hosting URL, OS-level installer signing
|
||||
(Windows/macOS publisher trust), and the Windows kiosk-browser fallback path.
|
||||
- **Update-hosting endpoint (found broken, fixed 2026-09-03):** the endpoint originally pointed at
|
||||
the **source repo's own** Gitea "latest release" redirect
|
||||
(`.../mca/parking_solution/releases/latest/download/latest.json`) — but `mca/parking_solution` is
|
||||
**private**, and the updater runs on offline-first field appliances with **no Gitea credentials**.
|
||||
Every deployed update check was silently failing (swallowed by a `try/catch` in
|
||||
`desktop-updater.ts`) — this was never field-verified, and it couldn't have worked as configured.
|
||||
**Fix:** signed installers are now mirrored to a separate **public**, releases-only repo,
|
||||
`mca/public_releases` (shared across apps in the org — see [[fleet-deployment-komodo]] sibling
|
||||
infra), holding **only compiled installers, no source**. `tauri.conf.json`'s endpoint now points
|
||||
there at a fixed `desktop-latest` tag (NOT that repo's generic "latest release" redirect, since
|
||||
other apps publishing there would shadow ours — see the `desktop-latest` vs `desktop-<TAG>`
|
||||
split below). `.gitea/workflows/release.yml` pushes to both repos: the private source repo (own
|
||||
record) and the public mirror (what the updater and any human downloader actually use).
|
||||
**Rejected alternative:** embedding a `read:repository` Gitea token in `tauri.conf.json`'s
|
||||
updater `headers` so it could read the private repo directly — ruled out because that token would
|
||||
ship inside every installed binary in the field, and this appliance's own threat model names the
|
||||
**booth operator as the primary adversary** (see root `CLAUDE.md`); a leaked token scoped to the
|
||||
whole private repo, with no cheap way to rotate it across appliances already in the field, was
|
||||
judged worse than publishing installers-only.
|
||||
- **Still deferred:** OS-level installer signing (Windows/macOS publisher trust) and the Windows
|
||||
kiosk-browser fallback path.
|
||||
|
||||
### Desktop in CI — two workflows, two purposes (added 2026-06-24)
|
||||
|
||||
@@ -174,7 +189,15 @@ The desktop bundle now runs in CI under **two distinct workflows** — keep the
|
||||
|
||||
- **`.gitea/workflows/release.yml`** (tag `v*`) — the **signed, versioned release**: builds
|
||||
`.deb`/`.rpm`/`.AppImage` **+ their `.sig`** (updater key from secrets), assembles `latest.json`,
|
||||
and publishes a Gitea Release. This is what the auto-updater consumes. Unchanged.
|
||||
and publishes a Gitea Release **on `mca/parking_solution` (source, own record) AND mirrors it to
|
||||
`mca/public_releases`** (public, installers-only — see the update-hosting-endpoint entry above for
|
||||
why). The mirror step uses a second token, `RELEASES_MIRROR_TOKEN`
|
||||
(`write:repository`, scoped for pushing into `public_releases` only — a CI-side secret, never
|
||||
shipped to any client, distinct from the embedded updater *pubkey*). It publishes two tags there:
|
||||
`desktop-<TAG>` (versioned, permanent, for audit/rollback) and `desktop-latest` (moving — existing
|
||||
assets deleted then re-uploaded each release, since Gitea has no per-app "latest" concept and this
|
||||
repo is shared across apps). `latest.json`'s asset URL and `tauri.conf.json`'s updater endpoint
|
||||
both point at `desktop-latest`. This is what the auto-updater actually consumes.
|
||||
- **`.gitea/workflows/build-desktop.yml`** (push to `dev`/`main`) — a **per-commit test build**:
|
||||
compiles `.deb` + `.AppImage` only (`pnpm --filter @parking/desktop bundle --bundles deb,appimage`)
|
||||
and publishes them to a **rolling per-branch pre-release** (tag `desktop-<branch>`). **Unsigned** —
|
||||
|
||||
Reference in New Issue
Block a user