Skip to content

Overview & architecture

Last reviewed against the codebase: 2026-07-25.

This is the narrative layer of the docs: what Fire Path AI is, why it is built the way it is, and how a fire detection becomes a property-specific alert. The exhaustive, always-current detail - every package, route, table and environment variable - lives in the generated reference, which is regenerated from the code and drift-gated on every build so it cannot silently fall out of sync. Read this page to understand the shape of the system; follow the cross-links for the specifics.

1. System context

Fire Path AI is an Australian bushfire situational-awareness and decision-support system. It turns live satellite fire detections, weather, terrain and a property’s own details into property-specific risk, spread estimates, time-to-impact and alerts - for residents, landholders and (as a roadmap) emergency services.

  • Users: individuals and landholders on mobile + web; an operations view for wider situational awareness. Access is organisation-scoped with four roles (homeowner, agency_officer, council_admin, system_admin) - see the API reference for the RBAC matrix.
  • Boundary: Fire Path AI complements official warnings; it does not replace agency systems. Predictions are physics-based estimates, not authoritative agency products.

2. The tiers

Mobile + Web app ──HTTPS──▶ API server ──▶ PostGIS (Postgres)
(Expo / MapLibre) (Node/Express) ▲ │
▲ │ │ └─▶ Redis queue ──▶ Wind-worker
│ │ │ (WindNinja, terrain wind)
└────── push/alerts ◀──────┤ │
├─ Ingest scheduler ─▶ FIRMS / DEA / OpenMeteo (external feeds)
└─ Alert evaluator ──▶ push (FCM/APNs) + SMS/email fallback

Client - a universal Expo / React Native app (artifacts/firepath-ai) targeting iOS, Android and a web SPA from one codebase, with MapLibre maps and an offline-map subsystem for coverage gaps (the pack/eviction logic is built; actual on-device tile download is not yet wired - see §7). It talks to the API over HTTPS and never contains database credentials.

API server - Node/Express (TypeScript) in artifacts/api-server, using Drizzle ORM over PostGIS. It exposes auth, assets, fires, predictions, forecast, alerts, check-in and geocoding routes (see the generated API reference). It is also the host for two in-process background loops:

  • the ingest scheduler (src/ingest/scheduler.ts) - overlap-guarded setInterval loops that pull external feeds, run the clustering pass, and precompute spread snapshots;
  • the alert evaluator (src/notifications/evaluator.ts) - matches active fires to a user’s assets and raises alerts, writing through a threat registry (threatRegistry.ts) that is the single writer for fire↔asset threats.

Terrain-wind worker - a separate container (Dockerfile.worker) that runs containerised WindNinja jobs pulled off a Redis / BullMQ queue. It is a distinct tier because WindNinja is CPU-heavy and slow, and must not block API request/response latency (see §4, step 4).

Data + queue - PostgreSQL + PostGIS is the system of record; Redis backs the BullMQ queue and cache. Both live in AWS in a full deployment - see Infrastructure.

3. Technology stack at a glance

LayerChoiceWhere
ClientExpo / React Native (iOS, Android, web SPA), MapLibreartifacts/firepath-ai
APINode.js / Express 5, TypeScript, Drizzle ORMartifacts/api-server
Shared spread geometry@workspace/spread-model (dependency-free trig)lib/spread-model
Schema / typesDrizzle schema + Zod contractslib/db, lib/api-zod, lib/api-spec
DatabasePostgreSQL + PostGIS (spatial)AWS RDS
Queue / cacheRedis (BullMQ)AWS ElastiCache
Terrain windWindNinja (containerised worker)Dockerfile.worker, src/sim/windninja.ts
InfraAWS ECS Fargate, ALB, RDS, ElastiCache, Terraform (IaC)see Infrastructure
Static sitesCloudflare Pages / Workers (marketing, docs, web-app shell)see Infrastructure
CI / testsVitest (API + client), k6 load, drift/redaction/link doc gatessee Testing

