# Tailnet multi-dashboard handoff

Use when the user asks to start several existing local dashboards and return Tailscale IP + ports, especially for JARVIS/Finance/Health/AutoShorts style operator handoffs.

## Durable lessons

1. **Preserve occupied fixed ports explicitly.** If a user says a dashboard port is fixed (e.g. FamilyDashboard on `5173`), do not let helper scripts auto-select or steal that port for another service. Pick separate ports up front and document that the fixed service is unchanged.
2. **Prefer an existing orchestrator, but inspect its port assumptions.** JARVIS `make handoff` is useful, but its defaults may assume Finance frontend on `5173`. Override those ports before launch.
3. **If Vite/Node is unavailable but built `dist/` exists, serve the compiled dashboard instead of declaring failure.** A small Python static server with optional `/api` reverse proxy can expose Vue/Vite builds over the Tailnet without rebuilding. This is especially useful when a handoff script fails with a frontend launcher error: keep/stop partial services cleanly, then start the APIs directly and serve the already-built dashboards from ignored `.tmp/` copies.
4. **Patch baked API URLs only in the temporary served copy, not in source.** Copy `dist/` into an ignored temp directory, replace baked localhost API bases there, and leave repo files unchanged. For Vue/Vite bundles, search the copied JS assets for defaults such as `http://127.0.0.1:8080` and replace them with the runtime Tailnet API URL.
5. **For frontends with relative API calls, proxy `/api` from the static server.** This avoids rebuilding just to set `VITE_API_BASE_URL`. The proxy must preserve GET/POST/PATCH when the specialist dashboard itself expects them, but the JARVIS handoff layer remains link-only/read-only.
6. **Health dashboards may be static HTML.** If an approved static dashboard file exists, serve it read-only over a Tailnet-only port and do not inspect or expose raw medical data.
6. **Health dashboards may be static HTML.** If an approved static dashboard file exists, serve it read-only over a Tailnet-only port and do not inspect or expose raw medical data.
7. **Verify reachability before replying.** Probe each final URL over the Tailscale IP and report only URLs that returned successfully. Include APIs only if relevant; the user usually asked for dashboards.
8. **Verify browser CORS, not only backend curl.** For dashboards whose frontend port may be dynamically reassigned, send `Origin: <actual-frontend-url>` to the API and require `access-control-allow-origin` to match; otherwise the API can return 200 in curl while the browser shows empty data.
9. **Prefer each dashboard backend's own venv.** Start Finance/AutoShorts/etc. APIs with their repo `.venv/bin/python` when available, falling back to `python3` only if absent, so runtime dependencies do not accidentally depend on the Hermes agent venv.
8. **A dashboard `200 OK` is not enough.** For data-backed frontends, also verify the backend's safe JSON shape and that CORS allows the actual frontend origin chosen by the handoff. Dynamic fallback ports are a common cause of “dashboard opens but no data”. See `references/tailnet-dashboard-cors-readiness.md`.

## Example safe port allocation

- FamilyDashboard fixed: `5173` — do not change or repurpose this port for Finance/JARVIS.
- JARVIS Dashboard: `5175`; API: `8011`.
- Finance Dashboard: `5176`; API: `8012`.
- AutoShorts Dashboard: `5177`; API: `8013`.
- Health static dashboard: `8014`.

## Minimal static server pattern

Create a temporary Python server under the ignored handoff temp directory. It should:

- bind to the Tailscale IP and selected port;
- serve files from a copied `dist/` directory;
- optionally fall back to `index.html` for SPA routes;
- optionally proxy `/api/*` to the corresponding local/Tailnet API;
- write PID/log files under `.tmp/` or another ignored runtime directory.

Do not commit generated temp servers, copied dist directories, logs, PID files, or runtime DBs.

## Reporting format

Keep the final response short and operational:

- `Tailscale IP: <ip>`
- one bullet per dashboard: `<Name>: http://<ip>:<port>`
- note fixed/unchanged ports separately, especially FamilyDashboard `5173`.

Avoid long troubleshooting narratives unless something failed.