Files
Shadowbroker/frontend
Shadowbroker 19fb7f0b1e Fix #288: viewport-scoped live-data for heavy layers only (#294)
Reported by @tg12 in the external security/correctness audit.

Before this change, /api/live-data/{fast,slow} accepted s/w/n/e query
params but their Query() descriptions explicitly said "(ignored)". The
endpoints shipped the full in-memory world dataset on every poll:

    /api/live-data/fast → 16.88 MB
    /api/live-data/slow → 10.12 MB
                          ── 27 MB per poll cycle, regardless of zoom

For a node with N operators each polling at the steady 15s/120s cadence,
this is hundreds of MB/minute of outbound traffic that never gets used —
the GPU just culls everything outside the viewport client-side. On a
Tor-bridged or LTE-backed node, that bandwidth bill is the actual cost.

This change makes the existing s/w/n/e params honored — when all four
bounds are supplied, the backend bbox-filters a curated set of heavy,
density-driven, time-sensitive collections to that viewport (with the
existing 20% padding from _bbox_filter):

    /fast: commercial_flights, military_flights, private_flights,
           private_jets, tracked_flights, ships, cctv, uavs, liveuamap,
           gps_jamming, sigint, trains
    /slow: gdelt, firms_fires, kiwisdr, scanners, psk_reporter

Static reference layers (satellites, datacenters, military_bases,
power_plants, satnogs, weather, news, stocks, etc.) deliberately STAY
world-scale so panning never reveals an "empty world" of infrastructure.
That preserves the no-hostile-UX feel of the existing dashboard.

Behavior contract:

  * Without bbox params (or with a partial bbox), the response is
    byte-for-byte identical to the pre-#288 implementation. No
    behavior change for any existing caller that hasn't opted in.
  * World-scale bbox (lng_span >= 300 or lat_span >= 120) short-circuits
    filtering and shares the global ETag — zoomed-out operators all
    hit the same 304 cache exactly like before.
  * ETag now mixes a 1°-quantized bbox suffix when filtering engages,
    so two viewports never poison each other's 304 cache. Sub-degree
    pans land in the same ETag bucket (i.e. don't bust the cache on
    every mouse drag).

Polling cadence, rate-limit windows, and the 304 short-circuit are all
unchanged. Only the SIZE of the responses changes, and only when the
caller opts in via bounds.

Frontend wiring: useViewportBounds reuses the same coarsened/
expanded bounds it already computes for the AIS /api/viewport POST and
pushes them into a new module-level liveDataViewport store.
useDataPolling reads from that store via appendLiveDataBoundsParams
when building each live-data URL.

Tests cover: no-bbox → world data; bbox → heavy layers filtered;
bbox → reference layers untouched; world-scale bbox → no filter;
partial bbox → treated as no bbox; ETag changes with bbox; sub-degree
pan → same ETag; 304 path works; antimeridian-crossing bbox handled.

Co-authored-by: BigBodyCobain <moatbc@gmail.com>
2026-05-22 00:56:29 -06:00
..
2026-03-04 22:44:08 -07:00
2026-05-01 22:56:50 -06:00
2026-03-04 22:44:08 -07:00
2026-05-01 22:56:50 -06:00
2026-05-01 22:56:50 -06:00

ShadowBroker Frontend

Next.js 16 dashboard with MapLibre GL, Cesium, and Framer Motion.

Development

npm install
npm run dev        # http://localhost:3000

API URL Configuration

The frontend needs to reach the backend (default port 8000). Resolution order:

  1. NEXT_PUBLIC_API_URL env var — if set, used as-is (build-time, baked by Next.js)
  2. Server-side (SSR) — falls back to http://localhost:8000
  3. Client-side (browser) — auto-detects using window.location.hostname:8000

Common scenarios

Scenario Action needed
Local dev (localhost:3000 + localhost:8000) None — auto-detected
LAN access (192.168.x.x:3000) None — auto-detected from browser hostname
Public deploy (same host, port 8000) None — auto-detected
Backend on different port (e.g. 9096) Set NEXT_PUBLIC_API_URL=http://host:9096 before build
Backend on different host Set NEXT_PUBLIC_API_URL=http://backend-host:8000 before build
Behind reverse proxy (e.g. /api path) Set NEXT_PUBLIC_API_URL=https://yourdomain.com before build

Setting the variable

# Shell (Linux/macOS)
NEXT_PUBLIC_API_URL=http://myserver:8000 npm run build

# PowerShell (Windows)
$env:NEXT_PUBLIC_API_URL="http://myserver:8000"; npm run build

# Docker Compose (set in .env file next to docker-compose.yml)
NEXT_PUBLIC_API_URL=http://myserver:8000

Note: This is a build-time variable. Changing it requires rebuilding the frontend.

Theming

Dark mode is the default. A light/dark toggle is available in the left panel toolbar. Theme preference is persisted in localStorage as sb-theme and applied via data-theme attribute on <html>. CSS variables in globals.css define all structural colors for both themes.