> ## 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.

# Local development quickstart

> Step-by-step guide to run the full sync stack locally with real-time multi-terminal sync.

## Prerequisites

* **Node.js 20+** (`node -v` to check)
* **Odoo 12** running locally at `http://localhost:8069` with a POS session open
* A terminal with 3 tabs

## Step 1: Build the protocol package

The protocol package must be built first since the hub depends on it.

```bash theme={null}
cd nu_pos_sync_protocol
npm install
npm run build
```

## Step 2: Start the hub in dev mode

Open a **new terminal tab**:

```bash theme={null}
cd nu_pos_hub
npm install
```

Create a `.env` file for local development:

```bash title=".env" theme={null}
ODOO_URL=http://localhost:8069
ODOO_DB=pos
ODOO_USER=admin
ODOO_PASSWORD=admin
HUB_HTTP_PORT=8766
HUB_DB_PATH=./dev.sqlite
HUB_LOG_LEVEL=debug
HUB_MDNS_ENABLED=false
HUB_DEV_AUTH=true
```

<Note>
  `HUB_DEV_AUTH=true` enables dev-mode auth bypass. When the frontend sends an empty token, the hub authenticates using its own Odoo credentials instead of rejecting the connection.
</Note>

Start with tsx (auto-reloads on changes):

```bash theme={null}
npm run dev
```

Verify it works:

```bash theme={null}
curl http://localhost:8766/health | python3 -m json.tool
```

Expected output:

```json theme={null}
{
    "status": "ok",
    "uptime": 5,
    "connectedTerminals": 0,
    "activeLeases": 0,
    "orders": 0,
    "pendingForward": 0,
    "totalForwarded": 0,
    "lastOdooSync": null
}
```

Open `http://localhost:8766/` in a browser to see the live dashboard.

## Step 3: Enable hub mode in Odoo

<Tabs>
  <Tab title="Odoo UI">
    1. Go to **Point of Sale > Configuration**
    2. Set `nu_hub_enabled` = checked
    3. Set `nu_hub_url` = `ws://localhost:8766`
    4. Save
  </Tab>

  <Tab title="Python shell">
    ```python theme={null}
    config = env['nu.pos.config'].search([], limit=1)
    config.write({
        'nu_hub_enabled': True,
        'nu_hub_url': 'ws://localhost:8766',
    })
    ```
  </Tab>

  <Tab title="Environment variable">
    In dev mode with mock bootstrap data:

    ```bash theme={null}
    NEXT_PUBLIC_HUB_URL=ws://localhost:8766 npm run dev
    ```

    This adds `nu_hub_enabled: true` and `nu_hub_url` to the mock bootstrap config automatically.
  </Tab>
</Tabs>

## Step 4: Start the frontend

Open a **third terminal tab**:

```bash theme={null}
cd nu_pos_react
NEXT_PUBLIC_HUB_URL=ws://localhost:8766 npm run dev
```

Open two browser tabs:

* Tab 1: `http://localhost:3000/pos-react/order/?table=1`
* Tab 2: `http://localhost:3000/pos-react/order/?table=1`

Both tabs should connect to the hub. The dashboard at `http://localhost:8766/` should show 2 connected terminals.

## Step 5: Test real-time sync

1. **In Tab 1**: Add an item to the order
2. **Watch Tab 2**: The item should appear within milliseconds
3. **Check the dashboard**: 2 terminals, 1 active lease, events flowing

### Test leasing

1. In Tab 1, open an order (acquires a lease)
2. In Tab 2, try to open the same order
3. Tab 2 should see a "LEASE\_DENIED"
4. Navigate away in Tab 1 (releases the lease)
5. Tab 2 can now acquire the lease

### Test offline recovery

1. Stop the hub (`Ctrl+C` in the hub terminal)
2. Add items in Tab 1 — they queue in the localStorage outbox
3. Restart the hub (`npm run dev`)
4. The frontend reconnects automatically and flushes the outbox

