System topology
Three-tier data flow
1. Terminal (browser)
The POS frontend connects to the hub via WebSocket. On every order change, the terminal sends anORDER_SNAPSHOT message containing the full order state. It does not talk to Odoo directly in hub mode.
Key modules (in nu_pos_react/src/realtime/):
hubTransport.ts— ImplementsSyncTransportfor hub modehub/hubConnection.ts— WebSocket lifecycle with auto-reconnecthub/hubDiscovery.ts— Resolves hub URL from bootstrap config
2. Hub (Raspberry Pi)
The hub is a stateless-except-SQLite Node.js service that:- Authenticates terminals via Odoo session validation
- Enforces single-writer order ownership through leases
- Persists order snapshots with optimistic versioning
- Broadcasts changes to all other connected terminals
- Forwards events to Odoo in the background
nu_pos_hub/src/):
handlers/— Message handlers (auth, lease, order, table)storage/— SQLite stores (orders, leases, events, tables)leasing/leaseManager.ts— Lease business logicupstream/forwarder.ts— Background event drain to Odoo
3. Odoo (cloud)
Odoo is the system of record. The hub forwards order events via the existing/pos-react/api/orders/{create,update} endpoints. These are idempotent by pos_reference, so re-forwarding is safe.
Direct mode vs hub mode
Both modes implement the same
SyncTransport interface (see transport layer), so the UI code is identical regardless of mode.
Component ownership
The frontend does not import
@nu/sync-protocol directly. Instead, transport.ts defines OrderSnapshotData (a mirror of the protocol’s OrderSnapshot) to avoid coupling the browser bundle to the Node.js protocol package.