Designing a geospatial community intelligence engine.
Inside NeyborHuud, Sentinel AI turns raw posts into a location-aware threat signal — without a spatial database, and without sending every message to a model.
A safety feed needs to know where you are — without a PostGIS budget.
A community safety layer has to answer two questions cheaply and constantly: is this post a real threat, and is it relevant to thisneighborhood? Answering both with a full geospatial database and an LLM call on every post is the textbook approach — and it's also slow, expensive, and overkill for an MVP that needs to work today on a Nigerian LGA map, not a hypothetical global one.
Sentinel's answer was to build the geospatial intelligence out of primitives that were already cheap to have: a static table of Nigerian administrative boundaries, a distance formula, and a time window. The result is a real, working geo-intelligence layer — just not the one built on a spatial database extension. The seam where PostGIS would slot in later is left explicit in the code, not hidden.
Haversine distance, not spatial SQL.
Every coordinate pair NeyborHuud sees — a new signup, a new post — is resolved against a local dataset of Nigerian LGAs. The matcher walks the table, computes great-circle distance to each LGA centroid with the Haversine formula, and takes the closest one within a 50km cutoff. Outside that radius, the location is explicitly tagged Uncovered Area rather than silently mismatched to the wrong community.
Boundary resolution
Nearest-LGA match by Haversine distance against a static coordinate table, capped at 50km.
Temporal clustering
Same-LGA, same-30-minute-window matching surfaces when multiple people report the same thing.
Contextual scoring
Gemini rates severity 0–10 using both the text and the cluster signal, not text alone.
| Layer | Technology | Why it's there |
|---|---|---|
| Boundary matching | Haversine distance · static LGA table | Assigns every user & post to a Nigerian state/LGA/ward without a spatial database |
| Content store | MongoDB | Polymorphic content schema tagged with a resolved location.lga field |
| Clustering | Time-windowed query · MongoDB | Finds co-occurring security keywords in the same LGA within a 30-minute window |
| Scoring | Google Gemini 2.0 Flash | Contextual 0–10 threat rating, informed by keyword matches and cluster count |
| Fan-out | Socket.IO broadcast | Pushes red-zone alerts to connected clients in real time |
A four-stage cascade, geography folded into stage two.
The naive build sends every post straight to an LLM. Sentinel only escalates once cheaper stages have already narrowed the field — and the geospatial cluster check sits deliberately before the model call, so the AI is reasoning about a pattern across a neighborhood, not a single isolated sentence.
Cultural pre-processor
Before anything else, text is checked against a hand-built cultural dictionary of Nigerian vernacular — scam terms like 419 and yahoo boy, pidgin insults — tagged by severity and category. High-severity matches auto-file a moderation report immediately, independent of the safety pipeline below.
Keyword prefilter
A curated security-keyword list — armed men, gunmen, car snatching, kidnap, ransom — does a cheap first pass over the lowercased text. No match and no cultural hit means the pipeline exits at zero cost: no database query, no model call.
Geospatial cluster check
This is the geospatial core. Sentinel queries recent content sharing the same resolved location.lga field within the last 30 minutes, then counts how many of those also contain a security keyword. A single report is a data point; three reports of gunmen in the same LGA in the same half hour is a pattern — and the pattern, not the keyword alone, is what the next stage reasons about.
Gemini scoring
Only content that cleared stage 1 reaches the model. Gemini receives the raw text and returns a threat_score from 0–10 — the cluster count is folded into the reasoning trail logged alongside the score, not silently discarded.
Red-zone alert. A SafetyAlert record is created and broadcast in real time over Socket.IO to connected clients.
Logged and monitored — visible to moderation, not yet escalated to a broadcast alert.
No action. A false-positive keyword hit ("water don finish") never reaches a human or a push notification.
The cluster count is the difference between a rumor and a pattern.
A keyword match alone is noisy — sarcasm, a joke, a news headline shared in the wrong tone all trip the same list. What actually distinguishes a credible local threat is corroboration: is anyone else, nearby, saying something similar right now? Sentinel answers that with a direct query — recent content tagged to the same LGA in the last 30 minutes, filtered again for security keywords — and passes the resulting count into the model's reasoning, logged alongside the score as an explicit audit trail rather than a black box.
Geography is the corroboration signal, not just the routing key.
Most systems use location only to decide who sees an alert. Sentinel uses it earlier — to decide how much to trust the alert in the first place.
Built for today's scale, with the next seam left visible.
Two posts 300m apart but on opposite sides of an LGA boundary line are treated as different neighborhoods; two posts 40km apart in the same sprawling LGA are treated as the same one. A tighter meter-radius match — the documented next step — fixes both edges.
A confirmed red-zone alert currently broadcasts to all connected clients rather than only those within the affected LGA. Correct for an early network still small enough that global reach is a feature, not noise — and a bounded change once geofenced push is in place.