fix(archreview-p1): SEC-02/12/20 dashboard + observability hardening

- SEC-02: DashboardAuthorizationHandler restricts the loopback and
  Authentication:Mode=Disabled bypasses to read-only. They now satisfy only a
  Viewer-bearing requirement (AnyDashboardRole), never AdminOnly, so anonymous
  localhost can view the dashboard but cannot reach API-key CRUD or session
  Close/Kill at the policy layer (previously guarded only by service re-checks).
- SEC-12: DashboardSessionAdminService emits canonical AuditEvents through
  IAuditWriter (actions dashboard-close-session / dashboard-kill-worker, category
  SessionAdmin) on Success/Failure/Denied, mirroring the API-key audit path so
  destructive session actions leave durable, queryable rows.
- SEC-20: drop the unbounded session_id tag from the exported
  mxgateway.heartbeats.failed counter (per-session detail stays in the snapshot/log).

Docs updated same-change: CLAUDE.md (read-only loopback + 5-min bearer),
GatewayDashboardDesign.md (bypass scoping + session-admin audit), Metrics.md.
Server build clean; 30/30 targeted + 295/295 Dashboard/Security/App/Metrics sweep.
This commit is contained in:
Joseph Doherty
2026-07-09 07:47:51 -04:00
parent c2df4a0aae
commit 74e815c4d7
10 changed files with 347 additions and 20 deletions
@@ -351,7 +351,11 @@ public sealed class GatewayMetrics : IDisposable
_heartbeatFailures++;
}
_heartbeatFailuresCounter.Add(1, new KeyValuePair<string, object?>("session_id", sessionId));
// Exported without a session_id tag: per-session attribution is unbounded cardinality
// (every session ever opened mints a new exporter time series, bloating Prometheus/OTLP
// storage on long-running gateways). Per-session detail stays available via the dashboard
// snapshot and the log scope; the exported counter tracks only the aggregate.
_heartbeatFailuresCounter.Add(1);
}
/// <summary>