Skip to content

Emergency services

Last reviewed against the codebase: 2026-08-08 (Phase 16.5 backend — agency delegated administration + jurisdictional authority, flag-gated; on 16.4 broadcast / area alerting + escalation, 16.3 operator exclusion zones, 16.2 Command Center, and 16.1-A..E agency control-plane).

Fire Path AI includes an emergency-services / agency persona alongside the resident-facing app. It is a decision-support + values-at-risk + prediction layer designed to support agency decision-making — never to replace official warnings or an agency’s own incident-management system. The authoritative, always-current status lives in the repo: PREDICTION-STATUS.md (prediction claims) and EMERGENCY-SERVICES-PLAN.md (the Phase 16 workstream), with the grounded audit in EMERGENCY-SERVICES-AUDIT-2026-08-07.md.

What exists today

  • RBAC foundation (production-grade). Four roles — homeowner, agency_officer, council_admin, system_admin — with memberships (user → org → role). requireRole() re-reads the user’s roles fresh from the DB on every request, fails closed, and audits denials. See Security.
  • Role-gated emergency-services spread preview (GET /predictions/spread-preview) — a temporal footprint readout for planning. Homeowner tokens receive 403; ops tokens receive a response flagged inDevelopment: true. Not alert-trusted.
  • Prediction-accuracy scorecard (GET /predictions/accuracy-scorecard + the “Prediction accuracy” screen) — the “prove-it” artifact grading the modelled corridor against observed fire perimeters. Honest by construction: it never says “validated” and shows an explicit “not yet a skill claim” state until enough real fires are scored and an SME signs off. Ops/agency only; homeowner tokens 403.
  • Command Center (preview, incident-command IA — Phase 16.2). A desktop three-pane ops view (map · situational awareness · command rail) and a mobile field scroll, reachable from the welcome screen. The situational rail is an incident list → incident detail flow (sharing the map’s pane-toggle pattern): each incident shows an ops-framed card (behaviour, movement, total FRP, recency — deliberately not the resident “distance from you”), and the selected incident drives the operator-selectable temporal spread preview. It is honestly labelled a preview and distinguishes live data (fires, tracked incidents, at-risk assets, telemetry) from illustrative sample data (crew positions + dispatch actions, grouped under one “Dispatch — preview” block). Values-at-risk are honestly scoped to the operator’s own registered assets.
  • Service Telemetry — a role-gated operational dashboard (ingest health, API usage, sim runs, cost).
  • Agency control-plane API (/admin/agencies/*, system_admin) — create an agency/council org, invite an officer (one-time hashed token), verify an officer, and list members/invitations. Every mutation writes an un-sampled admin_actions audit row.
  • In-app system-admin console — an “Agencies” screen (create / invite / verify / revoke) under a system_admin-only “Administration” nav section.
  • Invitee accept / sign-in (/auth/agency/*) — an invited officer redeems their token, sets a password, and gets a real account + membership in their agency org. Flagged off (AGENCY_ACCOUNTS_ENABLED) until onboarding; identity is pluggable (local now, per-agency SSO later).
  • Operator exclusion zones (/admin/zones/*, Phase 16.3) — a verified officer declares a no-go / restricted / prescribed-burn area (advisory overlays) or a suppress-hotspot zone (a known false-positive). Three stacked gates (requirePermission("zones.write") + requireVerifiedOfficer + assertZoneOrgAccess), the whole surface behind EXCLUSION_ZONES_ENABLED, and every write audited to admin_actions. A suppress-hotspot zone excludes its hotspots from the tracked-fire model and — only behind the separate ZONE_ALERT_SUPPRESSION_ENABLED flag, and never for an Emergency Warning — from the alert paths. Reason + expiry are required for suppression; expiry restores the fire. A non-silent “N active zones” banner + a FireMap map overlay keep suppression visible.
  • Broadcast / targeted area alerting (/admin/notifications/*, Phase 16.4) — a verified officer composes a warning and sends it to the people who own a registered asset inside a drawn radius or polygon. There is no boundary data (no LGA / postcode / state polygons), so the audience is exactly “registered asset-owners intersecting the shape” — never “everyone in the suburb”. Compose → preview the exact recipient count → confirm → send; the send fans out one per-user alerts row (source="broadcast") that rides the existing push + inbox paths. Four stacked gates (requirePermission("broadcast.send") + requireVerifiedOfficer + assertBroadcastAuthority + an explicit confirm for Emergency tier), the whole surface behind BROADCAST_ENABLED (off), every send audited. Emergency Warning is the only quiet-hours bypass. assertBroadcastAuthority is the single authority seam: today own-org, with a wide audience (over a tunable recipient/km² cap) restricted to system_admin; Phase 16.5 swaps its internals for State/region jurisdiction + per-staff grants without touching the route.
  • Delivery escalation queue (/admin/notifications/escalations, Phase 16.4-D) — an undelivered Emergency Warning past the escalation window now records a durable, ack/resolve-able escalation_events row in addition to the CloudWatch alarm log, so a life-safety non-delivery is never just a log line. This safety-net is not behind BROADCAST_ENABLED — it is always available to ops.
  • Delegated administration + jurisdictional authority — backend (/admin/authority/*, Phase 16.5, behind DELEGATED_AUTHORITY_ENABLED) — the authority model beneath the 16.4 seam. A new agency_admin role administers only their own agency: define a jurisdictions tree (Federal → State → region → …, arbitrary depth, extensible level, optional admin-drawn boundary — decoupled from orgs so one org can span many jurisdictions and one jurisdiction can be shared by several), assign an officer to a jurisdiction, and grant/revoke per-staff authority (membership_grants — broadcast audience ceilings, zone/fire management). When the flag is on, assertBroadcastAuthority uses each officer’s grant ceiling (falling back to the flat cap when un-granted) and requires the drawn area to sit within their jurisdiction boundary (falling back to no containment where un-mapped) — so an un-configured deploy behaves exactly like the 16.4 interim rule. system_admin unbounded; every mutation assertAgencyOrgAccess-confined + audited. The in-app Agency Admin console (/admin/authority, 16.5-E) lets an admin define jurisdictions
    • draw boundaries, assign officers to a jurisdiction, and grant/revoke per-staff authority — surfaced under the ops nav for agency_admin + system_admin, server-gated the same way. Phase 16.5 is complete (A–E).

What is planned (Phase 16)

The staged plan (EMERGENCY-SERVICES-PLAN.md) closes the gaps, each phase RBAC-gated, audited, and reversible:

PhaseAddsStatus
16.0Security + honesty hardening; un-orphan the scorecard; shared access cardbuilt
16.1-A/BDurable-core schema (invitations, admin_actions, membership verification) + agency control-plane API (create org, invite, verify) + requirePermission()built
16.1-DIn-app system-admin console (create / invite / verify / revoke)built
16.1-EInvitee accept / minimal flagged sign-in (AGENCY_ACCOUNTS_ENABLED off)built
16.1-CReal per-agency tenancy (agency control-plane, council_admin org-confined, flagged)built
16.2Command Center redesign (incident-command IA, operator-selectable preview)built (Scope A)
16.3Operator exclusion zones + audited writes + flag-gated alert suppressionbuilt (A–E; annotation split out)
16.4Broadcast / area alerting + real escalationbuilt (flagged; web/email need keys)
16.5Agency delegated administration + jurisdictional authority (agency_admin role, jurisdictions tree, membership_grants, Agency Admin console; the seam consumes grants + jurisdiction behind DELEGATED_AUTHORITY_ENABLED) — supersedes the 16.4 interim authority seambuilt (A–E, flagged)
16.6Moderation queue; places curation, test-fire injection, audit reviewplanned
16.7Agency SSO/SAML federation + state incident feedsneeds partnerships

Identity is pluggable. How an officer authenticates (local invite→set-password now; per-agency OIDC/SAML later — the auth_provider enum already carries both) is deliberately separated from who they are (orgs, roles, memberships, verification). Tenancy and verification key off org membership, so they are independent of the sign-in mechanism — an agency’s own SSO slots in at token-minting only, with the RBAC and tenancy model unchanged. The agency control-plane (/admin/agencies/*) is system_admin-gated and every mutation writes an un-sampled admin_actions row.

Access model

Ops surfaces are gated server-side by requireRole(["system_admin", "agency_officer"]) (defence in depth: the client also hides them from non-ops sessions); the agency control-plane adds requirePermission("agency.manage") (system_admin only), and the operator write surfaces add requirePermission("zones.write") / requirePermission("broadcast.send") + requireVerifiedOfficer. A homeowner token is 403 on every ops endpoint. Verified-officer identity — the precondition for any suppression or broadcast authority — is built (manual verification + a flagged invitee sign-in), but the invitee flow ships dark until onboarding.

Broadcast authority is a single seam. assertBroadcastAuthority decides who may send how wide in one place, so the interim rule (own-org; a wide audience — over a tunable recipient/km² cap — is system_admin-only) can be replaced by the Phase 16.5 model (an officer’s State/region jurisdiction bounding their reach + an Agency Admin granting per-staff broadcast / fire-management authority) without touching the broadcast route. The same design now also governs the 16.3 zone writes: assertZoneAuthority (16.5-D) gates the exclusion-zone route so that, when DELEGATED_AUTHORITY_ENABLED is on, a non-system_admin officer needs an active grant (fires.manage for suppress-hotspot, zones.write for advisory kinds) and must draw within their jurisdiction — un-granted is denied (a zone is a binary capability, deliberately stricter than broadcast’s flat-cap fallback). Both operator surfaces now run through one delegated-authority model.

Tenant isolation is enforced on the agency control-plane: a delegated council_admin is confined to their own org (assertAgencyOrgAccess — roles read fresh from the DB, fail-closed, audited — behind AGENCY_DELEGATION_ENABLED, off by default so today’s behaviour is system_admin-only); system_admin is unconfined. Note the deliberate scope: the only tenant-specific data today is agency membership. Shared national data (fires, predictions, the accuracy scorecard) is intentionally not org-partitioned — every agency sees the same fires — so tenancy is localized to the control-plane, not applied to shared reads.