This is a pnpm monorepo - the artifacts/* deployables share code through lib/* workspace packages, so (for example) the spread-cone geometry is imported byte-for-byte identical by both the server and the client rather than reimplemented twice. The full per-package breakdown is the generated repository guide.

4. The fire-science model (physics, not “AI”)

The spread engine is deterministic fire physics - there is no machine-learning model in the prediction runtime. This is a deliberate, load-bearing decision: for a Government-grade life-safety product, an auditable formula you can defend line-by-line beats a black box, and (per the clustering module’s own header) ML adds no accuracy on this sparse, physically-constrained problem while losing auditability. The pipeline, in order:

  1. Satellite hotspots - NASA FIRMS and Geoscience Australia DEA detections are pulled by the ingest scheduler and stored as a per-detection time series (fires table, one row per hotspot pixel). See src/ingest/firms.ts and src/ingest/dea.ts.
  2. Fire-danger (FFDI) - the McArthur Forest Fire Danger Index is computed from OpenMeteo weather in src/ingest/ffdi.ts, using the standard BoM form FFDI = 2·exp(-0.45 + 0.987·ln(DF) − 0.0345·RH + 0.0338·T + 0.0234·V) and mapped to the AFDRS-aligned danger labels (Moderate → Catastrophic).
  3. Rate of spread - a McArthur Mk5 (grass, simplified) rate-of-spread drives every band, arrow and label. It lives in the shared @workspace/spread-model package (lib/spread-model/src/index.ts) as ros_kmh = max(0.5, 0.5 + 0.18·windKmh), so server-precomputed geometry and any client-side fallback are identical and auditable.
  4. Terrain-aware wind - WindNinja refines the wind field over terrain. It runs on the dedicated worker (src/sim/windninja.ts), asynchronously, because a terrain solve is slow and must never sit on the request path. The module supports two exec modes - docker for local dev and native for the ECS worker container (Fargate has no Docker daemon, so native is what makes it run in production).
  5. Spread cone + ETA - a wind-direction hazard cone (apex at the fire, arcing ±half-angle downwind) with the uncertainty half-angle widening with the forecast horizon (25° @ 1 h → 35° @ 3 h → 45° @ 6 h, from HAZARD_BAND_SPEC). The cone communicates “fire would likely head this way, this far, in this time” - it is not a physical cell-based fire simulation (that is future Cell2Fire work). Time-to-impact per asset is derived from the same ROS.
  6. Fire tracking - a deterministic clustering + temporal-linking pass (src/ingest/clustering.ts) turns the detection-centric fires table into a fire-centric model: ST_ClusterDBSCAN groups this cycle’s hotspots, then FEDS-style append-or-new linking carries each fire’s identity forward across ingest cycles (UPDATE / SPLIT / CREATE / MERGE / INACTIVATE), giving every fire a persistent id, a hull, an intensity, and an observable movement vector - with a lineage audit trail.

Honest positioning: validated for grassland; forest/shrubland is approximate (a single fuel model is applied uniformly today - per-landscape fuel data is a roadmap item). Prediction accuracy is not yet measured - a backtest/scorecard is the key roadmap deliverable, and until it exists you should describe outputs as physics-based estimates, never as validated or AI predictions.

5. External data feeds

The ingest scheduler (src/ingest/scheduler.ts) drives all of these on independent, config-driven intervals (config.firms.ingestIntervalMin, etc.), each cycle wrapped in try/catch so one failing sensor never stops the schedule, and each run recorded in the ingest_cycles table for observability.

FeedPurposeCadence
NASA FIRMSSatellite fire hotspots (with a mirror-URL retry on HTTP failure)~30 min
Geoscience Australia (DEA)Australian hotspots~10 min
OpenMeteoWeather → FFDI, wind, forecastper request/cycle
OSM / Overpass (places)“What’s around me” - refuges, fire/police stations, hospitals, fuel, water (PLACES_INGEST_ENABLED, off by default)daily
Elevation (DEM)Slope for rate-of-spread + WindNinja terrainper prediction

6. Deployment & cost model

Fire Path AI runs across two clouds: static surfaces (marketing, docs, the web-app shell) live on Cloudflare and run 24/7 at effectively no cost; only the AWS API backend (ECS Fargate + RDS + ElastiCache + ALB + NAT) has real running cost. That backend follows a hibernate/wake pattern - it tears down to ~$0 idle and rebuilds on demand, and on boot it self-bootstraps (migrates + seeds its own database and updates the api. DNS record) so a wake needs no manual steps.

This split - which surface is up, why the app shell still loads while the backend sleeps, the wake command, the two ECS tiers, and the Terraform module map - is documented in full in Infrastructure and Operations. This page just names the shape; those pages own the mechanics.

7. Capability status (keep this current - it is the tracked status of the codebase)

CapabilityStatus
Live satellite fire mapping (FIRMS + DEA)✅ Built, on
Property/asset risk + time-to-impact (ETA)✅ Built, on
Emergency alerts + push (SMS/email fallback)🟠 Pipeline built + on; end-to-end device delivery (FCM) not yet proven
Family check-in✅ Built, on
Offline map tiles🟠 Logic built (pack math + manager wrappers); on-device tile download/serve not yet wired/proven
Physics spread + hazard cone (McArthur Mk5)✅ Built, on
Fire tracking / movement (clustering)🟠 Built; enabled for the current dev cycle
Terrain-aware wind (WindNinja worker)🟠 Built; deploys on the next full wake
Prediction accuracy (backtest/scorecard)⬜ Planned - the key roadmap deliverable
Fuel/vegetation data (per-landscape model)⬜ Planned (grass model applied uniformly today)
Historical-fire learning (the only sanctioned ML)⬜ Planned - after the accuracy loop exists

8. Where the detail lives


Update triggers (when to revise this page + bump the date above)

Update this overview whenever any of these change: a new tier/subsystem or external feed; the prediction method or its honest-positioning caveats; the deployment/cost model; or a capability moving between built ↔ enabled ↔ planned (update the table in §7). The generated reference updates itself; this narrative does not - so this list is the discipline that keeps it true.