Hackathon project — built at the IPAI × Public Makers × Komm.one hackathon, June 2026. For VOST Baden-Württemberg (Virtual Operations Support Team).
Ingests live open-source crisis reports, runs them through an AI credibility filter and a geospatial verification engine, and plots verified incidents on a live map — while exposing the disinformation it caught and the real-time environmental status (weather, water levels, traffic) for any location you search.
Demo sector centre: Konstanz (47.6603 N, 9.1758 E). All data is real OSINT — when the feeds are quiet, the map is legitimately empty.
Requirements: Python 3.10+, Node.js 18.18+.
# 1. from the repo root
npm install # installs the root dev-server runner
npm run setup # installs backend (pip) + frontend (npm) dependencies
# 2. configure the backend
cp backend/.env.example backend/.env
# then set LITELLM_API_KEY=<your key> in backend/.env
# (or set USE_LIVE_AI=false to run the heuristic-only filter — no API key, no credits)
# 3. start both servers (API + web)
npm run devThen open http://localhost:3000.
Run the backend test suite (fully offline — no network, no AI credits):
npm run test:api| Variable | Default | Meaning |
|---|---|---|
FEEDS_ENABLED |
true |
Live OSINT ingestion |
USE_LIVE_AI |
true |
Live LLM analyst — set false for the deterministic heuristic filter (real logic, no credits) |
INGEST_NATIONAL |
true |
Ingest nationwide so any searched city surfaces its news; false restricts to the Konstanz sector |
LITELLM_API_KEY |
— | Gateway key for the live analyst |
NOMINATIM_ENABLED |
true |
Geocoding fallback (keyless, cached, rate-limited per OSM policy) |
GET /api/health reports the active ai_mode and data_mode.
- Live OSINT ingestion — polls real, keyless open sources on an interval: NINA civil-protection warnings, Presseportal police/fire press releases, public Mastodon hashtag timelines, and DWD weather warnings. No synthetic feed.
- AI credibility filter — every report passes deterministic heuristics first (bot-spam phrasing, recycled-footage EXIF, geotag conflict), then an optional live LLM analyst (Qwen3-VL via the LiteLLM gateway) that judges tone, specificity and plausibility — and, with vision enabled, whether attached imagery matches the claim — returning a verdict with a rationale.
- Geospatial verification — surviving reports are clustered (same event type, 1 km radius, 60 min window) into verified incidents; confidence scales with the number of independent corroborating sources.
- Responder guidance — each incident carries an impact-based severity, a recommended action, and an agency-tagged SOP checklist (Polizei / Feuerwehr / THW / Rettungsdienst / LRA), mirroring the BW Ministry of the Interior crisis catalogue (27 event classes: natural hazards, technological/industrial, CBRN, pandemic, terrorism/security, supply crises, evacuations).
- Search any German city — ingestion runs nationwide; searching a city scopes the map, feed and stats to incidents within ~100 km of it.
- Live status tiles — header tiles give real-time context for the focused location from official open APIs: DWD weather warnings (via Bright Sky), PegelOnline water levels, and MobiData BW traffic/roadworks.
- Disinfo caught — a dedicated panel shows each rejected report with the rule that fired (
TEMPORALstale EXIF ·SPATIALgeotag conflict ·LINGUISTICbot-spam ·OUTPUT GUARDAI) and the exact reason. - Ingestion inbox —
GET /api/ingestion/inboxexposes every received item and how the pipeline disposed of it (verified / debunked / duplicate / stale / off-topic / unlocated) — full transparency into what came in but isn't on the map, and why.
live OSINT feeds ──▶ ingestion ──▶ AI credibility filter ──▶ geo-clustering ──▶ FastAPI ──▶ Next.js map
NINA · Presse- RawReport heuristics + live LLM 1.0 km radius (:8000) (:3000)
portal · Mastodon bot-spam / EXIF / vision 60 min window
· DWD
- Backend — Python / FastAPI (
backend/): ingestion connectors (ingestion/) → credibility filter (logic/verification.py) → geo-clustering (logic/geospatial.py) → responder guidance (logic/guidance.py), served over thin endpoints (main.py). SQLite store; in-memory served snapshot. Strictly type-hinted; Pydantic models for everything crossing a boundary. - Frontend — Next.js / React 19 / Tailwind v4 (
frontend/): Leaflet map with verification rings, live signal feed, incident dossier, and the live status tiles. A single 5 s poll loop (hooks/useDashboard.ts) is the only data source.
Clean pipeline separation: ingestion · AI verification · API routing are independently testable.
| Method | Path | Purpose |
|---|---|---|
GET |
/api/incidents |
Verified, clustered incidents |
GET |
/api/debunked |
Reports caught by the credibility filter |
GET |
/api/health |
Liveness, AI mode, per-connector feed status |
GET |
/api/ingestion/inbox |
What was received + why it was/wasn't displayed |
GET |
/api/{dwd,pegel,mobidata}/status |
Live status tiles |
POST |
/api/poll · /api/reset · /api/reports |
Operator actions (poll now · wipe & re-poll · inject a report) |
- All data is real OSINT. Sources: NINA (
warnung.bund.de), Presseportal RSS, public Mastodon timelines, DWD (via Bright Sky), PegelOnline (WSV), MobiData BW. Map tiles © OpenStreetMap / CARTO; geocoding via Nominatim (per OSM usage policy). - Metric system throughout — kilometres, metres, °C.
- Tests run fully offline (
npm run test:api): connectors replay recorded fixtures and the LLM client is monkeypatched — zero network calls, no burned credits.