--- status: accepted --- # Diagnosis access boundary: detail-free overview, why behind admin gate ops-db-ui diagnosis wiring, epic #139. Relates to ADR-0001 (live-check contract) and the data-transfer error-breadcrumb work. ## Context Two audiences watch the transfer pipeline. **Observers** (scientists, data-reduction users) cannot act on the system — no tokens, no SSH — but need to know *what is stuck where* ("did my data leave the site, is my package stuck"). **Admins** can act and need the *why*. The new "dive into the diagnosis" UI hangs a **Diagnose** action off the shown failures; the question is who may see the `error_context` (paths, hosts, traceback, the **Diagnosis** line). The constraint that decides the design: `GET /api/transfer/overview` **and** the `/ws/overview` WebSocket are currently **anonymous** at the API layer, and the WebSocket broadcasts one shared payload to every connected client. Today that payload embeds each failed operation's `error_context`/`failure_error_message`, so the why is already broadcast to anyone listening. ## Decision The boundary is **structural, not a per-role strip.** The overview payload (HTTP and WebSocket) carries **no error text for anyone**: `error_context`, `failure_error_message`, and `unpack_error_context` are removed from the failed-op entries. What remains is what an Observer needs — package, location pair, stage, status, retry count, ids, timestamps. The *why* flows **only** through the already-admin-gated endpoints (`/api/admin/diagnose/{type}/{id}`, `/api/admin/diagnose/by-package/{name}`, `failure-history`, and the live-check trigger/poll). The UI's **Diagnose** action (admin-only via `auth/isAdmin`) is the single gateway to a **Diagnostic Report**; there is no inline **Diagnosis**, even for admins. ## Considered options - **(a) Detail-free overview (chosen).** The overview stays anonymous and broadcastable; the boundary is enforced by *where `error_context` lives* (behind the admin gate), not by *who is listening*. Cost: admins lose the inline breadcrumb in the failure panel and click **Diagnose** to see the why. - **(b) Role-aware overview.** Authenticate `GET /overview` and `/ws/overview`, include `error_context` for admins, null it otherwise. Rejected: retrofits auth onto a hot anonymous endpoint **and a broadcast WebSocket** — role-aware stripping over WS needs per-connection filtering or two payload variants, a bug-prone surface for a diagnostic nicety. The leak would also remain one schema change away from reappearing. ## Consequences - The inline `FailureBreadcrumb` rendered inside `FailureAlertPanel` is removed; the breadcrumb now lives only inside the Diagnose drawer / `/admin/diagnose` page (`OperationDiagnosisRow` already renders it). - `/overview` and `/ws/overview` need **no** auth changes — the reason this option was chosen. - Removing `error_context` from `TransferOverview` and its failed-op entries is a payload change; the ops-db-ui consumer must move why-display into the admin-gated Diagnose path in lockstep. - The boundary holds only as long as no other anonymous endpoint re-exposes `error_context`. New failure-surfacing endpoints must keep error text behind the admin gate.