Cybersecurity-Projects/PROJECTS/advanced/monitor-the-situation-dashb...
CarterPerez-dev 44f0ff3c3d feat(monitor/frontend): shared Panel shell + KPI + Sparkline + StaleIndicator primitives
The shared shell every operator panel uses. 28px header strip with
UPPERCASE letter-spaced title (--type-label, --fg-2), optional muted
subtitle (--fg-3), StaleIndicator dot, and an external-link "raw"
icon → upstream source. Body fills remaining space and scrolls
internally. Border-bottom 1px --fg-4 separates panels in the column.

Primitives in shared/:
  KPI — value (mono, --type-num-l, tabular-nums, --fg-1) + label
        (UPPERCASE, --type-label, --fg-3). No card wrapper, no
        rounded corner, no background. The number IS the unit.
  Sparkline — d3-scale + d3-array driven inline SVG. Single stroke
        in currentColor, no axes / no fill / no animation. Used
        only when the shape IS the insight (per ethos). Returns
        null with <2 points so empty panels don't ship a flat line
        as a placeholder.
  StaleIndicator — 6px square dot. --ok green (fresh), --amber
        (stale, breached threshold), --fg-4 muted (unknown / no
        data yet). Title attribute carries last update UTC time
        for hover context.

RawLink not extracted yet — Panel inlines the icon since one raw
link per panel is the rule. Extract when a panel needs more.

Adds d3-scale 4.0.2 + d3-array 3.2.4 + their @types — small d3
modules (not the full umbrella) per spec §10.3 to keep bundle lean.

Plan 5 Task 13 Design QA: panel header 28px, title UPPERCASE
letter-spaced, sparkline pure stroke (no axes / fill), stale dot
6px, no border-radius anywhere, no hover scale.
2026-05-03 07:12:20 -04:00
..
backend fix(monitor/collectors/gdelt): GDELT 'value' field is float64 not int (caught in live verify) 2026-05-02 04:42:39 -04:00
conf fix(monitor/nginx): strip /api prefix on WS proxy_pass so backend route /v1/ws is reached 2026-05-01 21:38:04 -04:00
frontend feat(monitor/frontend): shared Panel shell + KPI + Sparkline + StaleIndicator primitives 2026-05-03 07:12:20 -04:00
.env.example fix(monitor): pick conflict-free host ports (8432/5432/4432/6432/3432); JWT keygen-on-boot, healthcheck path, baseline migration; ignore frontend/.pnpm-store; pre-commit excludes 2026-05-01 20:13:00 -04:00
.gitignore fix(monitor): pick conflict-free host ports (8432/5432/4432/6432/3432); JWT keygen-on-boot, healthcheck path, baseline migration; ignore frontend/.pnpm-store; pre-commit excludes 2026-05-01 20:13:00 -04:00
Justfile chore(monitor): replace Makefile with Justfile (project convention) 2026-05-01 14:53:28 -04:00
README.md docs(monitor): add four world-collector rows to data source matrix 2026-05-02 04:43:03 -04:00
cloudflared.compose.yml feat(monitor/infra): three compose files, Makefile, .env.example 2026-05-01 06:17:28 -04:00
compose.yml fix(monitor): pick conflict-free host ports (8432/5432/4432/6432/3432); JWT keygen-on-boot, healthcheck path, baseline migration; ignore frontend/.pnpm-store; pre-commit excludes 2026-05-01 20:13:00 -04:00
dev.compose.yml fix(monitor): pick conflict-free host ports (8432/5432/4432/6432/3432); JWT keygen-on-boot, healthcheck path, baseline migration; ignore frontend/.pnpm-store; pre-commit excludes 2026-05-01 20:13:00 -04:00

README.md

Monitor the Situation

Operator-grade real-time situational awareness dashboard. Single-binary Go backend, React 19 frontend, Postgres + Redis, fronted by nginx and (optionally) a Cloudflare Tunnel.

The phrase "Monitoring the situation" is a Twitter/X meme from June 2025. This is the version that actually monitors the situation.

Stack

Layer Tech
Backend Go 1.22, chi router, coder/websocket, goose migrations
Frontend React 19, Vite, TanStack Query, Zustand, MapLibre, D3
Storage Postgres 16 (BRIN-indexed time-series), Redis 7
Ingress nginx (dev + prod), Cloudflare Tunnel (prod)
Build / run just recipes, multi-stage Docker, air for live reload

Data sources

Panel Source Cadence Auth
Mass-scan firehose DShield (SANS ISC) 1h none
Internet outages + BGP hijacks Cloudflare Radar 5m CF_RADAR_TOKEN (Radar:Read scope)
CVE velocity + EPSS NVD CVE 2.0 + FIRST EPSS 2h NVD_API_KEY (optional, raises rate limit)
CISA KEV CISA KEV catalog 1h none
Ransomware victims ransomware.live 15m none
Live BTC + ETH ticks Coinbase Advanced Trade WS persistent none
Earthquakes (M2.5+) USGS GeoJSON feed 1m none
Space weather NOAA SWPC (5 endpoints) 1m / 3h none
World events Wikipedia ITN + GDELT v2 API 5m / 15m none
ISS position wheretheiss.at + CelesTrak 10s / 24h none
IP enrichment GreyNoise Community on-demand GREYNOISE_API_KEY (optional, free tier)

Quickstart (development)

cp .env.example .env
# fill .env: POSTGRES_PASSWORD, JWT_SECRET, NOTIFICATION_ENCRYPTION_KEY
just dev-start
just migrate-dev
open http://localhost:8432

JWT signing keys auto-generate at backend/keys/private.pem on first boot. The dev stack binds host ports 8432 (nginx) / 5432 (backend) / 4432 (postgres) / 6432 (redis) / 3432 (vite).

Smoke checks

curl -s http://localhost:8432/api/v1/healthz
curl -s http://localhost:8432/api/v1/snapshot | jq .
docker run --rm -i --network host ghcr.io/vi/websocat:latest \
    "ws://localhost:8432/api/v1/ws?topics=heartbeat"

Production (Cloudflare Tunnel)

cp .env.example .env
# fill production secrets including CLOUDFLARE_TUNNEL_TOKEN
just tunnel-start
just migrate

Tests

cd backend && go test -race ./...

Layout

backend/    Go services (cmd/api, internal/{events,bus,ws,snapshot,collectors,...})
frontend/   React 19 dashboard
conf/       nginx and per-environment Docker configs
migrations/ goose SQL migrations (mounted into the backend container)