他们在监视。我们在反监视。
一个以 Android 为先的反监视 PWA,能在驾驶员驶过自动车牌识别器之前,告诉他们这些设备在哪里。
它针对一个可离线使用的摄像头档案库提供实时接近警报、本地暴露历史、设备端签名的哈希链式摄像头报告队列, 以及关于你周围运营摄像头的机构的、有来源支撑的公开记录背景信息。
已部署的应用没有报告上传端点。报告目前保留在 IndexedDB 中。提交网关作为运营方代码存在,并非已部署的路由; 这一区分在本 README 中始终是有意为之的。
它刻意不做第六件事:它不查询任何厂商的系统,车牌值永远不会离开设备。
darkroute/
├── apps/
│ ├── pwa/ React 19 + TypeScript + Vite PWA
│ ├── desktop/ api.darkroute.ai 上的开发者控制台;其 functions/ 将 /v1 和 /api/v1 代理到主域
│ └── android/ 可构建的 Android Trusted Web Activity 外壳
├── packages/
│ ├── core/ 纯几何与警报引擎;无 DOM 或 I/O
│ └── api-client/ 空占位符;实际契约是 GET /api/v1/openapi.json
├── functions/
│ ├── cameras/ 来自 CAMERA_TILES R2 绑定的同源摄像头瓦片
│ └── api/
│ ├── v1/ 公共 API:读取、/submit(开启 PR)、/photo、openapi.json、_middleware
│ └── admin/ Cloudflare Access 测试者允许列表(仅私有开发主机)
├── scripts/ 摄像头流水线、部署、资产与策略检查
├── docs/ 实现、运维与公开文档
├── the design-gap ledger 未解决的设计决策与替代方案
└── installer.sh
LoRa 协议栈是 Meshtastic 自有的、未经修改:本项目不构建固件,而是与现成节点配对。
packages/api-client 仍为占位符;生产 PWA 不使用它。
已部署的产品是一个静态 PWA 加上 Cloudflare Pages Functions:摄像头瓦片路由、
/api/v1/ 下的公共 API(见公共 API),以及私有开发主机上两个受 Access 保护的管理路由。
它没有 FastAPI 进程、Postgres 数据库、在线状态 API 或已部署的报告端点;唯一的写入路径
POST /api/v1/submit 会开启一个公开的拉取请求,而不是写入任何东西。
flowchart LR
OSM["OpenStreetMap"] --> PIPE["scheduled Node camera pipeline"]
PIPE --> R2[("R2 camera archive")]
R2 --> CAM["/cameras/* Pages Function"]
subgraph device["Browser or Android TWA"]
GPS["geolocation + other adapters"] --> CORE["packages/core alert engine"]
CORE --> UI["DRIVE / map / reports"]
UI <--> IDB[("IndexedDB + service-worker cache")]
end
CAM --> IDB
MAP["project tile host (PMTiles)"] --> UI
摄像头瓦片从同源的 /cameras/* 请求,由 CAMERA_TILES R2 绑定提供,并缓存以供离线使用。
底图和速度档案是对项目运营的公共瓦片主机的跨源范围请求。darkroute.ai 是公共应用。
管理 Functions 完全不在本发行版之内。
策展工具和提交网关是运营方代码,不在本发行版之内。二者都不是已部署的运行时服务器, 本仓库中没有任何东西依赖它们:应用在没有它们的情况下也能构建、运行、测试和审计。
地理空间计算位于 packages/core;界面消费存储和适配器。车牌匹配、
行程历史和报告证据链都保留在设备上。
档案库任何人都可以读取,无需密钥,地址为 https://darkroute.ai/api/v1/,也可通过开发者控制台的
代理以 https://api.darkroute.ai/v1/ 和 https://api.darkroute.ai/api/v1/ 提供。
机器可读的契约是 GET /api/v1/openapi.json;文字说明见
docs/public/API.md §1.3;试用它的控制台是
api.darkroute.ai。
| 路由 | 它回答什么 |
|---|---|
GET /api/v1/cameras | bbox 内的摄像头(每边 ≤ 1.5°,≤ 24 个瓦片,≤ 1000 行),可选 owner 过滤 |
GET /api/v1/stats | 档案库大小、代次、构建和上游时间戳 |
GET /api/v1/abuse | 有记录的案例和生成日期;可选县 fips |
GET /api/v1/news | 自动收集的 ALPR 头条、来源链接和收集状态 |
GET /api/v1/atlas | 按县划分的 EFF Atlas 机构和供应商,含检索/核查日期;可选 fips |
GET /api/v1/monitoring | 道路监控设备清单;可选 kind 和 bbox |
GET /api/v1/monitoring/image?id=... | 所选交通摄像头可用的发布方照片 |
GET /api/v1/place | 美国/波多黎各地点查询,经代理,使地理编码器永远看不到驾驶员的地址 |
GET /api/v1/route | 一条最多避开 60 个点的驾驶路线;从不缓存 |
GET /api/v1/doc/{name} | 来自公共镜像的已发布文档,以 Markdown 形式 |
POST /api/v1/submit | 一条更正,转化为 [unreviewed] 拉取请求,不向档案库写入任何内容 |
PUT /api/v1/photo | 提交所引用的一张照片,以其字节为键;GET /api/v1/photo/{key} 将其返回 |
摄像头响应带有 OpenStreetMap 署名,Atlas 响应带有其来源署名。
关于收集时间表以及应用和 API 如何共享数据,见报告与数据新鲜度。
关于默认启用的设备图层、来源覆盖和按需摄像头照片,见道路监控。
关于每个已发布数据集、直接下载、单独计数、新鲜度以及仅存在于设备上的数据,见完整数据清单。摄像头总数不包括监控设备、机构部署和报告。
每个错误都是带有稳定 error 代码的 JSON;/api/v1/ 下的未知
路径返回 404 JSON,而不是应用。每个地址每分钟 60 次请求是减速带,而非
上限,真正的上限是每请求的配额。空答案意味着那里没有映射任何摄像头,绝不意味着
那里不存在摄像头。整个档案库作为数据集发布;批量工作请使用它。
sequenceDiagram
participant GPS as Geolocation adapter
participant Core as Alert engine
participant Store as Alert store
participant UI as DRIVE
participant Out as Haptics / notifications
GPS->>Core: position tick (lat, lon, speed, heading, accuracy)
Core->>Core: accuracy gate - poor fix computes but does not alert
Core->>Core: stationary dwell - a red light does not toggle alerts
Core->>Core: nearest camera, dedupe, hysteresis band
Core->>Store: AlertState + distance + relative direction
Store->>UI: selectors - digits snap, they never tween
Store->>Out: only via useShouldAlertUser()
Note over Store,Out: Muted suppresses this arrow and nothing else.<br/>Detection, logging, exposure counts and map<br/>rendering are identical whether muted or not.
alt camera enters threshold
Out->>Out: camera haptic (2 pulses when multiple)
UI->>UI: alert takes over the screen, above any sheet
else county line crossed
Out->>Out: silent - haptics are reserved for cameras
end
Python、FastAPI、Postgres 和 PostGIS 不是本仓库的前置要求。本地 PWA 开发不需要账户、数据库或后端凭据。
git clone https://github.com/darkcodelabs/darkroute.git
cd darkroute
corepack enable
corepack prepare [email protected] --activate
./installer.sh
installer.sh 会验证受支持的 Node 版本并运行 pnpm install --frozen-lockfile。它是
幂等的,不会创建虚拟环境、复制 .env 文件或更改配置。
本地运行 PWA 不需要环境文件。以下是活跃的配置组:
| 变量或绑定 | 使用者 | 说明 |
|---|---|---|
VITE_FWM_BASEMAP_URL | PWA 构建 | 可选的 PMTiles 档案库覆盖;源码带有默认值。 |
CAMERA_TILES | Pages | functions/cameras/[[path]].ts 使用的 R2 绑定。 |
SUBMISSION_PHOTOS | Pages | 用于 PUT/GET /api/v1/photo 的 R2 绑定;缺失 → 503,不存储任何内容。 |
SUBMISSIONS_TOKEN | Pages | 密钥。允许 POST /api/v1/submit 开启拉取请求的 GitHub 令牌;缺失 → 503。 |
SUBMISSIONS_REPO | Pages | 可选;提交的落地位置。默认 darkcodelabs/darkroute。 |
NOMINATIM_URL, VALHALLA_URL | Pages | 用于 /api/v1/place 和 /api/v1/route 的可选地理编码器/路由器覆盖。 |
仅运营方的配置、管理 Functions、发布 凭据和策展工具,不在此处记录。这些名称 描述的是你无法访问的部署,对构建或 审计本仓库毫无用处;每个脚本在你运行时都会说明它需要什么。
密钥绝不进入源代码控制或 VITE_ 变量,因为 Vite 变量会被打包进
客户端。