# Tailnet dashboard CORS/readiness checks

Use when a local dashboard is exposed over Tailscale/Tailnet and the frontend opens but shows empty data, especially when ports are dynamically reassigned by a handoff script.

## Durable lesson

A backend can return `200 OK` to `curl` while the browser still blocks all useful data because the API does not allow the *actual* frontend origin. Always prove browser CORS from the final runtime origin, not only API reachability.

## Diagnostic sequence

1. Discover final URLs and ports from the handoff env/logs, not from defaults.
2. Probe frontend and API health:
   ```bash
   curl -fsS -o /dev/null -w '%{http_code}\n' "$FRONTEND_URL/"
   curl -fsS -o /dev/null -w '%{http_code}\n' "$API_URL/api/health"
   ```
3. Probe a safe data endpoint structurally. Redact sensitive values; report only keys/counts/booleans.
4. Probe CORS with the actual browser origin:
   ```bash
   curl -sS -D - -o /dev/null \
     -H "Origin: $FRONTEND_URL" \
     "$API_URL/api/overview" | tr -d '\r' | grep -i access-control-allow-origin
   ```
   Require the header value to equal `$FRONTEND_URL`.
5. If the frontend port was dynamically chosen, inject it into the backend CORS allowlist at process start via env rather than hardcoding a stale Tailnet host/port.
6. Restart the affected backend and frontend, then rerun the same checks.

## Implementation pattern

- Add a runtime env var such as `JARVIS_FINANCE_CORS_ORIGINS` / `<APP>_CORS_ORIGINS`.
- Validate origins conservatively: `http://localhost:<port>`, `http://127.0.0.1:<port>`, `http://100.x.x.x:<port>`, and trusted Tailnet hostnames such as `*.ts.net`.
- Have the handoff script set CORS origins from the actual computed URLs:
  ```bash
  APP_CORS_ORIGINS="$APP_FRONTEND_URL,$JARVIS_FRONTEND_URL,http://localhost:$APP_FRONTEND_PORT,http://127.0.0.1:$APP_FRONTEND_PORT"
  ```
- Prefer each dashboard backend's own `.venv/bin/python` when available so dependency drift in Hermes' venv does not mask missing repo setup.

## Robustness target

Provide a `make handoff-status` (or equivalent) that checks:

- dashboard root URL returns 200/302;
- API health returns 200;
- CORS matches actual frontend origin;
- runtime DB/source availability is true, reported without raw paths/values;
- key data endpoint returns non-empty structure by keys/counts only.
