Files
dashboards/.agents/phases/todo/29_gateway_dashboard_page/00_phase.md
T
ducoterra 558a83d0be chore(agent): phase roadmap from TODO.md, 2 phases
Convert the gateway-visualisation TODO item into phases 28 (gateway_agent_proxy: agent sections for haproxy/nginx configs + live sockets, 1.2.0, capture, rollout) and 29 (gateway_dashboard_page: source, wiring, API, animated-pipe page, nav, docs). Clear TODO.md.

LLM-Generated: true
2026-10-21 16:24:01 -04:00

6.1 KiB

Phase 29 — The gateway dashboard page: animated pipes from config + live sockets

Source: TODO.md L3 — "Create a 'gateway' page that visualizes my incoming connections based on the gateway host's nginx and haproxy configs. I want it to show animated pipes with data coming in (IPs, ports, domains, etc.) and data going out (IPs, ports, domains, etc.)." (the dashboard half) Story: n/a Context: phase 28 (the haproxy/nginx/sockets agent sections, AGENT_VERSION 1.2.0, the gateway host rolled out), src/dashboards/config.py (Settings, _env, the nut_enabled/actions_allowed gate properties), src/dashboards/sources/disks.py (the "a source of its own reading the same agents" precedent — the disks:<host> job dials the same /snapshot), src/dashboards/poller.py (job loop, health records, stale/last-good), src/dashboards/app.py (build_hub — the job list wired once for production and the injected-store test path; the agent_tls_map the host:/disks: jobs share per host), src/dashboards/api.py (the snapshot/history route pairs, the 404 {"configured": false} gate that runs before any dial, _history_batch, the /api/summary additive blocks), src/dashboards/alerts.py (the "an unconfigured source never alerts" rule), src/dashboards/store.py (the sample(source, host, metric, res, ts) + rollup/retention story — invariant 9), src/dashboards/web/pages/ + static/ (the five dashboards, the five-tab masthead pinned by tests/test_api.py, app.js's initChrome badges, Dash.hero/DashCharts — no framework, no CDN, invariant 5), tests/test_api.py (the served-bytes pins), tests/test_config.py, tests/test_live_fleet.py (the read-only live suite).

Objective

The fifth dashboard page — /gateway — renders the gateway host's connection topology as animated pipes: for every configured frontend (haproxy bind, nginx listen, the stream 443) an inbound pipe (client → gateway: the port, the server_name domains, the live connection count and client addresses) and, fanning out from the gateway, one outbound pipe per configured target (haproxy server, nginx proxy_pass, the stream map entries: the target host:port, the live count). The page is a source of its own (gateway, its own poller job on the same /snapshot the host: job dials, its own interval/timeout/health/events), its counts are stored as gauges (invariant 9's rollup/retention story) and graphed from /api/gateway/history, and it keeps every invariant the fleet already keeps: unconfigured is 404 without a dial (invariant 3), a dead source ages instead of zeroing (invariant 4), no count is a rate (rule 2 — no estimator, no Δ<window>), no count carries a verdict (like net.*), and an old agent (1.1.0, no sections) reads as "predates gateway support" — dimmed, n/a, never red.

Dependencies

  • 28_gateway_agent_proxy (todo) — the three agent sections on the wire and the gateway host running 1.2.0 (the live half of this phase's verification dials what 28 installed)
  • 27_mobile_tables_and_audit (todo) — the page's 360 px behaviour is audited by 27's script shape (the table, if any, scrolls inside its card)

Tasks

  1. 01_config_and_source.md — GATEWAY_HOST in Settings + the gate properties; sources/gateway.py: the config + sockets join into inbound/outbound pipes, gauges only, the three-way agent support
  2. 02_wiring.md — the gateway poller job in build_hub (30 s), health, the alerts rules, the /api/summary block, the /api/config entry
  3. 03_api_routes.md — /api/gateway/snapshot + /api/gateway/history: the configured gate before any dial (spy-pinned), history through _history_batch, unknown names as empty series
  4. 04_page.md — /gateway + page-gateway.js: the animated pipes (hand-rolled SVG + CSS, no CDN, prefers-reduced-motion, aria-label), the live counts, the three degradation arms
  5. 05_nav_and_summary.md — the sixth tab on every signed-in page (the pinned masthead pins updated + browser-measured), the /api/summary pin extended
  6. 06_docs_and_verify.md — README/AGENTS (the page row, the GATEWAY_HOST row, the fleet fact), the browser screenshot pass against live data, the read-only live verification

Testing & Quality

  • Unit: tests/test_gateway_source.py (new — the join over fixture snapshots: an inbound join by listen port, the domain-target port-only join labelled "by port", the raw-IP exact join, the $var→map join, the 1.1.0-predates arm, the never-answered stub arm); tests/test_config.py (GATEWAY_HOST default, the gate property); tests/test_api.py (the route pins, the summary block, the nav pins)
  • Coverage: >90% on sources/gateway.py + the new config field
  • Real-browser check (invariant 10): task 05's measurement + task 06's screenshots against the live dev server

Documentation & Consistency

  • Task 06 updates AGENTS.md's page table (/gateway row), the config.py bullet (GATEWAY_HOST), and the metric-naming paragraph (gateway.* gauges); README.md gains the page's how-to row. The invariant 9 story: the gateway.* series roll up raw → 1 m → 1 h like every other gauge.

Completion Criteria

  • uv run pytest green (the hermetic suite), coverage not down; uv run pytest --cov holds the 94% floor
  • GATEWAY_HOST unset (or a name not in HOST_TARGETS) → /api/gateway/snapshot answers 404 {"configured": false} with no dial (spy-pinned) and the page says "not configured"
  • a 1.1.0-style snapshot (no sections) → the page's "agent predates gateway support" arm, dimmed, every number n/a, the job still ok (the invariant-3 shape, pinned)
  • the gateway host (1.2.0, post-phase-28) → the page renders the six haproxy frontends, the nginx server blocks and the stream map, with live counts that match a direct /proc/net/tcp read (task 06's verification)
  • bash .agents/validate.sh green
  • no behavior change in completed work: the nine existing sources read ok in ./scripts/dev-server.sh status, the five existing dashboards render as before, the /api/summary pre-existing blocks are byte-identical in shape (the gateway block is additive)