A system for an open, public record of government vehicles on public roads, run by volunteers. Private number plates are destroyed on the camera and are never stored. https://sparrowmap.com
  • Python 62.6%
  • HTML 21.4%
  • JavaScript 10.8%
  • CSS 1.6%
  • Shell 1.6%
  • Other 1.9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
scriggs dfc16e4198 Phone cameras update themselves and keep watching
The clear-photo fix (fa2c7ff) shipped while both online phone cameras
(Seattle, Kelso) kept the old code: a volunteer's phone runs /app for days
and only a reload loads new code, and watching only starts on a tap.

- The heartbeat carries app_v (hub._app_version: the ?v= app.html loads).
- /app compares it with the ?v= it was loaded with; when behind, it
  reloads and, if it was watching, starts watching again with no tap.
- Once per version per tab (no loop if a reload comes back old), and never
  while the "is this a cop?" card is open.

Tests: tools/test_app_selfupdate.py drives the real /app in headless Chrome
with a fake camera: hub ahead -> one reload, watching again, no loop; same
version -> no reload (12 pass). tools/cdp.mjs is the shared driver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:31:26 -04:00
.github Search is plates only, and the checksum promise is now a published fact 2026-08-16 22:46:11 -04:00
brand SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
camctl grid: make saves fast — write sidecars now, update the index in the background 2026-08-22 19:54:58 -04:00
deploy nav: make the camera-avoidance detour a driver setting, with the cap raised 2026-09-27 00:07:07 -04:00
desktop /business becomes /IPCamera, and SparrowMap4Biz becomes "Add an IP Camera" 2026-08-16 12:12:39 -04:00
detect Score contributor crops before the fleet's so drive mode can ask in time 2026-10-07 16:54:35 -04:00
docs Officer accountability: national case ingest (dormant, no routes yet) 2026-09-05 15:54:04 -04:00
firmware/esp32cam-espnow guides: /espcam, the ESP32-CAM over ESP-NOW build by Lucian H, with links to his repo 2026-09-13 22:44:35 -04:00
landing Landing: sparrowmap.com/send reaches Sparrow Send 2026-09-07 20:22:10 -04:00
public Phone cameras update themselves and keep watching 2026-10-13 16:31:26 -04:00
rf What's-new panel, drone detection, ESP32 firmware, nav on the new pages 2026-08-22 11:58:23 -04:00
sources SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
tools Phone cameras update themselves and keep watching 2026-10-13 16:31:26 -04:00
train Let review-pen verdicts actually train the head, and keep them out of the test set 2026-10-07 08:18:40 -04:00
.gitattributes Keep shell scripts LF so curl|bash installs never break 2026-08-11 19:41:36 -04:00
.gitignore send: auto-rekey self-heal over mailbox AND mesh + K_REKEY marker 2026-08-30 19:25:33 -04:00
air_relay.py air_relay: mesh-lane frames keep 7-day mailbox-grade retention 2026-08-30 19:24:44 -04:00
aircraft.py Drive-mode radar alerts, sensor feeders + live drone/radio layers, setup guides, support navbar 2026-08-23 13:13:04 -04:00
board.py Contributor beta: a second hub on the same checkout, gated at the door, with a request board 2026-09-13 01:10:21 -04:00
bugs.py Report a bug from any page, with a screenshot, without publishing it 2026-08-15 12:35:32 -04:00
classify.py Private security is not government: refuse to publish it as police 2026-08-16 22:16:29 -04:00
CODE_OF_CONDUCT.md SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
config.launch.json SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
constraints.txt SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
CONTRIBUTING.md SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
core.py Contributor beta: a second hub on the same checkout, gated at the door, with a request board 2026-09-13 01:10:21 -04:00
db.py Drive mode: keep the clear photo on the phone until the sighting is reviewed 2026-10-11 22:26:03 -04:00
DEPLOY.md SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
dualstack.py Gate requests, not connections - the third attempt and the right noun 2026-08-14 00:08:23 -04:00
geocode.py geocode: fix the 429 lockout and make place search Apple-Maps robust 2026-09-26 21:33:06 -04:00
help_api.py Community labelling, and make the proof page something you can act on 2026-08-18 08:15:22 -04:00
hub.py Phone cameras update themselves and keep watching 2026-10-13 16:31:26 -04:00
isolate.py review: publish the crop he approved, and stop the pen admitting a dead score band 2026-09-02 17:59:53 -04:00
isolate_pil.py snap: recolor removed-background from warm brown to the detail-card navy 2026-08-26 18:07:59 -04:00
labelbank.py labelbank: sign confirm/label posts so operator confirmations reach the map 2026-08-26 12:56:06 -04:00
LICENSE Initial commit 2026-08-11 00:12:04 -04:00
mirror.py Score contributor crops before the fleet's so drive mode can ask in time 2026-10-07 16:54:35 -04:00
nav.py drive: show every ALPR camera in view, with the direction it faces 2026-09-27 09:29:13 -04:00
node_key.py Cameras now sign what they claim, so pubkey stops being decoration 2026-08-14 15:52:29 -04:00
node_label.py Add 'screen' label: a vehicle photographed off a screen (a faked sighting) 2026-08-22 17:54:22 -04:00
nodes.py A sighting must never wait on a network road lookup 2026-08-19 13:26:13 -04:00
operator_auth.py SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
oversight.py oversight: classify federal law enforcement - FBI, DHS, ICE, CBP, DEA, ATF, USMS 2026-09-24 19:22:25 -04:00
oversight_api.py Officer accountability phase 2: the /ov review surface 2026-09-12 16:14:00 -04:00
privacy.py speed: bank how a vehicle crossed the frame, and turn road dashes into a ruler 2026-09-24 20:30:23 -04:00
publish_stats.py Every client that talks to the hub now says who it is 2026-08-14 08:49:52 -04:00
qr.py Fix Sparrow Send QR (broken image) + WiFi CSI capture pipeline 2026-08-24 11:31:29 -04:00
README.md Traffic-only view, a 5-minute window, and stop promising every plate is destroyed 2026-08-14 16:41:51 -04:00
requirements-node.txt Add trained-head routing for phone crops + Phase 2 mirror relay 2026-08-11 12:07:08 -04:00
requirements-relay.txt /business: a shop owner goes from "I have a camera outside" to a running relay 2026-08-14 22:59:44 -04:00
review_api.py Phone cameras: the hub decides how long the clear photo is kept 2026-10-13 16:20:10 -04:00
review_auth.py A camera key opens the pool too - it was gating nothing 2026-08-15 17:48:59 -04:00
road.py road: 4000 cached cells still thrashed, because eviction dumps half 2026-08-19 14:52:01 -04:00
ROADMAP.md SparrowMap: a citizen-run camera network that watches government vehicles 2026-08-11 00:38:22 -04:00
send_push.py Sparrow Send: Web Push notifications (works with the app closed, incl. iOS) 2026-08-24 21:06:58 -04:00
send_relay.py Sparrow Send: free the 'sparrow' handle for the project owner to claim 2026-08-24 20:02:19 -04:00
snapshot.py review: publish the crop he approved, and stop the pen admitting a dead score band 2026-09-02 17:59:53 -04:00
test_air_relay.py Add air_relay module test; gitignore the downloaded RF OUI registry 2026-08-27 19:27:26 -04:00
THREAT_MODEL.md Stop destroying the evidence before the human is asked 2026-08-14 17:27:05 -04:00

