← back to index

U16 — Multi-device web login + POS single-actor enforcementPROD

Direct user request 2026-05-30: "we need to change the multi session login on nix cafe, we can login on the same account on multiple device, but NOT for the POS." Part A lifts R8.2 (drops the JWT sid vs tenant_users.active_session_token check in auth()) so web logins are multi-device. Part B extends R8.4 from PIN-only to all POS actors via a new tenant_users.active_pos_session_token column + unified lib/db/active_pos_session.ts DAO — Manager Override unlocks now kick prior terminals where the same user is active. Part C adds a KickBanner naming the kicker on the lock screen.

Summary

Status
12/12 prod · 51/51 regression = 63/63 · shipped
Commit
nix-cafe 7b5efee + 018a79b (Hyperdrive fix) · nix-outdoor-sales-backend 5492b36 (migration)
Files
11 modified + 2 new · 1 migration · ~400 LOC
Migration
tenant_users.active_pos_session_token UUID NULL. Mirrors commerce.pin_identities.active_session_token.

What ships

12/12 prod checks

Pre-flight: lumiere owner has POS PIN + active register resolves
Pre-flight: force-close any pre-existing open session; zero owner POS sid
Web multi-device login: terminal A reaches /cafe/dashboard after login
Load-bearing R8.2-lifted assertion: terminal B logs in fresh + reaches /cafe/dashboard + terminal A is STILL authenticated (no kick)
POS unlock: terminal A as Manager (active-cashier cookie set)
DB: tenant_users.active_pos_session_token populated after terminal A unlock
POS unlock: terminal B unlocks same register as Manager (kicks terminal A)
DB: active_pos_session_token rotated by terminal B's unlock (sid differs)
Kick UX: terminal A refreshes register URL → lock screen with pos-kick-banner visible naming "Sokha Lim"; banner copy contains "another device"
Banner-mount useEffect clears the stale cookie → next refresh shows no banner
No HTTP 5xx during the U16 flow on either terminal
Cleanup: force-close test sessions + zero owner POS sid

Screenshots (click to enlarge)

Mid-Gate-2 burn — Hyperdrive cache hit on the kick read

Architectural notes

Regression sweep — 51/51

12/12 + 51/51 = 63/63 prod tests green on the karouna-dev branch.
test-phase1-prod.mjs11/11
test-phase2-sso-outdoor-prod.mjs6/6
test-m1-prod.mjs10/10
test-r7-prod.mjs14/14
test-r8-prod.mjs4/4
test-phase2-cafe-multishop-prod.mjs6/6
22nd validation of feedback_phase2_cafe_multishop_solo_retry.