fix(web): permission-degrade the app shell for merchant-only users
Build desktop / desktop (push) Successful in 4m51s
Build & push images / images (push) Successful in 3m8s
CI / check (push) Successful in 52s

A user whose role has only validation:create (the bar/lavazh validator) made
the shell misbehave: useLiveFeed() connected /api/ws unconditionally, the
server's report:read guard 403'd the upgrade, and the capped-backoff
reconnect hammered it forever — a 403 in the server log every few seconds.
Gate the socket on report:read (mirrors routes/ws.ts WATCH_PERMISSION) and
render StatusDot / ShiftButton / DeviceFooter only with their backing
permissions (report:read / shift:read / device:read), so a merchant's shell
is just the nav + their /validate screen, with zero doomed requests.

Claude-Session: https://claude.ai/code/session_01YYkpEsLmoQPaize5ec3oUm
This commit is contained in:
2026-07-13 20:12:43 +02:00
parent 0ed43239c3
commit 19dff97c74
3 changed files with 34 additions and 10 deletions
+5 -1
View File
@@ -100,7 +100,11 @@ priced at booth check-in, inside the normal walk-back-grace flow).
both; mode/params/caps/receipt-label/bound-users). `/validate` (`ValidateScreen.tsx`) is the
merchant's whole surface (scan/key → apply → void own unused), mobile-friendly, autofocused
input works with HID scanners; merchant-only users (no `session:read`) land there on login and
the permission-gated nav shows them nothing else. Booth pay modal shows gross → lines → net;
the permission-gated nav shows them nothing else. The app SHELL also degrades by permission
(2026-07-13 follow-up): the live-feed WebSocket connects only with `report:read` (the server's
WS guard — a merchant's socket would 403 and the capped-backoff reconnect would spam the server
log forever), and the StatusDot / ShiftButton / DeviceFooter widgets render only with their
backing permissions (`report:read` / `shift:read` / `device:read`). Booth pay modal shows gross → lines → net;
the zero-net comp settles through the normal pay path (grace starts, voucher/exit unchanged).
Feed label `VALIDIM`/`VALIDATION`. RolesManager picks the new resource up generically.
- **Verified**: 8 route-level integration tests (guards, signed events, money cycle, void locks,