1. About the project

When AWS us-east-1 went down on October 20, 2025, I watched it from the inside. My company's stack runs on AWS, and for hours all we could do was tell customers "AWS is down" and wait — completely helpless. I couldn't even open Wealthsimple to get to my own money. Sitting there, I kept thinking: if a single-region cloud outage can freeze a fintech company in a stable country for a day, what happens to the places where the emergency is physical? I was born and raised in Nigeria, where flooding displaces thousands of people every rainy season. The tools meant to keep people accounted for during a disaster too often run on the same fragile, single-region infrastructure that fails exactly when it's needed most. That's why I built Aftershock.

Most disaster check-in apps run in a single cloud region, and regions fail. That October outage took Snapchat, Coinbase, Wealthsimple, and Fortnite down with it. If an outage like that lands during an earthquake or a flood, the app people are using to mark themselves safe goes down too — at the exact moment it's needed.

Aftershock is built to stay up through that.

It's a check-in platform for emergencies. When an earthquake or severe-weather event hits, anyone in the affected area taps Safe or Need Help. Admins for an organization (an employer, a university, a neighborhood group) watch their roster update live: who's accounted for, who needs help, who hasn't answered. That admin view is also the business case. Companies are legally obligated to account for their people during an emergency, and Everbridge and AlertMedia sell that capability to enterprises for six figures a year.

What holds it together is Amazon Aurora DSQL, running active-active across us-east-1 and us-east-2. Both regions take writes at the same time, with strong consistency, and there's no primary to fail over from. Take us-east-1 offline and writes keep landing in us-east-2 while the app behaves normally.

Three decisions make that work:

Check-ins are stored append-only, one row per check-in, with the newest row being the current status. Nothing gets overwritten, so the full history survives (safe at 14:02, need help at 14:40, both still there), and two writes from the same person never land on the same row.

Each row records which region wrote it. In normal operation the activity feed shows us-east-1 and us-east-2 rows mixed together. Drop a region and the feed shows only the survivor. You can watch the failover happen in the data itself.

DSQL commits writes synchronously across regions. DynamoDB Global Tables, the usual multi-region pick, resolves conflicts with last-writer-wins, so a delayed "I'm Safe" packet can overwrite a newer "Need Help" with no error, and nobody gets dispatched. For a safety app that outcome is unacceptable, so the stronger consistency model decided it.

Resilience runs all the way to the phone. Right after a disaster, networks buckle — towers overload, backhaul drops. So a check-in doesn't need a live connection: tap Safe with no signal and it's saved on your device, then synced automatically the moment you're back online — the confirmation screen tells you it will. Aftershock installs to the home screen as a PWA, so it's one tap away when it matters. A region can fail on the server and a phone can lose signal in the field, and the "are you safe" still lands.

The hazard data is live. Real USGS earthquakes refresh every minute alongside real NWS severe-weather alerts, drawn on a dark map with quake circles sized by magnitude and alert polygons tested in the browser.


2. How it was built

The core is a multi-region Aurora DSQL cluster: us-east-1 and us-east-2 peered active-active, with us-west-2 as the witness region. Every query goes through one failover wrapper in src/lib/db.ts. It holds both regional endpoints, tries the first, and retries the second if the connection fails. A KILL_REGION environment variable removes an endpoint from the list, which is how the demo drops a region without touching the cluster.

DSQL's constraints drove the schema. There are no foreign keys, sequences, triggers, or SELECT FOR UPDATE, and indexes build asynchronously. So tables use client-generated UUID keys, referential integrity lives in application code, and indexes are created with CREATE INDEX ASYNC during setup. There's no PostGIS either, so proximity runs in two passes: a bounding-box filter on a (lat, lng) index narrows the candidates, then a haversine calculation confirms whether someone is actually inside the hazard radius. NWS alert polygons are stored as raw GeoJSON and rendered on the map.

Hazard data comes from two free government feeds, neither needing an API key: the USGS earthquake GeoJSON summary and the NWS active-alerts endpoint. A Vercel Cron job runs the ingester on a schedule. It upserts on each event's stable external ID, so re-running it never creates duplicates. Earthquake magnitude maps to an approximate impact radius, and NWS alerts are filtered down to Severe and Extreme.

The frontend is Next.js, scaffolded one screen at a time in v0 and deployed on Vercel. v0 generated the hazard map and the org dashboard from prompts, and the API routes were wired to DSQL by hand. Vercel's Aurora DSQL integration supplies the endpoint URLs; authentication is a short-lived IAM token generated per connection from dsql:DbConnectAdmin credentials.

