# @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 ```bash 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. ## 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.