What I Built

Altadena, January 8, 2025. Anthony Mitchell Sr., 67, couldn't walk on his own. His son Justin had cerebral palsy. They called for an ambulance. They waited. It never came. (NPR)

The Eaton fire killed 19 people. 18 died in west Altadena, where the evacuation order came last. NASA satellites saw fire at those homes at 1:02 AM. The order went out at 3:25 AM. It told people to leave, and never asked who couldn't. 16 of the 19 couldn't leave alone.

The more I read, the clearer it got that the problem has two halves. People couldn't get out, and then they couldn't get help. Aid arrives weeks after the fire. The people who need it most can't front the gas or the motel room. Fraud drains the funds, and donors can't see where their money went, so they stop giving.

Every evacuation tool I found stops at the van. Surprisingly, texts work better than calls. I couldn't find a single one that also gets people money the same night. So I built EvacuAid:

Out safe. Aid tonight. EvacuAid finds who can't leave, gets them out first, and gets them fraud-checked aid the same night, with every donor able to see whose life their gift touched.

How a night works

  1. A two-way alert, hours early. When the fire forecast reaches a home, EvacuAid texts it over real iMessage and asks: 1 leaving on my own · 2 need a ride · 3 need help.
  2. Any words, any language. People don't reply "3". They write "my mom can't walk and the car's in the shop" or "mi esposo usa silla de ruedas". Gemini turns that into a structured case in about 2 seconds and replies in their language.
  3. Who goes first is math, not guesswork (more below). The operator sees a ranked list with reasons and approves every van in one click. AI suggests; a person decides.
  4. The resident sees every step: dispatched → outside → on board → checked in, plus a private link to watch the van move on a map. No reply means two more texts, then a door knock.
  5. Money, while they're leaving. Two questions size the aid ($150 gas · $315 gas + a night · $770 full). Five bank checks run on real card history before a dollar moves. Money lands on a relief card that only buys evacuation: gas, rooms, food and pharmacy go through; electronics get declined.
  6. Donors see it land. Donations sit in an on-chain pool whose rules are written in code. Every donor gets a receipt naming the families their gift reached.

Who goes first: a race between the fire and the household

Every home gets a score, recomputed every replay minute.

P, the chance the fire wins. Let A be when the fire reaches the home and T how long the household needs to get out. Fire timing is uncertain, so A is lognormal with σ = 0.35:

P(trapped) = Φ( (ln T − ln A) / σ ) 

T adds up real minutes: 30 to get moving, 20 to drive out, +60 for a wheelchair, +45 for oxygen, +15 for kids, +30 if they haven't replied. No car means at least 240 minutes. Asking for help means 360: only a rescue gets them out.

V, the harm if they're trapped. Risk factors from the CDC Social Vulnerability Index combine as independent risks:

V = 1 − Π (1 − wᵢ) priority = P × V 

The weights are medical 0.6, can't walk 0.5, someone left at home 0.4, age 75+ 0.3, silent 0.3, kids 0.15, non-English 0.1, and a 0.1 baseline so nobody is ever zero.

A wheelchair user on oxygen with the fire 90 minutes out scores 0.82. A family with a car on the same street scores 0.05.

Did it work? I replayed the real night.

I didn't invent a fire. EvacuAid replays the actual Eaton fire from 365 NASA FIRMS satellite detections, on the real streets, against the real 3:25 AM order.

My first model said fire reached west Altadena around 10 PM. The satellites said after 1 AM. It would have made my results look better. I threw out my own number and used theirs.

200 paired runs, 179 people in 114 homes who can't leave alone, 22 vehicles:

Scenario Alert People out before the fire
What happened One-way, 3:25 AM ~3 of 179
Generous baseline: alert when satellites saw fire One-way, 1:02 AM ~97 of 179
EvacuAid Two-way, 6:49 PM 179 of 179

That's +176 people, the same gain in all 200 runs, with a median of about 4.5 hours to spare. The baseline even knows who needs a ride. It still loses, because a one-way alert can't hear "I can't."

Fire is the proof, not the limit. Only one input changes per disaster (the feed that says when the hazard reaches each home), so the same system works for floods, hurricanes, earthquakes and chemical spills.

Demo

🔗 Live: evacuid.us · the pitch at evacuid.us/pitch

What you can do right now:

  • Sign up on the landing page and text the number. You'll get the real alert flow on your phone over iMessage.
  • Watch the console replay the Eaton fire minute by minute, with households lighting up as the fire reaches them.
  • Open /relief to see every dollar: Nessie transfers, card swipes, van bills, and Solana releases paying the county back.
  • Donate on /donate. That sends a real Solana devnet transaction, and your receipt names the family it reached.

Partner Technologies

I wanted every sponsor platform to be load-bearing: take any one away and a piece of the rescue breaks.

Google Gemini: turning panic into a case

