Files

@parking/desktop — Tauri v2 kiosk shell

A thin native desktop window over the @parking/web SPA. It contains no UI and no business logic of its own: the window renders the same web app the browser does, so the desktop and the browser stay identical and never drift. Device/auth/ledger logic stays in @parking/server. See wiki/decisions/desktop-shell-tauri.md.

How the "same look & functionality" guarantee works

Source of the UI
Dev (tauri dev) the window loads http://localhost:5173 — the @parking/web Vite dev server. Edit a component in apps/web → HMR updates the desktop window live.
Prod (tauri build) the window bundles apps/web's built dist/. beforeBuildCommand rebuilds the SPA first.

There is only one UI codebase (apps/web); this package just wraps it.

Backend connection

The SPA talks to Fastify over HTTP/WS. In a browser that's same-origin (relative /api). In the desktop build the bundled assets load from tauri://localhost, so set VITE_API_BASE (read at web build time — see .env.example) to the appliance's Fastify origin, e.g. http://127.0.0.1:3000. The CSP connect-src in tauri.conf.json is already allowed for that origin, and the backend must include the Tauri origin in WS_ALLOWED_ORIGINS for the live feed.

Commands

pnpm --filter @parking/desktop dev      # native window over the web dev server (HMR)
pnpm --filter @parking/desktop bundle   # build the SPA + bundle the desktop app (.deb/.rpm/.AppImage)

build is a no-op in this package so turbo run build stays fast — the real desktop bundle (compiles Rust, minutes long) is the explicit bundle script above.

Requires the Rust toolchain and (on Linux) WebKitGTK 4.1 + libsoup-3 dev libraries. Under WSL2 the window needs a display (WSLg or an X server).

Auto-update

Signed updates are built and published by .gitea/workflows/release.yml on a vX.Y.Z tag, mirrored to the public mca/public_releases repo (this repo is private; the updater runs on offline-first field appliances with no Gitea credentials, so its endpoint must be reachable unauthenticated — see that workflow's header and wiki/decisions/desktop-shell-tauri.md). The updater config and signing pubkey live in tauri.conf.json; the private signing key is held outside the repo, never committed.

The manifest carries one entry per installer type (linux-x86_64-deb, linux-x86_64-rpm, and bare linux-x86_64 for AppImage). The updater picks the entry matching how the running app was installed — a .deb install will only ever accept a signed .deb. Booths run the .deb, so an in-app update ends in a polkit password prompt (pkexec dpkg -i): that is expected, and it is the right gate — the package lives in /usr/bin, root-owned, and the operator is not supposed to be able to replace it silently. Cancel the prompt and the app keeps running the old version; the failure is logged to the server's Logs viewer.

Release gate — run the REAL bundle locally before tagging

tauri dev loads the SPA from http://localhost:5173, a plain http origin. The shipped bundle loads it from tauri://localhost, a secure custom-scheme origin — and every desktop-only bug found in the field on 2026-09-03/04 (relative-URL DOMException, mixed content, missing WS Origin, the reqwest-vs-webview cookie split, the WS handshake that can't carry the cookie) depends on that difference. Dev mode cannot reproduce any of them, so "works in tauri dev" carries no information about a release. Before pushing a vX.Y.Z tag:

  1. pnpm --filter @parking/server dev (local backend; .env must have COOKIE_SECURE=0 and tauri://localhost in WS_ALLOWED_ORIGINS).
  2. pnpm --filter @parking/desktop bundle and run the produced AppImage from src-tauri/target/release/bundle/appimage/ (WSLg is enough).
  3. On the ConnectScreen enter 127.0.0.1:3000, Test must say reachable, then Save.
  4. Log in. The booth header must show LIVE (not "JASHTË LINJË") within a few seconds.
  5. Perform one mutation (e.g. change your UI language) — it must succeed (proves CSRF).
  6. Open Setup → Logs and confirm a frontend-sourced row from this desktop session exists (proves the desktop log channel; historically it was silently 403'd).

Only then tag. If a release still fails in the field, the gap is in this list — fix the list.

Not here (deliberately)

Kiosk lockdown (fullscreen/no-decorations) and launching Fastify from the shell are out of scope for the scaffold — on the appliance Fastify runs as its own service and this shell connects to it.