feat(desktop): Tauri v2 kiosk shell — maximized window, prod right-click block, auto-update + code-signing
Add apps/desktop, a thin Tauri v2 shell wrapping the SAME @parking/web SPA so the desktop and browser UIs never drift: dev loads the Vite dev server (HMR), prod bundles the web app's dist/. No business logic in the shell (device/auth/ ledger stay in @parking/server); deny-by-default capabilities. apps/web (single UI source of truth): - lib/origin.ts: centralize the backend origin (API_BASE/apiUrl/wsUrl from VITE_API_BASE); no-op in the browser, lets the desktop build target Fastify. - lib/kiosk.ts: block the right-click context menu in PROD only (dev keeps it + devtools). - lib/desktop-updater.ts: prompt-on-update auto-update (no-op in browser/offline) → downloadAndInstall + relaunch; i18n update.* keys (sq+en). - .env.production: VITE_API_BASE wired to the Fastify origin for the bundle. Desktop: - window starts maximized (not fullscreen — operator keeps OS access). - auto-update via tauri-plugin-updater + -process; self-hosted endpoint is a PLACEHOLDER to fill in. Updater keypair: pubkey embedded in tauri.conf.json; private key + password kept OUTSIDE the repo (~/.parking-updater-keys) and as TAURI_SIGNING_* build secrets. - Turbo build is a no-op; the real signed bundle is `pnpm --filter @parking/desktop bundle` (verified → .deb/.rpm/.AppImage + .sig signatures). Verified: cargo check clean; turbo run build lint 14/14 green; i18n parity holds; no key/sig/bundle artifacts in the repo. Wiki (security + desktop analysis recorded alongside): - new concepts/tpm.md (TPM 2.0: how it works, sealed-LUKS auto-unlock + non- extractable signing key, limits — live-root, bus-sniff — TPM-vs-ATECC608 by platform). - new decisions/desktop-shell-tauri.md (Tauri v2 over Electron; best-case Ubuntu 26.04 LTS, worst-case Windows+WSL → kiosk browser; full as-built). - pull-the-disk attack trace on append-only-event-chain; ATECC608 not-in-a-PC caveat; cross-links from disk-os-hardening / threat-model. - open-questions #11 (appliance WebKitGTK), #12 (TPM hardening impl), #13 (startup verifyChain self-check); index/overview/log/standing-decisions. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
+56
@@ -1173,3 +1173,59 @@ reserve checkbox (SiteSettings); booth pay modal shows an "OUT-OF-WINDOW" charge
|
||||
Verified on a copy of the live DB: qty 2 = 2× price; night-plan 19:30 entry → 30min/15,000 ALL owed,
|
||||
stamped + paid → gate clears, chain verifies; reserve toggle holds a qty-2 sub's 2 spots. Build+lint
|
||||
12/12; 80 shared tests. Updated [[subscription]], [[capacity-occupancy]], [[tariff]].
|
||||
|
||||
## [2026-06-21] query | Desktop shell: Tauri v2 vs. Electron
|
||||
Compared Tauri v2 and Electron for shipping the operator UI as a desktop app (mobile deferred).
|
||||
Decision (with user): **Tauri v2** — small footprint, no bundled Chromium to patch, deny-by-default
|
||||
native surface fitting the booth-operator threat model; MIT/Apache. Shell stays thin (device/auth/
|
||||
ledger/pricing remain in Fastify, per user). Filed [[desktop-shell-tauri]]; cross-linked from
|
||||
[[standing-decisions]], [[overview]], [[index]]. Open dependency: appliance WebKitGTK version
|
||||
([[open-questions]] #11) — flips to Electron if ancient/unavailable.
|
||||
|
||||
## [2026-06-21] query | Desktop shell — target OS (best/worst case)
|
||||
User specified deployment span: best = Ubuntu 26.04 LTS desktop, worst = Windows 11 + WSL + Docker.
|
||||
Refined [[desktop-shell-tauri]] + [[open-questions]] #11: Ubuntu 26.04 LTS ships a current
|
||||
distro-maintained WebKitGTK → effectively closes the WebView risk; Tauri unconditional there. The
|
||||
Windows+WSL case is NOT an "Electron instead" fallback — it conflicts with the standing Linux-
|
||||
appliance platform decision and can't host a GUI shell in headless WSL/Docker; fallback is a kiosk
|
||||
browser at the local Fastify-served SPA (Electron only if a standalone Windows installer is
|
||||
mandated). Thin-shell architecture makes that fallback cheap.
|
||||
|
||||
## [2026-06-21] query | TPM 2.0 hardening — analysis + pull-the-disk attack trace
|
||||
How a TPM works (non-extractable keys + PCR sealing) and its limits, recorded after tracing the
|
||||
"pull the SSD, tamper parking.sqlite offline, reboot" attack against event-log.ts/signer.ts.
|
||||
Findings: verifyChain() catches every blind tamper (bad sig / index gap / prevHash / unknown keyId)
|
||||
but (a) nothing runs it at boot, and (b) the software HMAC key lives in .env on the same disk →
|
||||
attacker can re-sign undetectably. Only a secure-element key (TPM on a PC, ATECC608 on embedded)
|
||||
makes it tamper-PROOF; TPM-sealed LUKS additionally blocks off-host mount. TPM verdict: recommended
|
||||
not required on the Ubuntu appliance (sealed-LUKS auto-unlock + non-extractable signing key); does
|
||||
NOT defend a rooted live host or the operator; bus-sniff/PCR-brittleness caveats → prefer fTPM + PIN,
|
||||
keep recovery passphrase + re-seal runbook; complements not replaces reconciliation. Also corrected:
|
||||
ATECC608 is NOT in a PC (external I²C part) → on a PC appliance the TPM is the host secure-element,
|
||||
ATECC608 reserved for the ESP32 controller. New page [[tpm]]; cross-linked [[disk-os-hardening]],
|
||||
[[threat-model]], [[atecc608]], [[append-only-event-chain]]; open-questions #12 (TPM impl, to build),
|
||||
#13 (startup verifyChain self-check, to build); index + counts updated.
|
||||
|
||||
## [2026-06-21] build | apps/desktop — Tauri v2 kiosk shell scaffolded
|
||||
Built the thin Tauri v2 shell per [[desktop-shell-tauri]]: new apps/desktop package wrapping the
|
||||
SAME apps/web SPA (dev → localhost:5173 with HMR; prod → bundled web dist/), so desktop and browser
|
||||
UIs can't drift (user requirement). Rust core holds no business logic; capabilities core:default
|
||||
only (deny-by-default). One apps/web change: centralized the backend origin into lib/origin.ts
|
||||
(API_BASE/apiUrl/wsUrl from VITE_API_BASE) — no-op in the browser, lets the Tauri build target the
|
||||
Fastify origin. Turbo build is a no-op; real bundle = `pnpm --filter @parking/desktop bundle`.
|
||||
VERIFIED: cargo check + full tauri build → working .deb/.rpm/.AppImage; turbo run build lint 14/14
|
||||
green; prereqs present (Rust 1.93, WebKitGTK 4.1, libsoup-3, WSLg). Filled the As-built section of
|
||||
[[desktop-shell-tauri]]. Deferred: kiosk lockdown, auto-update, signing, Windows kiosk-browser path.
|
||||
|
||||
## [2026-06-21] build | apps/desktop — window/right-click, auto-update, code-signing, env wiring
|
||||
Per user choices on the Tauri shell: window starts MAXIMIZED (not fullscreen — operator keeps OS
|
||||
access); right-click context menu blocked in PROD only (lib/kiosk.ts, dev keeps devtools).
|
||||
VITE_API_BASE wired via apps/web/.env.production (committed non-secret, allow-listed in .gitignore;
|
||||
auto-loaded by vite build → desktop bundle targets Fastify, no manual export). Auto-update built:
|
||||
tauri-plugin-updater + -process, prompt-on-update flow (lib/desktop-updater.ts, no-op in browser/
|
||||
offline) → downloadAndInstall + relaunch; endpoint is a self-hosted PLACEHOLDER to fill in. Updater
|
||||
keypair generated: pubkey embedded in tauri.conf.json; private key + password kept OUTSIDE the repo
|
||||
(~/.parking-updater-keys, 0600) + as TAURI_SIGNING_* build secrets. VERIFIED: signed bundle →
|
||||
.deb/.rpm/.AppImage + .sig updater signatures; turbo run build lint 14/14 green; no key material in
|
||||
the repo. Updated As-built in [[desktop-shell-tauri]]. Deferred: real update URL, OS installer
|
||||
signing, Windows kiosk-browser fallback.
|
||||
|
||||
Reference in New Issue
Block a user