A frightened text is the hardest input in the system. I use gemini-3.5-flash-lite through the Interactions API with minimal thinking, which gets about 2 seconds per message.

  • A management prompt with the mission, 14 conversation stages, the needs taxonomy, safety rules, and worked examples in English and Spanish.
  • Structured JSON output: status, stage, needs, who's left behind, emergency, language, a one-line operator summary, and the reply. Code decides; AI reads.
  • Guardrails in code. It can never promise a ride or a dollar amount. If it says "on the way" before an operator dispatched, code replaces the line.
  • A hybrid engine. A state machine answers menu replies instantly and for free; Gemini handles everything else. If Gemini fails, keyword rules answer, so no text is ever dropped.

What I learned: Gemini is remarkable at reading "the car's in the shop and grandma's on oxygen" and getting both needs. But approving a van belongs to a person, so I built the system so it physically can't skip that step.

Vultr: the always-on front door

EvacuAid isn't a stateless website. It holds a live roster, checks iMessage every 2 seconds and times every van trip, all things serverless functions forget between requests. So it runs on a Vultr Cloud Compute server (Ubuntu 24.04):

  • Caddy for automatic HTTPS; ufw leaves only SSH, 80 and 443 open.
  • One live copy, always. Photon only lets you read messages, so two running copies both reply to every text. An SSH reverse tunnel carries evacuid.us to a single operator station, and a full standby copy on Vultr (Node 22, systemd, restarts in 2 s) takes the whole night over in one command, data included.
  • Deploys can't break the site. Each build goes into a new release folder, 34 smoke checks run against the real domain, and any failure rolls back automatically. Nightly backups, 14 kept.

The scariest part of the weekend was deploying something with this many moving parts. Three of my first deploys failed in instructive ways: an rsync exclude that also skipped my fire data, a Turbopack panic on a symlinked data folder, and a symlink nested inside a real directory. The rollback script is why none of them reached the live site.

Solana: donor money with rules in code

"Trust us" doesn't work for disaster money anymore. I wrote an Anchor program, relief_pool, deployed on devnet:

  • 4 instructions: init_pool · donate · publish_order · release.
  • PDAs for the pool, the vault, one zone per evacuation order, and one claim per household hash, so a home can't double-dip.
  • Rules enforced on-chain: money only moves for zones the county actually ordered out, never more than $770 per home, and only to the county's account (has_one county_payout). Each rule is proven to refuse on devnet, so not even a stolen key can send money elsewhere.
  • Pro-rata receipts: each release draws from each donor in proportion to what their gift still makes up of the pool, so your receipt names the real families you helped.

Capital One Nessie: the bank, the fraud engine and the books

Nessie isn't just a ledger here. EvacuAid uses 10+ endpoints, each with a job:

  • A customer per home (the ZIP is the residency check) and a relief card per family.
  • GET /accounts/{id}/transactions answers "where was this card an hour ago?" That's how it catches the "evacuee" whose card was swiped in Las Vegas.
  • Transfers only after approval, and a decision on every claim. A held claim moves $0. Nessie settles pending transfers on its own sweep, so I rebuilt the flow to create no transfer at all until a human says yes.
  • Every van trip is billed live to County Emergency Operations, itemized for FEMA's 75% reimbursement.
  • The bridge: Nessie pays the family in seconds; Solana pays the county back from donors, and each release lands as a Nessie deposit that records the transaction it reimbursed. Chain and bank point at each other.

Tiger Data: the incident's memory

Every text, case, dispatch and dollar is a row in an evac_events TimescaleDB hypertable, with a 5-minute continuous aggregate, compression, and a (run, kind, time desc) index so the console's charts return in milliseconds. Writes are fire-and-forget: analytics can fail; a ride can't.

Photon: real iMessage

Residents don't install anything. EvacuAid talks to the phone they already have over real iMessage through Photon, polling every 2 seconds, with conversation routes saved to disk so a restart never forgets who it was talking to.

Hackathon Experience

I built EvacuAid solo at HackUMass XIV: about 12,000 lines of TypeScript, 297 lines of on-chain Rust, and 34 automated tests.

What I'll remember most is the moment the first real iPhone buzzed with the alert, someone replied "my mom uses a wheelchair and we have no car," and a van, a $150 relief card and a donor receipt all followed from that one sentence.

Some things I'll carry out of this weekend:

  • Rescue is a listening problem. The people who died weren't the ones who missed the alert. They were the ones nobody asked.
  • Money is a rescue tool. People stay because leaving costs money. Aid that arrives next month gets nobody out tonight.
  • Honest numbers beat impressive ones. Throwing out my own fire timing made the result smaller and far more believable.
  • Resets that erase real people are bugs. Early on, "reset drill" wiped real phone numbers. Now every run is archived, and only replying STOP turns a phone off.

Nobody should have to wait for an ambulance that never comes, or for a check that comes too late.

If this moved you, share it with someone who works in emergency management, accessibility or disaster relief. I'd love to hear what you'd build next.

Residents in the replay are simulated; the fire, the streets and the timeline are real. In an emergency, call 911.