# ⚓ The Bridge — ParVagues web dashboard

The always-on **Ship's Bridge**: a proper webserver dashboard with a live
**perf/thermal toolbar** (folded from `perf-tray.py`) and an extensible **hub**
of the fleet's tools (Foundry, Armada…). The web answer to "is there a ParVagues
service running a dashboard?" — now yes.

```bash
cd tools/bridge
python3 bridge.py serve            # → http://127.0.0.1:8773/
python3 bridge.py status           # one perf/thermal snapshot (JSON)
python3 bridge.py perf             # quick mode/temp line
python3 bridge.py perf cool        # switch perf mode (needs deploy, below)
```

## What it shows

- **The rig** — one **RIG UP** button over an honest status list. The button runs
  `tools/gig-up.sh --converge` (services: setlist, preload, faders, SuperDirt,
  watchdog, autoroute) and then launches Pulsar and Ardour, which converge
  deliberately refuses to open. The list is how you see what it actually did.
  A service whose unit is `active` while its process is **dead** reads `broken`,
  not `up` — `systemctl start` is a no-op on an active unit, and that exact case
  (2026-08-01) was a green unit over silence. Absent ≠ down: a thing you cannot
  start gets no button.

- **Toolbar** — live package-temp badge (colour tracks heat: green<55 ·
  amber<70 · orange<85 · red≥85, same thresholds as the tray), current perf mode
  as a segmented switch (Cool / Standard / Extreme / Normal), and freq / fan /
  thermal-throttle readouts. Polls `/api/perf` every 2 s.
- **Hub** — cards launching the fleet's GUIs (Foundry :8765, Armada :8731…).
  Extend by adding a dict to `HUB` in `server.py`.

## Why a web service (and how it relates to perf-tray)

`perf-tray.py` is a PyQt system-tray badge — great for an at-a-glance icon, but
not a dashboard and not reachable from a phone/another screen. The Bridge reuses
the tray's *proven, rootless* logic — `Thermals` (hwmon sysfs), `detect_mode()`,
the `sudo -n` mode switch — ported into `perf.py` with no Qt, and serves it over
HTTP. The two can coexist (tray icon + web dashboard); a later pass can DRY them
onto this one `perf.py`.

## Security — why it binds localhost

`POST /api/perf` flips the CPU governor via `sudo`. So the server **binds
`127.0.0.1` by default** — mode-switching is not LAN-triggerable. Opt into LAN
(e.g. to drive it from your phone) explicitly:

```bash
python3 bridge.py serve --host 0.0.0.0    # ⚠ exposes mode-switch to the LAN
```

## Deploy mode-switching (root-owned perf.sh + sudoers)

Reading thermals/mode is rootless. *Changing* mode runs
`sudo -n /usr/local/sbin/perf-audio --<mode>` against a **root-owned** copy of
`perf.sh`, whitelisted NOPASSWD via `perf-audio.sudoers` (shared with the tray —
never sudo the editable repo copy). If the binary isn't deployed the mode
buttons are disabled and the API returns a hint.

```bash
sudo install -m 755 -o root -g root \
     ~/Work/Sound/Tidal/perf.sh /usr/local/sbin/perf-audio
sudo install -m 440 -o root -g root \
     ~/Work/Sound/Tidal/perf-audio.sudoers /etc/sudoers.d/perf-audio
```

## Always-on (systemd --user)

```bash
systemctl --user enable --now \
  ~/Work/Sound/Tidal/tools/bridge/parvagues-bridge.service
# (or: cp the unit into ~/.config/systemd/user/ then enable --now)
```

## Tests

```bash
cd tools/bridge && python3 -m pytest tests/ -q   # pure perf logic (no sudo)
```

## Layout

```
tools/bridge/
  perf.py                    # rootless thermals + mode detect + sudo switch (from perf-tray)
  rig.py                     # RIG UP: rig status (unit AND process) + converge job
  server.py                  # stdlib http.server dashboard (GET/POST /api/perf, /api/hub)
  bridge.py                  # CLI (serve / status / perf)
  ui/index.html              # the dashboard (Ship's Bridge tokens)
  parvagues-bridge.service   # systemd --user unit (autostart)
  tests/test_perf.py
  tests/test_rig.py
```
