# Command Dashboard Operator Handoff Links + Read-only Pages

Use after a sensitive multi-domain command dashboard demo opens correctly but feels too passive: operators need safe navigation into legacy/domain tools and useful read-only sidebar pages, without enabling writes.

## Pattern

1. Keep links runtime-configured only:
   - `FINANCE_LEGACY_DASHBOARD_URL`
   - `HEALTH_LEGACY_DASHBOARD_URL`
   - `AUTOSHORTS_LEGACY_DASHBOARD_URL`
   - `JARVIS_OPERATOR_PUBLIC_ORIGIN`
   - `JARVIS_ALLOW_TAILNET_LINKS=0` by default.
2. Never hardcode current Tailnet/Tailscale IPs or demo ports. If a Tailnet link is running for the user, report it as the current runtime link only.
3. Add a server-side legacy-link validator:
   - allow `http`/`https` only;
   - allow `localhost` / `127.0.0.1` by default;
   - allow Tailnet `100.64.0.0/10` only with explicit flag;
   - block external hosts, userinfo, querystrings, and URL substrings such as token/secret/oauth/access_token/refresh_token/password/api_key;
   - fail closed and return sanitized status only.
4. In API status endpoints such as `/api/demo-info`, expose only link status categories:
   - `configured`
   - `not_configured`
   - `blocked`
   Do not expose full URLs, ports, paths, env values, or secrets.
5. Extend `ModuleSnapshot.links` with optional legacy handoff links for each module while keeping `primary_action` link-only:
   - Finance: Open Finance Dashboard
   - Health: Open Health Status
   - AutoShorts: Open AutoShorts Dashboard
   - System: Open System Status
   If missing/blocked, include a disabled link descriptor with a clear blocked reason.
6. Make module detail pages operator-usable with three sections:
   - Status / Kennzahlen
   - Quellenstatus
   - Arbeitswege
   Include a visible note: legacy links open external tools; JARVIS does not execute actions.
7. Make formerly passive sidebar pages useful but read-only:
   - `/approvals`: group current attention/review items by module; only show Open module and optional legacy handoff; no Approve/Reject controls.
   - `/reports`: safe placeholder/report index; no PDFs, file listings, report generation, or raw report content.
   - `/settings`: sanitized runtime overview with demo profile, actions=false, adapter modes, and legacy-link status categories only.
8. Smoke should check all operator routes, not only home:
   - `/`, `/approvals`, `/reports`, `/settings`, `/finance`, `/health`, `/autoshorts`, `/system`.
   - Verify no iframes, no secret/path/media leaks in API output, max module/KPI counts, primary actions link-only, and legacy-link status values are category-only.

## Pitfalls

- Smoke/tests that forbid words like Upload/Render/Publish should distinguish forbidden buttons/actions from explanatory locked-action text. Prefer neutral copy such as “Domain command flows are disabled” if the smoke scans visible text broadly.
- Pinia store tests may inject partial module objects; settings/runtime views should tolerate missing `notes` and fall back to source type or mock instead of reading `notes.find(...)` blindly.
- Safety scanners may flag synthetic secret/querystring fixtures in legacy-link tests; add narrowly scoped allowlist entries for the dedicated test file rather than broad `apps/**` exceptions.
- Do not use Tailnet links for automatic backend API calls merely because they are allowed for human handoff. That needs a separate design and approval.

## Verification

Run and report real output:

```bash
make test-gateway
make dashboard-test
make dashboard-build
python3 scripts/verify-repo-safety.py
make verify
make smoke-operator-demo
```

If restarting a Tailnet demo for the user, verify `/api/healthz`, `/api/demo-info`, `/api/overview`, the frontend root, a representative page such as `/settings`, and safe API output before sharing the current runtime link.