How akumascoffin/nswpsn-public-safety works
Purpose and Entry Points
NSW PSN is a real-time NSW public-safety monitoring platform: it aggregates emergency, weather, traffic, utility, aviation, marine, pager, scanner, and community incident data for static map/dashboard pages and Discord alerts. The current backend implementation is TypeScript/Node, despite stale README references to Flask.
Public entry pages are static root HTML such as index.html, map.html, live.html, logs.html, and dashboard.html. Backend startup is backends/node/src/index.ts and backends/node/src/server.ts. The Discord worker starts at discord-bot/bot.py. Distributed radio feeder software starts at feeder-nodes/radio-node/cmd/nodeagent/main.go.
Static Client and Identity Boundary
The frontend is build-free HTML/JS/CSS, with Leaflet-oriented map/editor experiences and client-side API calls. Shared client auth uses Supabase, while management access is mediated through backend Discord OAuth/session APIs.
Key paths: auth-common.js, map-editor-module.js, analytics.js. Frontend configuration is deployment-local (config.sample.js template); the static host is also a security/routing boundary via .htaccess.
Node API and Live Aggregation
The Node service exposes HTTP APIs for source data, maps, history, dashboard administration, incidents, status, and feeder operations. Route modules are split by domain; server.ts is the composition boundary between transport and application services.
Key paths: backends/node/src/server.ts, backends/node/src/api/status.ts, backends/node/src/api/dashboard.ts. APIs serve live/current data and operational controls while applying API-key, ingest-key, node-token, role, or Supabase JWT authentication as appropriate.
Source Polling, Normalization, and Caching
Source adapters obtain upstream feeds and normalize them into the platform’s source registry. A poller coordinates refresh cycles; source health and stale-while-revalidate caching isolate consumers from upstream failures. Waze differs: browsers running a userscript post regional payloads to an authenticated ingest endpoint rather than being scraped server-side.
Key paths: backends/node/src/services/poller.ts, backends/node/src/services/sourceRegistry.ts, backends/node/src/sources/registerAll.ts; Waze ingress: backends/node/src/api/waze-ingest.ts.
Live, Archive, and Analytics Data Stores
The service separates current in-memory/live state from persistent historical archives and derived filter/statistical caches. PostgreSQL is the durable backend store, initialized and evolved through SQL migrations. Archive “latest” sidecar/backfill/recompute services support efficient current-versus-history queries.
Key paths: backends/node/src/store/live.ts, backends/node/src/store/archive.ts, backends/node/src/db/migrate.ts. Schema history is under backends/node/src/db/migrations/, including incident, archive, dashboard, and feeder-node tables.
Incident, Scanner, and Alert Processing
Pager/radio and user incidents flow through domain APIs and services; scanner transcripts can be validated, quality-checked, summarized with an LLM, and converted into incident alerts. Images and Central Watch browser capture are handled as backend integrations rather than frontend responsibilities.
Key paths: backends/node/src/api/incidents.ts, backends/node/src/services/rdioIncidentAlerts.ts, backends/node/src/services/llm.ts. Browser-backed external integrations include backends/node/src/services/centralwatchBrowser.ts and backends/node/src/services/marinetrafficBrowser.ts.
Discord and Management Plane
The Python Discord bot polls platform data, resolves channel/preset subscriptions and mute/filter rules, and sends Discord messages. The Node dashboard APIs share a bot-data PostgreSQL database for dashboard state and queued actions; Discord OAuth links the web management UI to Discord identity.
Key paths: discord-bot/bot.py, discord-bot/alert_poller.py, discord-bot/database.py; backend-side integration: backends/node/src/services/discordApi.ts and backends/node/src/services/discordOauth.ts.
Feeder-Node Control Plane and Runtime
Remote radio nodes run a Go agent that maintains a WebSocket connection to the hub, relays scanner-related data, receives signed configuration updates, supervises local decoder processes, and supports self-update. The backend owns node registration, protocol, configuration merge/push, and WebSocket endpoints.
Key paths: feeder-nodes/radio-node/internal/wsclient/client.go, feeder-nodes/radio-node/internal/supervise/supervisor.go, backends/node/src/services/nodes/hub.ts. Material infrastructure includes Node.js, PostgreSQL, Python/Discord, Go feeder agents, Supabase authentication, Discord APIs, external government/community feeds, browser automation, and PM2 deployment configuration (backends/ecosystem.config.js).
Main entry points: server.ts Node API server · dashboard.ts Status & dashboard APIs · poller.ts Poller & source registry · index.html Public static pages
Components 23
Static client
Build-free public maps, dashboards, and management UI.
- Public static pages · Maps, live views, logs, and dashboards call platform APIs.
index.html - Client auth · Shared browser authentication for protected client features.
auth-common.js - Map editor
map-editor-module.js - Static host boundary · Serves static assets and applies host-level access rules.
.htaccess
Node platform
HTTP API, live aggregation, persistence, and control planes.
- Node API server · Composes routes, application services, and authentication boundaries.
backends/node/src/server.ts - Status & dashboard APIs · Serves operational status and dashboard administration.
backends/node/src/api/dashboard.ts - Incident API · Accepts and exposes pager, radio, and user incidents.
backends/node/src/api/incidents.ts - Waze ingest · Receives regional payloads from browser userscripts.
backends/node/src/api/waze-ingest.ts - Poller & source registry · Refreshes, normalizes, caches, and tracks health of upstream sources.
backends/node/src/services/poller.ts - Live state · Holds current source and operational state.
backends/node/src/store/live.ts - Archive service · Manages historical queries plus latest/backfill/recompute sidecars.
backends/node/src/store/archive.ts - PostgreSQL · Stores archives, incidents, dashboards, feeder records, and bot state.
backends/node/src/db/migrate.ts - Scanner alert pipeline · Validates transcripts, applies quality checks, summarizes, and emits alerts.
backends/node/src/services/rdioIncidentAlerts.ts - Feeder node hub · Registers nodes and pushes merged signed configuration.
backends/node/src/services/nodes/hub.ts
Discord plane
Alert delivery and Discord-linked management.
- Discord bot · Polls platform data and delivers subscription-filtered alerts.
discord-bot/bot.py - Alert poller · Resolves subscriptions, mute rules, and outbound alerts.
discord-bot/alert_poller.py - Discord OAuth & API · Links dashboard users to Discord identity and actions.
backends/node/src/services/discordOauth.ts
Feeder runtime
Remote radio-node connection and local decoder supervision.
- Radio node agent · Maintains hub connection and relays scanner-related data.
feeder-nodes/radio-node/cmd/nodeagent/main.go - Decoder supervisor · Supervises decoder processes and supports node self-update.
feeder-nodes/radio-node/internal/supervise/supervisor.go
External systems
Identity providers, upstream feeds, and browser-backed sources.
- Supabase
- Emergency & transport feeds · Government, community, weather, traffic, aviation, marine, pager, and scanner inputs.
- Browser integrations · Captures Central Watch and MarineTraffic data outside the frontend.
backends/node/src/services/centralwatchBrowser.ts - Discord APIs
Connections 28
- Static host boundary → Public static pages: serves
- Public static pages → Client auth: uses
- Client auth → Supabase: authenticates with
- Public static pages → Node API server: API calls
- Map editor → Node API server: map APIs
- Node API server → Status & dashboard APIs: routes to
- Node API server → Incident API: routes to
- Node API server → Waze ingest: routes to
- Node API server → Feeder node hub: hosts
- Status & dashboard APIs → Live state: reads current state
- Status & dashboard APIs → PostgreSQL: manages dashboard state
- Incident API → Scanner alert pipeline: submits incidents
- Scanner alert pipeline → Archive service: records alerts
- Poller & source registry → Emergency & transport feeds: polls
- Browser integrations → Poller & source registry: supplies captures
- Waze ingest → Poller & source registry: supplies regional data
- Poller & source registry → Live state: updates
- Live state → Archive service: archives
- Archive service → PostgreSQL: persists history
- Discord bot → Alert poller: runs
- Alert poller → PostgreSQL: reads subscriptions
- Alert poller → Node API server: polls platform data
- Discord bot → Discord APIs: sends messages
- Discord OAuth & API → Discord APIs: OAuth and actions
- Status & dashboard APIs → Discord OAuth & API: management identity
- Radio node agent → Feeder node hub: WebSocket connection
- Feeder node hub → Radio node agent: signed config
- Radio node agent → Decoder supervisor: supervises