Live demo → (runs entirely in your browser — the offline build with the baked snapshot)
Prahari (प्रहरी, sentinel) finds where the ground changed across an Indian region — new construction, land clearing, encroachment — without downloading any satellite imagery. It works on open geo-embedding vectors instead: each 2560 m grid cell has a 1024-dimensional embedding per annual Sentinel-2 snapshot, and a cell whose embedding moved far between 2024 and 2025 changed on the ground. Change detection is one cosine distance per cell; similarity search (cosine again) then sweeps the region for look-alikes of anything you find. No labels, no training, no GPUs.
The UI borrows the look of a Survey of India toposheet, and has day and night themes:
Selecting a flagged cell opens its record, and find similar ranks every other cell in the quadrant by cosine similarity and marks the top matches on the plate — useful for finding more of whatever you just spotted (more new construction, more cleared land):
The pilot AOI is the Bengaluru North-East quadrant: 192 cells of 2560 m (MajorTOM grid), June 2024 vs June 2025, from LGND's open Clay v1.5 Sentinel-2 embeddings (CC-BY 4.0, 15.2B embeddings covering the whole Sentinel-2 archive).
flowchart LR
SC[(Source Coop\nClay v1.5 parquet)] -->|pipeline/ingest.py| DB[(embeddings.duckdb)]
DB -->|pipeline/analyze.py\ncosine Δ + kNN| DB
DB --> API[FastAPI gateway\n/api/v1]
DB -->|pipeline/export_dashboard.py| SNAP[baked snapshot\ndata.ts]
API --> UI[React dashboard]
SNAP -.offline fallback.-> UI
- Change detection — cosine distance between a cell's 2024 and 2025 embeddings; cells beyond +2σ of the AOI mean are flagged for inspection.
- Similarity search — cosine similarity between one cell and every other cell in the same snapshot, served live by the API.
- Alert workflow — flagged cells can be acknowledged; the state is persisted server-side in SQLite.
- Offline fallback — the frontend uses the live API when it can reach one
and otherwise falls back to a baked data snapshot (the footer shows
LIVE APIorOFFLINE SNAPSHOT), sonpm run buildproduces a single self-containeddist/index.htmlthat works with no server at all.
One FastAPI gateway (server/main.py), service-per-module, OpenAPI docs at /docs:
| Endpoint | Service |
|---|---|
GET /api/v1/aois |
geo — areas of interest |
GET /api/v1/aois/{id}/dashboard |
analytics — KPIs, cells, distribution |
GET /api/v1/aois/{id}/cells/{cid}/similar?k= |
vector — similarity search |
GET /api/v1/aois/{id}/alerts · POST …/alerts/{cid}/ack |
alerts — flag lifecycle |
├─ pipeline/ batch data plane
│ ├─ ingest.py pull embeddings for an AOI → embeddings.duckdb
│ ├─ analyze.py change detection + similarity search
│ └─ export_dashboard.py bake dashboard snapshot → webapp/land-watch/src/data.ts
├─ embeddings.duckdb local embedding store (Bengaluru pilot AOI, ships with repo)
├─ server/ Python platform — one FastAPI gateway, service-per-module
│ ├─ main.py gateway: mounts every service under /api/v1
│ ├─ platform/ shared kernel: DuckDB store (→ pgvector seam), alerts repo
│ └─ services/ geo · analytics · vector · alerts
├─ webapp/ micro frontends — one app per product
│ └─ land-watch/ Register of Changes console (React 19 + TS + Vite)
├─ tests/ pytest: export math + API contract (vitest suite lives in the webapp)
└─ ARCHITECTURE.md service boundaries; when each piece becomes its own deployment
Prerequisites: uv and Node 20+. The embedding store ships with the repo, so no ingest is needed to get started.
uv sync # Python deps
uv run python -m pipeline.export_dashboard # bake the frontend snapshot
uv run uvicorn server.main:app --reload # API at :8000 (docs at /docs)
cd webapp/land-watch
npm install
npm run dev # dashboard at :5173, proxies /api → :8000To re-pull embeddings from source (needs internet, ~2 min):
uv run python -m pipeline.ingest.
uv run pytest # export math + API contract
cd webapp/land-watch && npm test # components + interactions
cd webapp/land-watch && npm run build # typecheck + single-file offline bundleThis is a proof of concept for a startup idea I was exploring: a geo-embeddings analytics platform hosted in India, built around the 2021 geospatial data-residency rules and the private national EO constellation now under construction. The research ended up talking me out of the original pitch (the market is smaller than it looks and the regulatory moat is weaker than it reads), but all the notes are in the repo:
- NOTES.md — the concept, the landscape (LGND, Google AlphaEarth), and why a plain vector-DB product no longer makes sense
- RESEARCH.md — viability research with sources. Short version: not viable as originally pitched, possibly viable as something narrower
- ARCHITECTURE.md — where the service boundaries would be if this grew into a real platform, and what has to happen before each one gets split out
Everything runs locally: DuckDB for the vectors, SQLite for workflow state. The Postgres + pgvector migration path is written down in ARCHITECTURE.md instead of built.
