How it works
Acquiring a lease
- Terminal opens an order and sends
LEASE_ACQUIRE { posReference, baseVersion } - Hub checks if the order is already leased:
- Not leased — Grant immediately. Returns
LEASE_GRANTED { version, expiresAt } - Leased by same terminal — Grant (re-entrant). Returns current version
- Leased by another terminal — Deny. Returns
LEASE_DENIED { holder, reason }
- Not leased — Grant immediately. Returns
- Terminal must hold a lease before sending
ORDER_SNAPSHOT
Maintaining a lease
- Leases expire after 60 seconds (
LEASE_TTL_MS) without renewal - Terminals send
LEASE_HEARTBEATevery 20 seconds (HEARTBEAT_MS) - Each heartbeat resets the expiry to
now + 60s - If a terminal crashes or loses connection, the lease auto-expires
Releasing a lease
- Terminal sends
LEASE_RELEASE { posReference, finalSnapshot? } - If
finalSnapshotis provided, the hub saves it before releasing - This happens when closing an order, navigating away, or paying
Lease expiry (tick)
The hub runs a periodic tick every 20 seconds:- Query all leases where
expires_at <= now - For each expired lease:
- Load the last known order snapshot
- Send
LEASE_REVOKED { posReference, reason: "Lease expired", snapshot }to the terminal - Delete the lease from storage
- The order is now available for other terminals
Disconnect grace period
When a terminal’s WebSocket disconnects (network drop, browser close):- Hub does not immediately revoke leases
- Instead, sets lease expiry to
now + 10s(grace period) - If the terminal reconnects within 10 seconds, it can resume editing
- If grace period expires, the normal tick process revokes the lease
Manager override
Managers can forcibly take over an order held by another terminal:1
Request
Manager terminal sends
LEASE_ACQUIRE { posReference, force: true }2
Validate
Hub checks that
force is set and the requesting user has appropriate role3
Revoke
Hub sends
LEASE_REVOKED { reason: "Manager override", snapshot } to the current holder4
Grant
Hub grants the lease to the manager. Manager receives
LEASE_GRANTED with the latest versionLEASE_DENIED response. Managers see a confirmation dialog before sending the force-acquire.
Lease storage
Leases are stored in SQLite (nu_pos_hub/src/storage/leaseStore.ts):
grant(), renew(), release(), getExpired(now), getByTerminal(id).
Frontend lease handling
In hub mode,hubTransport.ts manages lease state:
acquireLease()sendsLEASE_ACQUIREand returns aPromise<LeaseResult>that resolves onLEASE_GRANTEDorLEASE_DENIED(or times out after 10 seconds)releaseLease()sendsLEASE_RELEASEand removes from internalheldLeasessetisLeaseHeld()checks the internal setonLeaseRevoked()fires when hub sendsLEASE_REVOKED
directTransport.ts uses local tab-level locks (orderLocks.ts) with a 45-second TTL. No network coordination exists.