## Development workflow

```
Terminal 1 (hub):      npm run dev          # auto-reloads on src/ changes
Terminal 2 (frontend): npm run dev          # Next.js hot reload
Terminal 3 (tests):    npm run test         # vitest watch mode
Browser:               http://localhost:8766  # hub dashboard
```

## Inspecting the hub database

The dev SQLite file is at `nu_pos_hub/dev.sqlite`:

```bash theme={null}
sqlite3 nu_pos_hub/dev.sqlite

# See all orders
SELECT pos_reference, version, terminal_id, updated_at FROM orders;

# See active leases
SELECT pos_reference, terminal_id, role, expires_at FROM leases;

# See pending events
SELECT id, type, pos_reference, forwarded, created_at FROM events ORDER BY id DESC LIMIT 20;
```

## Testing with wscat

Connect to the hub directly to test the protocol:

```bash theme={null}
npx wscat -c ws://localhost:8766
```

Then send messages manually:

```json theme={null}
{"type":"AUTH","token":"your-odoo-session-id","deviceId":"test-001"}
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Hub won't start: Cannot find module '@nu/sync-protocol'">
    Build the protocol package first:

    ```bash theme={null}
    cd nu_pos_sync_protocol && npm run build
    ```
  </Accordion>

  <Accordion title="Auth fails for all terminals">
    Make sure Odoo is running and credentials in `.env` are correct. In dev mode, ensure `HUB_DEV_AUTH=true` is set.
  </Accordion>

  <Accordion title="Frontend stays in 'Connecting' state">
    1. Is the hub running? `curl http://localhost:8766/health`
    2. Is `nu_hub_enabled` set to `true`?
    3. Check browser DevTools Network tab for WS connections
    4. Look for connection errors in the browser console
  </Accordion>

  <Accordion title="Orders not forwarding to Odoo">
    1. Is Odoo reachable? `curl http://localhost:8069/web/login`
    2. Check hub logs for forwarding errors
    3. Ensure `ODOO_USER`/`ODOO_PASSWORD` has POS access
  </Accordion>

  <Accordion title="'Lease request timed out'">
    The hub didn't respond to `LEASE_ACQUIRE` within 10 seconds. Usually means the hub is overloaded or the message was lost. Check hub logs.
  </Accordion>

  <Accordion title="Stale dev.sqlite causing SQLite errors">
    ```bash theme={null}
    rm nu_pos_hub/dev.sqlite
    # Restart the hub — it recreates the database
    ```
  </Accordion>
</AccordionGroup>

## Environment variable reference

| Variable                  | Default                 | Description                  |
| ------------------------- | ----------------------- | ---------------------------- |
| `ODOO_URL`                | `http://localhost:8069` | Odoo instance URL            |
| `ODOO_DB`                 | `pos`                   | Odoo database name           |
| `ODOO_USER`               | `admin`                 | Odoo user for hub session    |
| `ODOO_PASSWORD`           | `admin`                 | Odoo password                |
| `HUB_HTTP_PORT`           | `8766`                  | HTTP + WebSocket port        |
| `HUB_DB_PATH`             | `./hub.sqlite`          | SQLite database file         |
| `HUB_LOG_LEVEL`           | `info`                  | Log level                    |
| `HUB_MDNS_ENABLED`        | `true`                  | mDNS discovery               |
| `HUB_LEASE_TTL_MS`        | `60000`                 | Lease duration (ms)          |
| `HUB_HEARTBEAT_MS`        | `20000`                 | Heartbeat interval (ms)      |
| `HUB_GRACE_MS`            | `10000`                 | Disconnect grace period (ms) |
| `HUB_FORWARD_INTERVAL_MS` | `5000`                  | Odoo drain interval (ms)     |
| `HUB_FORWARD_BATCH_SIZE`  | `20`                    | Events per drain batch       |
| `HUB_DEV_AUTH`            | `false`                 | Dev-mode auth bypass         |
