In the self-host model each team runs the stack locally with one board 1:1 over
USB, so the claim code (which only disambiguated boards in a shared pool) is
unnecessary. Add a LOCAL_MODE that auto-registers + auto-binds the board:
- API: `LOCAL_MODE` env + `GET /mode`; `POST /claim {local:true}` codelessly
binds the single board (`claimLocal`, rebinding a stale claim on a fresh
single-team stack); `POST /nodes/:team/disconnect` for the participant's own
release. Shared/LAN code path is unchanged.
- Frontend: `useLocalMode()` + `LocalBoardConnect` — detect → auto-bind →
"connected"; USB drop keeps the binding and auto-reconnects; explicit
Disconnect releases and waits for Reconnect. TeamRegistration swaps the code
card for it only when the API reports localMode.
- deploy/lan: compose sets `LOCAL_MODE=true` + `host.docker.internal:host-gateway`
(so the containerized API reaches the adb-forwarded board); `connect-board.sh`
forwards the tunnels and registers the board with the local stack (`--watch`
re-attaches on every reconnect).
Validated end-to-end through the Docker stack: mode → auto-bind → live status +
matrix mirror → disconnect → reconnect. 222 tests pass.
Co-Authored-By: Claude Opus 4.8 <[email protected]>