Skip to content

Kansas City hail alerts

Last updated View as MarkdownAgent index

org/apps/hail-alerts/ handles the first StormTrust hail-notification use case. It evaluates National Weather Service (NWS) warnings for the Missouri side of the Kansas City metro. It explains decisions, maps warnings, and prepares team email.

The application remains isolated from the command-center UI. It does not change existing frontend or backend contracts.

For configured cadence, queue retries, persistence checkpoints, and recovery limits, see Polling and triggers.

Application reference

Property Value
Production URL https://hail.stormtrust.net/
Local URL http://127.0.0.1:8791/
Source org/apps/hail-alerts/
Runtime Native WorkerEntrypoint and SQLite-backed Durable Object
Frontend Vue through the Cloudflare Vite plugin and Static Assets
Active source NWS active Missouri Severe Thunderstorm Warnings
Historical source Rolling seven-day Missouri Severe Thunderstorm Warnings
Market Missouri warnings intersecting Census CBSA 28140
Access Exact-email Cloudflare Access policy
Verification make app-verify APP=hail-alerts

Runtime flow

NWS active feed, history, or labeled simulation
                    ↓
Bounded GeoJSON normalization
                    ↓
Kansas City market intersection
                    ↓
VTEC warning-chain reconciliation
                    ↓
Observed-hail qualification and escalation
                    ↓
Decision ledger, warning map, and message preview
                    ↓
Bounded Cloudflare Email Sending delivery

Terraform owns the Worker identity and Access resources. Wrangler owns the Worker deployment, Custom Domain, Static Assets, Durable Object migration, cron, Email Sending binding, and observability.

Qualification rules

A revision qualifies when it is an Actual, Public Severe Thunderstorm Warning. It must intersect the market and contain a valid VTEC identity. It must also include hailThreat=OBSERVED and a positive maxHailSize.

There is no minimum hail-size floor. Radar-indicated hail does not qualify. A later revision escalates after a 0.25-inch increase from the delivered size.

The ledger explains every notification, escalation, duplicate, rejection, stale revision, and warning end. A newer unsent revision supersedes the older message. Warning cancellation withdraws an unsent message.

The map treats its external basemap and StormTrust operational geometry as separate layers. If the basemap cannot load, the application switches to a local fallback style and continues showing the Kansas City boundary and NWS warning geometry with a visible degraded-state notice.

The map uses the same production runtime boundary as the existing StormTrust maps. Response headers explicitly allow OpenFreeMap tile requests, while both MapLibre module-worker files are deployed as immutable same-origin assets. The Access-protected hostname does not register a service worker or web manifest; those browser-managed requests can otherwise be redirected to the Access login origin instead of returning the required asset type.

Delivery controls

Cloudflare Access protects the complete production hostname. Terraform limits Access to named operators. The Email Sending binding independently limits recipients and sender addresses.

HTTP requests cannot provide recipients. State-changing browser requests must be same-origin. A generated deployment token protects manual submission. Daily caps permit 100 live attempts and 10 test attempts.

Only a scheduled live poll may request automatic delivery. Browser polling cannot. Replay always requires explicit confirmation and includes [TEST REPLAY] in the subject.

Production begins in shadow mode. The Worker polls and records real NWS data, but it does not send unattended email. Enable scheduled delivery only after Access and a controlled replay email pass verification.

Run locally

Use Node.js 24 or newer. From org/apps/hail-alerts/:

pnpm install --frozen-lockfile
pnpm dev

Open http://127.0.0.1:8791/. Select Replay, then Simulation, to test the complete journey. Local email remains simulated and needs no Cloudflare credential.

API boundary

Route Purpose
GET /api/health Worker health
GET /api/market Kansas City market geometry
GET /api/state?mode=live Live source and decision state
GET /api/state?mode=replay Isolated replay state
POST /api/poll Live NWS evaluation
POST /api/replay Seven-day or deterministic replay
POST /api/notifications/:decisionId/send One confirmed eligible delivery

NWS requests allow only api.weather.gov. They time out after 12 seconds and reject responses above 8 MiB or 1,000 features. Active polling uses ETags. Affected-zone fallback permits up to 20 zones per warning.

For Worker Custom Domain behavior, review the Cloudflare Custom Domains documentation ↗. For binding restrictions, review the Email Sending binding documentation ↗.

Operational guides

Use the shared validation and deployment guides and health evidence by app. For a failed release, follow recovery procedures with this application's state and access boundaries.