fix(desktop): WS ticket auth for the live feed; desktop logs never reached the server
Build & push images / images (push) Successful in 2m51s
Release desktop / bundle (push) Successful in 41m19s

The v0.1.4 Origin fix cleared only the first of two gates in /api/ws's
preHandler. The second, req.jwtVerify(), reads the HttpOnly cookie — which
tauri-plugin-websocket (a bare tungstenite client, no cookie jar) can never
send. Every desktop handshake 401'd and use-live-feed reconnected every 10s
(confirmed in the park-2 server log).

- routes/ws.ts: POST /api/ws/ticket (cookie + CSRF auth) mints a 30s,
  single-use, in-memory ticket; the WS preHandler accepts it via an
  x-ws-ticket header after the Origin check, then the same report:read
  role check. Browser cookie path unchanged; JWT stays out of JS.
- platform-ws.ts: fetch a ticket before connect, send it with the Origin
  header; connect failures now go through logClient (rate-limited).
- logger.ts: flush read the CSRF token from document.cookie, null on
  desktop, so every desktop POST /api/logs 403'd and was dropped silently —
  no desktop client log had ever reached app_logs. Stash moved to a
  dependency-free lib/desktop-csrf.ts shared by api.ts and logger.ts.
- backend-config.ts: ConnectScreen probe uses the unauthenticated /health
  (now also returns app: "parking-system") instead of accepting any 401.
- README: local-AppImage release gate — tauri dev runs at
  http://localhost:5173, not tauri://localhost, so none of these
  origin-dependent bugs reproduce there.
- wiki: new section + log entry; four citation corrections.

Requires the server image with this commit deployed before the new desktop
build connects (the ticket endpoint must exist).

Claude-Session: https://claude.ai/code/session_01FWncR69HgGPuei1dLrW3cU
This commit is contained in:
2026-09-04 12:09:30 +02:00
parent 70e1e9939f
commit 8fa66c9911
11 changed files with 279 additions and 56 deletions
+21
View File
@@ -44,6 +44,27 @@ see that workflow's header and `wiki/decisions/desktop-shell-tauri.md`). The upd
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