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

# Deployment

> Raspberry Pi setup, Docker, systemd, mDNS discovery, and monitoring.

## Prerequisites

* **Node.js 20+** on the hub machine
* **Raspberry Pi 4/5** (recommended) or any ARM64/AMD64 Linux machine
* **Network**: Hub and terminals must be on the same LAN
* **Odoo**: Accessible from the hub (for token validation and event forwarding)

## Option 1: Bare-metal install (systemd)

Best for dedicated Raspberry Pi deployments.

<Steps>
  <Step title="Copy the source to the hub machine">
    ```bash theme={null}
    scp -r nu_pos_sync_protocol/ nu_pos_hub/ pi@<hub-ip>:/opt/
    ```
  </Step>

  <Step title="Run the installer (as root)">
    ```bash theme={null}
    sudo POS_HUB_DIR=/opt/nu_pos_hub bash /opt/nu_pos_hub/scripts/install.sh
    ```

    The installer:

    * Checks Node.js 20+
    * Creates a `pos-hub` system user
    * Creates `/var/lib/pos-hub/` data directory
    * Builds the protocol and hub packages
    * Creates `/etc/pos-hub/env` with default config
    * Installs and starts a systemd service
  </Step>

  <Step title="Edit the configuration">
    ```bash theme={null}
    sudo nano /etc/pos-hub/env
    ```

    Required changes:

    ```
    ODOO_URL=http://your-odoo-server:8069
    ODOO_DB=your_database
    ODOO_USER=admin
    ODOO_PASSWORD=your_password
    ```
  </Step>

  <Step title="Restart the service">
    ```bash theme={null}
    sudo systemctl restart pos-hub
    ```
  </Step>
</Steps>

### Service management

```bash theme={null}
systemctl status pos-hub          # Check status
systemctl restart pos-hub         # Restart
systemctl stop pos-hub            # Stop
journalctl -u pos-hub -f          # Follow logs
journalctl -u pos-hub --since "1 hour ago"  # Recent logs
```

### Security hardening

The systemd unit includes:

* `NoNewPrivileges=true`
* `ProtectSystem=strict` (read-only root filesystem)
* `ReadWritePaths=/var/lib/pos-hub` (only writable path)
* `ProtectHome=true`, `PrivateTmp=true`
* Runs as dedicated `pos-hub` user (no shell, no home)

## Option 2: Docker

Best for testing or multi-service deployments.

### Build

From the **repository root** (not `nu_pos_hub/`):

```bash theme={null}
docker build -t pos-hub -f nu_pos_hub/Dockerfile .
```

Supports both **ARM64** (Raspberry Pi) and **AMD64** (x86 servers).

### Run

```bash theme={null}
docker run -d \
  --name pos-hub \
  --restart unless-stopped \
  -p 8766:8766 \
  -v hub-data:/app/data \
  -e ODOO_URL=http://your-odoo-server:8069 \
  -e ODOO_DB=your_database \
  -e ODOO_USER=admin \
  -e ODOO_PASSWORD=your_password \
  pos-hub
```

### Docker Compose

```yaml theme={null}
version: "3.8"
services:
  pos-hub:
    build:
      context: .
      dockerfile: nu_pos_hub/Dockerfile
    ports:
      - "8766:8766"
    volumes:
      - hub-data:/app/data
    environment:
      - ODOO_URL=http://odoo:8069
      - ODOO_DB=pos
      - ODOO_USER=admin
      - ODOO_PASSWORD=admin
      - HUB_LOG_LEVEL=info
      - HUB_MDNS_ENABLED=false
    restart: unless-stopped

volumes:
  hub-data:
```

The Docker image includes a built-in health check (30s interval, 5s timeout, 3 retries).

## CLI tool

```bash theme={null}
pos-hub stats               # Show hub health and counts
pos-hub events              # Show last 20 events
pos-hub events 50           # Show last 50 events
pos-hub drain               # Wait for forwarder to flush pending events
pos-hub reset-leases        # Show active leases
pos-hub reset-data          # Reset all SQLite data
```

During development:

```bash theme={null}
cd nu_pos_hub
npm run cli -- stats
npm run cli -- events 50
npm run cli -- reset-data
```

### Example output: `pos-hub stats`

```
  POS Hub Status
  ──────────────────────────────
  Status:           OK
  Uptime:           2h 15m 30s
  Terminals:        4
  Active Leases:    2
  Orders:           47
  Pending Forward:  0
  Total Forwarded:  312
  Last Odoo Sync:   2/20/2026, 11:45:00 AM
```

## Hub dashboard

Access at `http://<hub-ip>:8766/` — a self-contained HTML page with:

* **Stats grid**: Connected terminals, active leases, total orders, pending/forwarded events
* **Terminals table**: ID, device, role, last seen
* **Leases table**: Order reference, terminal, role, expiry
* **Event log**: Last 50 events with type, reference, status, timestamp
* **Reset All Data button**: Clears all SQLite data with confirmation

Auto-refreshes every 3 seconds. No external dependencies (all CSS/JS inline).

## mDNS discovery

When `HUB_MDNS_ENABLED=true` (default), the hub advertises itself on the local network:

* Service type: `_pos-hub._tcp`
* Name: "POS Hub"
* Port: value of `HUB_HTTP_PORT`

<Note>
  mDNS doesn't work well in Docker. Disable it with `HUB_MDNS_ENABLED=false` for container deployments.
</Note>

## Enabling hub mode in Odoo

| Field                | Value                | Description                                            |
| -------------------- | -------------------- | ------------------------------------------------------ |
| `nu_hub_enabled`     | `True`               | Master switch for hub mode                             |
| `nu_hub_url`         | `ws://10.0.0.5:8766` | Hub WebSocket URL                                      |
| `nu_hub_fallback_ip` | `10.0.0.5`           | Fallback IP (auto-prefixed with `ws://` and port 8766) |

These values are included in the bootstrap payload and read by `createTransport()` on the frontend.

## Data persistence

| Data                               | Location                | Survives restart          |
| ---------------------------------- | ----------------------- | ------------------------- |
| Hub orders, leases, events, tables | SQLite at `HUB_DB_PATH` | Yes                       |
| Terminal order state               | IndexedDB (browser)     | Yes                       |
| Terminal outbox                    | localStorage (browser)  | Yes                       |
| Connected terminals (in-memory)    | RAM                     | No (reconnect on restart) |

## Monitoring checklist

| Check               | How                                                         |
| ------------------- | ----------------------------------------------------------- |
| Hub is running      | `systemctl status pos-hub` or `curl http://hub:8766/health` |
| Terminals connected | Dashboard or `pos-hub stats` → "Terminals" count            |
| Events forwarding   | `pos-hub stats` → "Pending Forward" should be 0 or low      |
| Odoo reachable      | `pos-hub stats` → "Last Odoo Sync" should be recent         |
| Disk usage          | `du -sh /var/lib/pos-hub/` (SQLite grows with events)       |