Stack: Amazon Aurora DSQL (active-active, us-east-1 + us-east-2), Next.js, Vercel + Vercel Cron, v0, MapLibre GL, Serwist (PWA), USGS earthquake feed, NWS Weather Alerts API.


3. Challenges I ran into

Unlearning Postgres habits. My first check-in table had a single current_status column I planned to update in place, and a design that assumed row locks via SELECT FOR UPDATE. DSQL has neither. It uses optimistic concurrency rather than locks, and overwriting a status column throws away history. Switching to append-only inserts solved both problems at once and made the rest of the schema simpler.

Building the backend first made v0 harder. I had the API finished before generating any UI, so v0's components assumed response shapes my backend didn't return and dropped files in the wrong places. It also regenerated globals.css and layout.tsx on every pass and erased my font setup, so I skipped those two files by hand each time. That's the cost of handing a scaffolding tool a backend it has never seen. Next time I'd build the UI first.

Making the failover visible. Claiming an app is multi-region convinces nobody. To show it honestly, every write records its origin region and the kill switch removes an endpoint at the wrapper level, so the region change appears in the live feed on camera with nothing staged.

Geo without PostGIS. With no spatial extension, proximity had to be rebuilt from parts: a bounding box for a cheap first cut, and haversine to confirm distance. NWS alert polygons are stored as raw GeoJSON and rendered on the map.

The managed database integration didn't fit. Adding a database from inside the project pushed me toward creating a new, Vercel-managed AWS account. That wasn't where my cluster lived. I'd already built the two-region active-active DSQL setup in my own AWS account, and the integration gave me no clear way to point at it.

There is a workaround. From Marketplace → AWS you can "Link an existing AWS account" via IAM delegation. I couldn't find documentation on whether that path supports a multi-region active-active cluster or only a single-region database. Multi-region is the entire value of the product. Aftershock is only worth building if it stays up when a region goes down, so a single-region database would defeat the reason it exists. So I built the cluster by hand and connected Vercel to it through environment variables.


4. Accomplishments I'm proud of

The failover works on camera. Cut a region during the demo and the check-in counter keeps climbing while the activity feed narrows to the surviving region. I didn't write any failover logic to make that happen; it comes from how DSQL is built, which is the whole point.

It runs on real disaster data, not seeded fixtures. Live USGS quakes and NWS alerts, refreshed every minute.

Offline-first check-in. Aftershock is an installable PWA: tap Safe with no signal and the check-in is queued on the device and synced automatically when connectivity returns. A service worker and an offline fallback page keep it usable on a dead network — the exact condition that follows a disaster.

The schema does the hard work. Append-only check-ins mean a "Need Help" can't be silently overwritten and two writes from the same person can't collide, so correctness lives in the data model instead of in defensive code.

The full B2B roster view (status counts, the member list, location filters) is one indexed query that the dashboard polls every five seconds and stays cheap.


5. What I learned

The real shape of Aurora DSQL's constraints, the kind you only find by building on it: no SELECT FOR UPDATE, async index builds, client-side keys, no PostGIS. The pleasant surprise was that DDL replicates across regions on its own. Apply the schema to one region and the peer has the same tables and indexes within seconds.

The consistency model is a product decision, not a footnote in the architecture. For a safety app, a dropped status update means a real person doesn't get help, which is why last-writer-wins was disqualifying and why DSQL won over DynamoDB Global Tables here.

Append-only storage is worth reaching for whenever you'd want to audit or replay something later, and it removes write contention as a bonus.

v0 has improved a lot, but it wants to own the architecture. Letting it scaffold the UI first means fighting it far less.


6. What's next

Add push notifications. The app already installs as a PWA and works offline. The next step is web push, so a nearby hazard can alert someone's phone even when the app is closed.

Stress-test against realistic load. The 10,000-check-in surge in the demo is synthetic — generated by a replay script, because the app doesn't have thousands of real users yet. The honest next step is a load test that mirrors how check-ins actually arrive after a real quake — a sharp spike, then a long tail — and publishes commit-latency percentiles across both write regions. Aurora DSQL runs active-active across two write regions today (us-east-1 and us-east-2) with a witness region for quorum; as the service supports more write regions, we'd widen coverage to put an endpoint closer to more people.

Build out the B2B tooling. SSO, audit export, and multi-org administration are the baseline for selling against Everbridge and AlertMedia.

Add more hazard sources beyond USGS and NWS — wildfire and flood feeds, and international sources for the developing regions where disasters like flooding hit hardest and resilient infrastructure matters most.

Built With

Share this project:

Updates

Submission history