
Real-time OSINT intelligence dashboard: 7 live data sources (aircraft, AIS vessels, seismic, fires, weather, GDELT events, news) on an interactive globe. Correlation engine, scored alerts, watch mode. Bun + React 19 + Canvas 2D Web Worker rendering. PWA with offline support. https://sigint-5154d935429b.herokuapp.com/
Real-time OSINT dashboard with live aircraft, vessel, seismic, fire, weather, and event tracking on an interactive globe. Built with Bun, React 19, and a custom Canvas 2D + Web Worker rendering engine. Installable as a PWA.

git clone https://github.com/iitoneloc/sigint.git
cd sigint
bun install
Create a .env file in the project root with at minimum:
SIGINT_SERVER_SECRET=<output of openssl rand -hex 32>
Optionally add keys for ship and fire data:
AISSTREAM_API_KEY=<your aisstream.io key>
FIRMS_MAP_KEY=<your NASA FIRMS key>
See Deployment for dev, production, and Heroku options.
bun test # run all tests
bun test --watch # watch mode
bun run docker:test # run in Docker
bun run docker:dev:up # https://localhost (self-signed cert)
bun run docker:dev:down # stop
Two env vars short-circuit live data fetches in development so you can
work against a known frozen state. Both are gated on
NODE_ENV !== "production" and ignored in production builds.
Labels match /^[a-z0-9-]+$/ (OWASP A01 — strict allowlist before any
file lookup) and resolve to tests/fixtures/<source>/<label>.json.
Invalid labels throw at startup; missing files throw with the resolved
path. To use:
CYCLONES_FIXTURE=multi-storm bun run dev
AIRCRAFT_FIXTURE=conus-snapshot bun run dev
Or via Docker Compose (docker-compose.dev.yml passes both through):
CYCLONES_FIXTURE=single-cat5 bun run docker:dev:up
bun run docker:prod:up # http://localhost:5500
bun run docker:prod:down # stop
DOMAIN=sigint.example.com bun run docker:prod:tls:up
bun run docker:prod:tls:down # stop
git push heroku main
bun run docker:clean:all # remove containers, volumes, images
SIGINT is installable as a Progressive Web App. After visiting the deployed app:
The service worker caches the app shell for offline boot. Live data loads from IndexedDB when offline. An offline indicator bar appears when connectivity is lost, with a RETRY button and pull-to-refresh on touch devices. When an update is available, a banner prompts the user to reload — no silent mid-session code swaps.
Full technical docs in docs/ covering architecture, data flow, feature system, pane system, rendering, caching, search, and constraints.
Dual-licensed:
| Variable | Required | Description |
|---|
SIGINT_SERVER_SECRET | Yes | Auth token signing key. openssl rand -hex 32 |
AISSTREAM_API_KEY | No | aisstream.io key for live ship data |
FIRMS_MAP_KEY | No | NASA FIRMS key for fire data |
DOMAIN | No | Domain for Let's Encrypt TLS |
PORT | No | Server port (default: 5500) |
| Layer | Source | Poll |
|---|
| Aircraft | adsb.fi (server-side tile sweep, 37 tiles × 250 nm) | 240s |
| Ships | aisstream.io (server WebSocket) | 300s |
| Seismic | USGS (client-side) | 420s |
| Fires | NASA FIRMS (server-side) | 600s |
| Weather | NOAA (client-side) | 300s |
| Events | GDELT 2.0 (server-side) | 15m |
| News | 6 RSS feeds (server-side) | 10m |
| Env var | Source it overrides | Valid labels |
|---|
CYCLONES_FIXTURE | /api/cyclones/latest (server fetches NHC) | single-cat3, single-cat5, multi-storm, subtropical-example, tropical-depression, empty-out-of-season |
AIRCRAFT_FIXTURE | /api/aircraft/states (server fetches adsb.fi tile sweep) | (none shipped — capture via bun run scripts/capture-fixture.ts aircraft <label>) |