Wildfire Watch
Real-time wildfire resource dispatch and community alert system, with every AI recommendation audited, validated, and gated before it reaches a human.
Inspiration
We live in California. Every fire season, the same pattern repeats: a plume goes up, the satellite catches it, and for the next forty minutes nobody downhill knows anything. Dispatch runs on phone calls. Residents find out from Twitter.
The data to close that gap already exists — NASA FIRMS, CAL FIRE, NOAA, USGS — it's just scattered across six agencies with different schemas. We wanted to fuse it, score it, and push an SMS to the right phones in under 60 seconds from detection.
How we built it
Data pipeline. NASA FIRMS and CAL FIRE scrapers (EventBridge-scheduled Lambdas) drop normalized fire events into Kinesis. A normalization Lambda coerces everything into one schema; an enrichment Lambda joins NOAA wind, USGS watershed, and Census population onto each event. Kinesis (not SQS) because three downstream consumers need the same stream.
ML + safety gate. Enriched events hit a SageMaker endpoint that scores a dispatch recommendation. A Bedrock claude-sonnet-4-6 call drafts a human-readable advisory. Guardrails validates it synchronously. Then the safety gate Lambda writes the hash-chained audit record before any SMS fires. The alert sender refuses to run if the audit write didn't commit.
Alerting. Above the confidence threshold, direct SNS publishes to residents inside the risk radius. Below threshold, Step Functions parks the decision and waits for a human verification.
Frontend. React 18 + Mapbox GL JS, hosted on Amplify. Live fire perimeters and a panel that replays the audit chain for any incident— so you can see why a decision was made.
Risk score. We combined radiative power, spread rate, and population exposure into a single scalar:
$$R = \alpha \cdot P_{\text{rad}} + \beta \cdot \dot{A} + \gamma \cdot \log(1 + N_{\text{pop}})$$
where $\dot{A}$ is spread rate in $\text{km}^2/\text{hr}$ and $N_{\text{pop}}$ is population at risk. Weights were hand-tuned against historical CAL FIRE incidents.
Challenges we faced
Mid-sprint architecture pivot. QLDB and Pinpoint were both not available due to some issues with the workshop accounts, which forced a rebuild of the audit and alerting paths. We kept the safety contracts intact (audit-before-alert, Guardrails-before-publish) and swapped the implementations underneath. The hash-chain table actually ended up cleaner than the QLDB design.
The 60-second budget. Kinesis → enrich → SageMaker → Bedrock → Guardrails → audit → SNS is seven network hops. We cut latency by running enrichment and SageMaker prediction in parallel and caching NOAA wind by H3 cell for 5 minutes.
Keeping PII out of CloudWatch. Phone numbers and addresses touch the system but never log. This meant rewriting a lot of default Lambda logging and adding a linter rule that fails CI if logger.info sees a phone_number field.
Built With
- amazon-api-gateway
- amazon-bedrock
- amazon-cloudwatch
- amazon-cognito
- amazon-dynamodb
- amazon-eventbridge
- amazon-kinesis
- amazon-location-service
- amazon-ses
- amazon-sns
- aws-amplify
- aws-cdk
- aws-iam
- aws-lambda
- aws-step-functions
- bedrock-guardrails
- boto3
- cal-fire-api
- claude-code
- claude-sonnet-4.6
- github
- google-maps
- javascript
- mapbox
- mapbox-gl-js
- nasa-firms
- noaa-weather-api
- npm
- python
- react
- sagemaker
- sagemaker-clarify
- sagemaker-model-monitor
- usgs-water-services
- vite
- xgboost
Log in or sign up for Devpost to join the conversation.