Privacy-first map + routing app that helps users avoid mass-surveillance infrastructure (ALPRs, Flock cameras, CCTV).
Currently shipping: directional-cone surveillance-aware routing (Phase 0b-1) + nightly-refreshed all-US camera dataset (Phase 0b-2). Real map product UI (search, directions, mobile, branded design) is Phase 0b-3 — see docs/superpowers/specs/ for design history.
Prereqs: Docker (with docker compose), Node 20+, npm.
npm install
# Bring up Valhalla — downloads ~250MB Georgia OSM PBF and builds tiles on first run
# (~10-20 min the first time; subsequent runs start in seconds).
./scripts/build-valhalla-tiles.sh
# Start the Vite dev server
npm run devFor nationwide routing instead of Atlanta-only, see docs/VALHALLA.md — the full-US build is a multi-hour, ~100 GB working-disk operation behind one extra npm script.
Open http://localhost:5173. The app fetches the latest published camera dataset from this repo's GitHub Releases by default. Pick a profile (Commuter / Activist / Vulnerable / Custom). Click Set Start on map, then click the map. Click Set End on map, then click somewhere else. Click Plan route — see Shortest vs Private routes with the "N cameras avoided" diff line.
To stop Valhalla: npm run valhalla:down.
For the production deployment (Caddy reverse proxy + HTTPS + the strict CSP that enforces the same-origin posture in the browser), see docs/DEPLOYMENT.md. The deploy is a Docker Compose overlay (deploy/docker-compose.prod.yml) that adds Caddy + a hermetic SPA builder to the existing Valhalla service — no Node on the host required.
Set the local-seed flag to use the bundled 12-camera Atlanta seed instead of fetching the live dataset:
VITE_USE_LOCAL_SEED=true npm run devCamera data is built nightly by a GitHub Action from two sources:
- DeFlock (deflock.me) — ALPR-focused dataset, published as static JSON tiles on Cloudflare CDN. See
scripts/build-dataset/DEFLOCK-ARCHITECTURE.mdfor upstream details. - OpenStreetMap — broader surveillance camera coverage via Overpass API.
Output: cameras-us.json + cameras-us.json.meta.json (manifest) + per-city subsets, published as GitHub Release assets at releases/latest. Free CDN-fronted by GitHub.
The merge applies a 10m proximity dedup with per-field source-preference rules (DeFlock wins ALPR conflicts; OSM wins non-ALPR conflicts). The resulting Camera records carry a sources array showing every contributing source as a trust signal.
# Against the committed fixtures (no network)
npm run build:dataset:fixture
# Live build (hits DeFlock CDN + Overpass — what the nightly Action does)
npm run build:datasetOutput lands in ./dist-dataset/ (gitignored).
- Code: AGPL-3.0 — prevents commercial enclosure; aligns with civil-liberties posture.
- Data: ODbL — see LICENSE-DATA.md for upstream attribution and downstream obligations.
npm test # unit + integration (integration tests skip-warn if Valhalla is down)
npx playwright test # privacy invariants + Atlanta benchmark (skip if Valhalla is down)Fast unit-only run:
npx vitest run tests/unitBehavior when Valhalla isn't reachable: integration tests and Playwright tests detect the unhealthy server in beforeAll and skip / early-return with a clear console message. The unit tests still run, so CI stays green even if Valhalla is down.
download.geofabrik.de is having an outage. Wait a few minutes and re-run. If it stays down, edit docker-compose.yml and change tile_urls to a working mirror (e.g. https://download.openstreetmap.fr/extracts/north-america/us/georgia.osm.pbf), then npm run valhalla:down && ./scripts/build-valhalla-tiles.sh. The same mirror trick applies to the full-US build — see docs/VALHALLA.md for the maintained list.
src/
├── domain/ # Camera (v3), ThreatProfile, Route, CameraTypeDefaults
├── data/ # CameraStore, ResolvedCamera, DatasetManifest
├── routing/ # ConePolygon, coneFromProfile, Valhalla client, Router, RouteScorer
├── privacy/ # Network host allowlist
└── ui/ # MapView, ProfilePicker, CustomProfileEditor, RoutePlanner, DatasetFreshness
scripts/
└── build-dataset/ # GH Action pipeline (fetchers, normalizer, merge, validate, publish)
tests/
├── unit/ # Vitest, pure-logic tests (jsdom for ui/)
├── integration/ # Vitest hits live Valhalla / fixture-mode pipeline
├── privacy/ # Playwright network-allowlist invariants
└── benchmark/ # Playwright per-city + aggregate routing-quality benchmark
This app's privacy stance is enforced by structure, not just policy:
- Routing computation runs server-side via locally-hosted Valhalla; trip endpoints go to
localhost:8002only (routed through the Vite dev proxy at/valhallafor same-origin compliance) - Browser → camera dataset fetches go to GitHub Release CDN (
github.com+objects.githubusercontent.com) - Map tiles fetched from
*.tile.openstreetmap.org - No analytics, no telemetry, no third-party scripts
The privacy-invariant Playwright tests (tests/privacy/networkInvariants.spec.ts) enforce that the running app makes no requests to any host outside this allowlist, and that the route request body carries no user/session identifiers.