SparrowMap

sparrowmap.com — a licence-plate camera network that the neighbourhood owns instead of a police department renting.

"And ye shall be brought before governors and kings for my sake, for a testimony against them." — Matthew 10:18

Volunteers point a camera at a public road from their own property. The camera does all recognition locally and sends a detection event, never video. The results land on a public map anyone can open with no account.

Start Sparrow.bat
  map          http://localhost:8150/
  transparency http://localhost:8150/transparency
  camera app   https://localhost:8151/app          <- note https

The hub ships with a simulated town (sources/synthetic.py) so the whole system is testable against known ground truth with no hardware attached. It is off by default; python hub.py --sim turns it on.

Running a camera? The one-tap browser camera needs no install — open the site and press Add a camera. For the full local pipeline (accurate vehicle classification, plate reading, and the trained government-vehicle head), see docs/DESKTOP_NODE.md.


The design decision everything else follows from

A network like this can be built two ways and only one is worth building. If every plate is public and searchable, anyone can look up an ex-partner's week with photographs. That is not accountability, that is a stalking service with a civic-sounding name.

So there are two tiers, enforced in code rather than in a policy document:

Public tier Private tier
who government vehicles, local and federal everyone else
plate text stored readable, searchable never written to disk
snapshot plate legible plate destroyed in the pixels
retention indefinite (public record) 14 days, then deleted
lookup by plate yes, and searches are never logged no path exists

