@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)
buildis a no-op in this package soturbo run buildstays fast — the real desktop bundle (compiles Rust, minutes long) is the explicitbundlescript 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:
pnpm --filter @parking/server dev(local backend;.envmust haveCOOKIE_SECURE=0andtauri://localhostinWS_ALLOWED_ORIGINS).pnpm --filter @parking/desktop bundleand run the produced AppImage fromsrc-tauri/target/release/bundle/appimage/(WSLg is enough).- On the ConnectScreen enter
127.0.0.1:3000, Test must say reachable, then Save. - Log in. The booth header must show LIVE (not "JASHTË LINJË") within a few seconds.
- Perform one mutation (e.g. change your UI language) — it must succeed (proves CSRF).
- 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.