Connecting the world's events in real time.
Meridian pulls live public feeds and turns them into typed events: a place, a time, a source. It links events that share space, time, or identity, and shows the result as a map, an event feed, and a link graph. Every object keeps its source and a confidence score that decays with age. Every link keeps its basis. Correlation is not causation, and Meridian doesn't pretend otherwise.
It runs entirely on Cloudflare's free tier.
Live instance: https://meridian.calm-butterfly-4753.workers.dev
Most "intelligence dashboards" are a map with dots that appear and disappear and never connect to anything. The dots aren't the point here. The connections between them are, and every connection has to show its work.
Nothing is invented to look authoritative. A 37-day-old event reads as low-confidence because it is: every object carries its source, fetch time, and a confidence score that decays with age. Same with links — a link records its basis (same-type spatio-temporal clustering and co-causal cross-domain co-occurrence today, shared-entity and semantic similarity on the roadmap) and a confidence, and it can only say events co-occurred and why they might be related. It never says one caused the other. That call is the analyst's, not the software's.
Entity resolution is real, not cosmetic: aircraft resolve by ICAO hex, vessels by MMSI, places by country and gazetteer, so the same real-world thing seen through five different feeds collapses into one entity and the links between events actually mean something.
Sources are official and public only. No surveillance of named individuals, no network scanning, no scanner audio, no facial recognition, no license-plate data. This reads the world from public records, not from watching people.
It runs on Cloudflare's free tier — Workers, D1, KV, R2, Cron. Vectorize and Workers AI are already wired in for the semantic-correlation and grounded-summary work that's next.
Live public feeds -> Cron ingest -> raw archive (R2)
|
v
normalize to typed events
|
v
D1 ontology <-> entity resolution (hex / MMSI / ISO3 / gazetteer)
|
v
correlation engine (spatio-temporal + co-causal cross-domain)
|
v
Map (MapLibre, flat + globe) + scoped event feed + link graph + inspector
Feeds are pulled on a schedule and archived as raw JSON in R2, then normalized into typed objects in D1. Because the raw tier is preserved, an upstream outage re-normalizes from the last archived blob instead of dropping the layer, and the archive doubles as replay history. Objects are resolved into entities, linked by the correlation engine, and read by the front end. Reads never hit the live sources.
Every event normalizes to a typed object: { id, domain, lat, lon, timestamp, severity, source, source_url, fetched_at, confidence, properties }, tagged with one of 13 domains (seismic, environmental, disaster, maritime, aviation, space, conflict, energy, health, transport, civic, political, other) plus country and admin-1 region. Domains and regions are first-class, so "scopes" (for example, Europe today, or global maritime and energy) are simply saved filters over the same data.
Spatio-temporally clustered events are folded into incidents: one anchor event plus the events that occur within a tight distance and time window of it, each member linked to the anchor with a stated basis. The cluster is anchor-radial by design, so an incident cannot sprawl across unrelated regions.
Cross-domain incidents go a step further: events of different types that co-occur AND share a plausible mechanism (a volcano and its earthquake swarm, a storm and the floods it drives). Membership is gated to an explicit co-causal whitelist, so naive proximity (a wildfire that merely happens to be near a quake) never forms one. Each pair carries a time window matched to how long the phenomenon stays active.
Every object stores where it came from, when it was fetched, and a confidence score derived from source reliability and recency. Every link stores its basis and confidence. A link without a basis cannot be created. This is enforced in code, not by convention.
A single Cloudflare Worker serves both the SPA and the API.
Browser (React + TypeScript + MapLibre GL)
|
v
Cloudflare Worker (Hono)
/api/health liveness
/api/objects read ontology from D1
/api/links read derived links from D1
/api/object/:id object detail + neighbors + provenance + entities
/api/incidents correlated incident clusters
/api/incidents/cross-domain co-causal cross-type incidents
/api/incident/:id incident detail + members
/api/entity/:id resolved entity + its events
/api/action write audited action + state
/api/ingest/run guarded manual ingest (token-protected)
* SPA static assets (Vite build in ./dist)
|
+-- D1 (SQLite): ontology, entities, links, incidents, audit trail
+-- KV: short-TTL feed cache + write budgets
+-- R2: raw feed archive (replay + resilience)
+-- Vectorize: semantic-correlation index (provisioned; roadmap)
+-- Workers AI: grounded summaries (provisioned; roadmap)
|
Cron -> ingest feeds -> archive raw (R2) -> normalize -> resolve entities -> derive links -> correlate
- Cloudflare Workers with static assets (not Pages), Hono for the API.
- React 18 + TypeScript + Vite for the SPA.
- MapLibre GL JS for the map, flat and globe projection, no token required.
- d3-force for the link-graph view.
- Cloudflare D1 (ontology + audit), KV (cache), R2 (raw archive), Cron (ingest). Vectorize and Workers AI are bound and provisioned for roadmap work (semantic correlation, grounded summaries).
- zod for API payload validation.
Everything runs on Cloudflare's free tier. Nothing runs locally in production.
meridian/
worker/ Hono API, D1 access, feed adapters, ingestion + correlation
web/ React + MapLibre SPA (builds to ./dist)
shared/ types shared by Worker and SPA
migrations/ D1 SQL migrations
wrangler.jsonc, vite.config.ts, package.json
npm install
npm run build # vite build -> ./dist
npm run dev # wrangler dev (local config): serves the SPA and the API together
Pushing to main triggers Cloudflare Workers Builds: npm ci && npm run build, then deploy, with D1 migrations applied automatically. D1, KV, R2, and the Vectorize index are created once by the operator; their bindings live in wrangler.jsonc. Secrets (INGEST_TOKEN, feed API keys) are set as Worker secrets, never committed.
Meridian draws only on official, public, free, and legal feeds. Live layers today include seismic (USGS), aviation (ADS-B via airplanes.live), maritime AIS (regional via Digitraffic, plus global live AIS via aisstream.io), global active fire (NASA FIRMS) and wildfire (NIFC), weather alerts (NWS), tropical cyclones (NHC), environmental events (NASA EONET), disasters (GDACS), space (CNEOS fireballs, Launch Library), and orbital (live satellites via CelesTrak), with more official sources being added in waves. See SOURCES.md for the full feed list and the provenance of each.
Fast-moving live layers (global AIS vessels, satellites) are kept out of the durable ontology and rendered as overlays: a SQLite-backed Durable Object holds the aisstream.io WebSocket open in bounded windows and serves vessel snapshots, and satellite positions are propagated client-side from orbital elements. Both stay within the free tier.
Not yet live, in build order:
- Shared-entity and semantic correlation (Vectorize embeddings + Workers AI), beyond the spatio-temporal and cross-domain links shipping today.
- Grounded event summaries via Workers AI, cited to sources, never invented.
- Live-updating mode: new events animate onto the map as they arrive (a Durable Object WebSocket ticker, once sub-minute latency justifies leaving pure free-tier).
- Additional official feeds across the energy, health, and civic domains.
Meridian uses official, public, aggregate sources only. It does not track named private individuals, scan or probe infrastructure it does not own, ingest scanner audio, perform facial recognition, or collect license-plate or personal-social-media data. Several free feeds are licensed for non-commercial use; Meridian honors each source's license and records it per source. Correlations are labeled as correlations, never as proven cause.
Security findings are taken seriously and fixed before new features ship. See SECURITY.md to report a vulnerability.
Contributions are welcome. Because Meridian is offered under a dual license (see below), external contributions require a Contributor License Agreement before they can be merged, so the project retains the right to offer commercial licenses. See CONTRIBUTING.md for the process and CLA.md for the agreement.
Meridian is AGPL-3.0 for open-source use. Meridian is original work, not a fork. Running a modified version as a network service requires offering its source under the same license. See LICENSE.
For a commercial license without AGPL obligations, contact the maintainer (Nick Sanchez, github.com/nickk02).