A publicly owned vehicle, doing public work, on a public road, is a public record. A private person driving to work is not.

Five things that make the tiering real rather than decorative

  1. The gate is conservative and corroborated. A vehicle reaches the public tier only above 0.85 confidence and only when something other than the plate text agrees. A single OCR slip must never be able to publish a stranger. classify.py.

  2. A private plate is destroyed in the image, not just in the database. This is the hole that sinks most designs: every plate reader photographs the plate, so a private tier that keeps the photo keeps the plate. SparrowMap pixelates and then bars the plate region before the JPEG is written. snapshot.redact_plate, gated on tier != "public" in snapshot.py - a government plate stays legible in the snapshot on purpose, because that photograph is the public record.

  3. Snapshots are crops of the vehicle, not the frame. The pedestrian, the house number, the kid in the yard are not in a tight crop of a car's rear end. Full-frame storage is off by default and requires face blurring when on.

  4. The hash key rotates. Private plates are keyed hashes so a car still re-identifies across cameras. The key rotates every 30 days, so trails cannot be joined across the boundary. This caps how long anyone can follow a private citizen including whoever runs the hub. privacy.rotate_pepper.

  5. No video crosses the network boundary. Recognition is on-device; what arrives is a few hundred bytes and one still. There is no stream to intercept, subpoena or leak.

Layout

core.py         paths + the entire privacy posture as one config block
privacy.py      plate hashing, pepper rotation, retention, redaction policy
classify.py     police / gov / fleet / civilian, with evidence and a hard gate
snapshot.py     vehicle crop, plate redaction, provenance stamp
nodes.py        ed25519 node identity, enrollment, camera cone geometry
db.py           sqlite schema + queries
hub.py          stdlib HTTP, the API, the janitor, TLS listener
sources/
  synthetic.py  a simulated town with ground truth, for building with no hardware
public/         map, about, transparency, phone contributor
data/           sparrow.db, snaps/, pepper.json  <- pepper.json is the crown jewel
certs/          self-signed TLS (browsers refuse the camera on plain http)

API

route what
GET /api/sightings?since=&vclass=&bbox=&limit= recent detections, redacted
GET /api/sighting/<id> one detection
GET /api/track/<plate_hash> a vehicle's trail + patrol score
GET /api/nodes cameras, at jittered positions
GET /api/leaderboard?hours= most-seen public vehicles
GET /api/policy this deployment's privacy posture, machine-readable
GET /api/audit the operator's public-tier decisions (searches are never logged)
POST /api/enroll register a camera
POST /api/sightings a node submits a detection (signed, or bearer for phones)

/api/policy exists so an outside auditor can diff a deployment's claims against its behaviour without being trusted with access. A promise nobody can check is not a promise.

Where it is honest about being incomplete

  • The signal weights in classify.py are starting values, not measurements. They need calibrating against locally labelled footage before the confidence numbers mean anything. See ROADMAP.
  • Government vehicles whose only evidence is plate text stay private, on purpose. That is rule 2 doing its job, and it costs real coverage.
  • Phone submissions are one person's eyes. They are allowed to publish, because a human looking at a marked patrol car beats any classifier, but they are marked unverified and attributed to the submitting node.
  • The keyed hash is only as strong as the pepper. A plate has ~10^8 possible values, so anyone who steals data/pepper.json can reverse every live hash. Retention and rotation bound the damage; they do not eliminate it.

Licence, and the name

The code is AGPL-3.0 (see LICENSE). In plain terms:

  • You can run it, read it, modify it, and share it, freely.
  • If you run a modified version as a network service, you must publish your source, including your changes. That is the point: a fork used to watch the public must itself be open to the public. A surveillance vendor cannot take this, close it, and sell it back.
  • Derivatives stay AGPL. Nobody can relicense it or take it proprietary.

The bundled vehicle detector (public/vendor/yolo11n.onnx, Ultralytics YOLO11) is itself AGPL, so the choice is partly the dependency's and partly ours, and it is the right one for this project.

The licence covers the CODE, not the NAME. SparrowMap, the sparrow mark, and sparrowmap.com are the identity of this project, not part of the AGPL grant. You are welcome, encouraged, to run your own instance from this code, but give it your own name and logo rather than presenting it as SparrowMap. (Same principle as Firefox: the source is open, the name is not.)

Running your own regional instance is the design. A network nobody owns is many small servers, not one big one.