Inspiration

East Africa loses lives every rainy season to floods, drought, and locusts that are increasingly predictable from satellite data, yet that data rarely reaches the person who needs it most. We looked at ICPAC's own Husika platform and found it excellent for organisational broadcast, but structurally unable to reach anyone outside a subscribed organisation's list. HALI exists to close that specific gap: the last mile, the person with a phone and no subscription.

What it does

HALI ingests hazard and condition data from 13 external sources, including FEWS NET's IPC food insecurity classifications, HDX's rainfall anomaly feed, GDACS, CHIRPS, NOAA's GFS forecast, IFRC's appeal data, and WHO's disease outbreak news, automatically, on independent schedules, with no human writing the alert. Three reference datasets sit underneath it: a WorldPop population grid (248,997 cells, 289 million people), Natural Earth country boundaries, and 891 OCHA COD-AB subnational district polygons across six countries, giving HALI district-level resolution that country-level tools can't match. A three-model AI ensemble (Claude, Gemini, Groq) translates every alert into ten languages and generates livelihood-specific action guidance for seven groups, from farmers to displaced populations. Delivery happens over USSD, WhatsApp, or a spatial PWA, whichever the person already has. Community reports feed back in: a DBSCAN clustering model detects emerging hazards before any official alert exists, and enough corroborating reports can automatically escalate an alert's severity.

How we built it

We built HALI in layers, each one verified end to end against live data before moving to the next, inside a single Nx monorepo so the backend, frontend, and shared types never drifted apart.

The database came first: PostgreSQL with PostGIS on Railway, six core tables, GiST spatial indexes on every geometry column. The first real lesson landed here — Railway's default Postgres template doesn't ship with PostGIS at all, and we had to re-provision against their dedicated PostGIS template before a single migration would run.

Ingestion came next, built around one typed BaseAdapter interface so every source, however different its format, went through the same extract, validate, transform, load pipeline with dead-letter tracking and idempotent writes. We planned for five sources and shipped with thirteen, because once the adapter pattern existed, adding FEWS NET, HDX, IFRC, and WHO alongside GDACS and CHIRPS was mostly configuration, not new architecture.

The AI layer runs three models in parallel, Claude, Gemini, and Groq, scored against a five-dimension clarity rubric rather than trusting whichever model answered first, with prompts that inject season and livelihood context so the same flood alert reads differently for a pastoralist in the dry season than a farmer in the long rains.

The frontend is a React PWA with shadcn/ui and Tailwind v4, a Leaflet map carrying the GIS layer, offline queuing for reports, and full dark mode. Messaging came after: USSD through Africa's Talking's sandbox, WhatsApp through Meta's Cloud API with templates pushed by script rather than built by hand in the dashboard.

The GIS layer came last and pushed the hardest: ICPAC WMS layers composited on top of our own alert zones, a compound risk index, draw- your-own-polygon spatial queries, and DBSCAN hotspot detection, all running against real WorldPop, Natural Earth, and OCHA geometry rather than placeholder shapes.

Everything deploys as three Railway services, backend, frontend, and landing, with GitHub Actions running the full test suite against a live PostGIS container before any deploy is allowed to reach production.

Challenges we ran into

Our two highest-volume adapters, FEWS NET and HDX HAPI, together account for over 97% of our alert volume, and for a stretch they were both silently bypassing our loader's spatial join, meaning alerts weren't being correctly attributed to the country whose polygon they actually intersected. It only surfaced once alert counts across countries stopped matching what we expected from source coverage, and we traced it back to an assumption the loader made about a field FEWS NET and HDX structure differently from GDACS. Fixing it against live production data, not a synthetic test fixture, was the right call but a nervous one.

Our first country boundaries were bounding boxes, not real geometry, left over from an early seed migration. They were good enough to demo an alert zone but silently wrong for anything that depended on actual shape, our compound risk index in particular, which was scoring countries by rectangle overlap rather than their real coastlines and borders until we re-ingested from Natural Earth. It's the kind of bug that looks fine until a GIS professional glances at the map for two seconds.

Free-tier rate limits on Gemini (5 requests/minute) and Groq (30/minute) meant our AI backlog processor would silently stall partway through a batch of alerts if we let concurrency run wild, so we added a hard concurrency cap and smaller batch sizes specifically tuned to those limits rather than treating the ensemble as infinitely parallel.

Accomplishments that we're proud of

A fully automated pipeline running with zero human content authorship across 13 live sources, every alert, translation, and action card is machine-generated and verified end to end against live data, resolved down to 891 subnational districts, not just 8 country outlines. A DBSCAN model that finds hazards no official system has flagged yet. Ten languages, including three, Tigrinya, Luganda, Afar, that no existing regional tool serves. A real population layer of 289 million people, not a per-call external lookup. And the honesty to leave Djibouti and Eritrea out of our subnational layer rather than fake geometry that doesn't exist.

What we learned

The surprising part wasn't technical, it was how much of the raw material already existed before we wrote a line of code. GDACS, CHIRPS, FEWS NET, HDX, IFRC, WHO: all of it public, all of it free, all of it sitting in English on servers nobody in a Turkana village will ever query directly. The hard problem was never sensing the hazard. It was assembling scattered feeds into one pipeline, translating what they said into a language and a livelihood a person actually has, and reaching a phone with no data plan and no subscription attached to it.

We also didn't expect food insecurity data to dwarf everything else. FEWS NET alone accounts for the overwhelming majority of our alert volume, which reframed what early warning meant to us, less about the flood that arrives in 72 hours and more about the slow, grinding food crisis a satellite has been quietly tracking for months before anyone says so out loud. And building the subnational layer meant confronting directly that Djibouti and Eritrea don't have the same open administrative geometry that Kenya or Ethiopia do, a small technical fact that says something real about whose vulnerability gets digitized first, and whose doesn't.

What's next for HALI

Beyond the hackathon, we want HALI to stop being a demo and become something a real community actually depends on. That means moving off Africa's Talking's sandbox and Meta's test number onto a live, paid short code and a verified WhatsApp Business line, piloting with one county disaster management unit or NGO already active in the region, most likely Turkana or West Pokot given the coordinates we've been testing against throughout the build, and finding a way to keep the pipeline funded and running past the point where we stop paying for it out of pocket. An early-warning system that goes quiet the week after a hackathon ends hasn't actually warned anyone.

Built With

Share this project:

Updates

Submission history