Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.laportenard.com/llms.txt

Use this file to discover all available pages before exploring further.

Leases enforce single-writer ownership on orders. Only the terminal holding the lease for an order can submit snapshots. This prevents two terminals from concurrently editing the same order and creating divergent state.

How it works

Acquiring a lease

  1. Terminal opens an order and sends LEASE_ACQUIRE { posReference, baseVersion }
  2. 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 }
  3. 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_HEARTBEAT every 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 finalSnapshot is 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:
  1. Query all leases where expires_at <= now
  2. 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):
  1. Hub does not immediately revoke leases
  2. Instead, sets lease expiry to now + 10s (grace period)
  3. If the terminal reconnects within 10 seconds, it can resume editing
  4. If grace period expires, the normal tick process revokes the lease
  Terminal                              Hub
     │                                   │
     ├──── WS Disconnect ───────────────►│  (unexpected close)
     │                                   │
     │                                   │  lease.expires_at = now + 10s
     │                                   │
     │      (reconnects within 10s)      │
     ├──── WS Connect ──────────────────►│
     ├──── AUTH { token } ──────────────►│
     │◄─── AUTH_OK ──────────────────────┤
     │                                   │  lease still valid → resume editing
     │                                   │
  ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ OR ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
     │                                   │
     │      (10s passes, no reconnect)   │
     │                                   │  tick(): lease expired
     │                                   ├──► LEASE_REVOKED sent to terminal
     │                                   │    lease deleted from storage

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 role
3

Revoke

Hub sends LEASE_REVOKED { reason: "Manager override", snapshot } to the current holder
4

Grant

Hub grants the lease to the manager. Manager receives LEASE_GRANTED with the latest version
Non-managers who attempt to access a held order see a LEASE_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):
CREATE TABLE leases (
  pos_reference TEXT PRIMARY KEY,
  terminal_id   TEXT NOT NULL,
  uid           INTEGER,
  role          TEXT,
  version       INTEGER NOT NULL DEFAULT 0,
  granted_at    TEXT NOT NULL DEFAULT (datetime('now')),
  expires_at    TEXT NOT NULL
);
Key operations: grant(), renew(), release(), getExpired(now), getByTerminal(id).

Frontend lease handling

In hub mode, hubTransport.ts manages lease state:
  • acquireLease() sends LEASE_ACQUIRE and returns a Promise<LeaseResult> that resolves on LEASE_GRANTED or LEASE_DENIED (or times out after 10 seconds)
  • releaseLease() sends LEASE_RELEASE and removes from internal heldLeases set
  • isLeaseHeld() checks the internal set
  • onLeaseRevoked() fires when hub sends LEASE_REVOKED
In direct mode, directTransport.ts uses local tab-level locks (orderLocks.ts) with a 45-second TTL. No network coordination exists.

Timing summary

ParameterValueConfigurable via
Lease TTL60sHUB_LEASE_TTL_MS env var
Heartbeat interval20sHUB_HEARTBEAT_MS env var
Disconnect grace10sHUB_GRACE_MS env var
Acquire timeout (frontend)10sLEASE_TIMEOUT_MS constant
Tick interval (hub)20sSame as heartbeat interval