15 KiB
ADAMaps Technical Documentation
Last updated: 2026-03-22
1. Overview
ADAMaps is a community-owned road sign mapping network built on:
- Hivemapper Bee dashcams (liberated from Hivemapper's proprietary ecosystem)
- PostGIS for geospatial data storage
- Flask API on Rackham (public VPS) for data ingest and serving
- MAP token (Cardano native asset) for contributor rewards
- Agent Training API for AI-based sign classification
The system has three main code repositories:
Sulkta-Coop/adamaps— Backend API, database schema, web frontend, deployment scriptsSulkta-Coop/adacam-api— On-device API running on liberated Bee dashcamsSulkta-Coop/adacam— Liberation scripts, firmware recovery, security research
2. API Architecture
2.1 Main API (Rackham)
Location: Deployed on Rackham VPS (142.44.213.229)
Framework: Flask (Python)
WSGI: Gunicorn (2 workers)
Port: 5001 (internal), reverse-proxied via ISPConfig to api.adamaps.org
Core Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/health |
None | Health check, returns {"status": "ok", "node": "rackham"} |
| GET | /api/stats |
None | Total detections, devices, images, signs counts |
| GET | /api/detections |
None | GeoJSON-style detection list (?limit=N) |
| GET | /api/signs |
None | Clustered sign inventory (?verified=true, ?limit=N) |
| POST | /api/ingest |
X-AdaMaps-Key |
Ingest detection batch from devices |
| POST | /api/images |
X-AdaMaps-Key |
Upload detection images (multipart) |
| GET | /api/images/<filename> |
None | Serve stored images |
Agent Training Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/agent/info |
None | API discovery, sign types, reward structure |
| GET | /api/agent/task |
X-Agent-Wallet |
Get task (image) to label |
| POST | /api/agent/submit |
X-Agent-Wallet |
Submit label for task |
| GET | /api/agent/stats |
X-Agent-Wallet |
Agent performance stats |
| GET | /api/agent/leaderboard |
None | Top agents by earnings |
| GET | /.well-known/agent-training.json |
None | Discovery endpoint |
Note: Agent API is gated by AGENT_API_LIVE = False. When image pipeline is ready, flip to True to enable task generation.
Authentication
Device Ingest:
Header: X-AdaMaps-Key: adamaps-ingest-2026
Agent Operations:
Header: X-Agent-Wallet: addr1qxy... (Cardano wallet address)
No API key required for agents — the wallet IS the identity.
2.2 On-Device API (adacam-api)
Runs on each liberated Hivemapper Bee dashcam.
Framework: Flask
Port: 5000
Data sources: Redis (GPS/IMU from camera pipeline), SQLite (local detection storage)
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /pair |
None | Pairing info for Varroa app |
| GET | /api/1/info |
None | Device identity |
| GET | /api/1/landmarks/last/<N> |
None | Last N detections |
| POST | /api/1/landmarks |
Token | Ingest new detection |
| GET | /api/1/gnssConcise/latestValid |
None | Current GPS fix |
| GET | /api/1/status |
None | Device status |
| GET | /api/1/recording/frames/latest |
None | Latest frame path |
| GET | /api/1/wifi/status |
None | WiFi client status |
| POST | /api/1/wifi/connect |
Token | Connect to WiFi |
| GET | /api/1/ssh/status |
None | SSH daemon status |
| POST | /api/1/ssh/toggle |
Token | Enable/disable SSH |
Config file: /data/adacam/config.json
{
"device_id": "auto-generated UUID",
"adamaps_key": "adamaps-ingest-2026",
"adamaps_api": "https://api.adamaps.org",
"ap_interface": "wlp1s0f0"
}
3. Database Schema
3.1 PostGIS Database (adamaps-postgres)
Image: postgis/postgis:16-3.4
Container: adamaps-postgres
Credentials: adamaps:adamaps2026
Database: adamaps
Tables
detections — Raw detection observations
CREATE TABLE detections (
id SERIAL PRIMARY KEY,
device_id TEXT NOT NULL,
detected_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
lat DOUBLE PRECISION NOT NULL,
lon DOUBLE PRECISION NOT NULL,
geom GEOMETRY(Point, 4326) GENERATED ALWAYS AS
(ST_SetSRID(ST_MakePoint(lon, lat), 4326)) STORED,
sign_type TEXT,
confidence DOUBLE PRECISION,
speed_mph DOUBLE PRECISION,
heading DOUBLE PRECISION,
image_path TEXT,
wallet_address TEXT,
raw_json JSONB
);
-- Indexes
CREATE INDEX detections_geom_idx ON detections USING GIST(geom);
CREATE INDEX detections_device_idx ON detections(device_id);
CREATE INDEX detections_time_idx ON detections(detected_at DESC);
signs — Clustered, deduplicated sign inventory (created by clustering job)
CREATE TABLE signs (
id SERIAL PRIMARY KEY,
lat DOUBLE PRECISION,
lon DOUBLE PRECISION,
sign_type TEXT,
heading TEXT,
observation_count INTEGER,
avg_confidence DOUBLE PRECISION,
first_seen TIMESTAMPTZ,
last_seen TIMESTAMPTZ,
device_count INTEGER,
verified BOOLEAN DEFAULT FALSE
);
agent_stats — Agent performance tracking
CREATE TABLE agent_stats (
wallet VARCHAR(128) PRIMARY KEY,
tasks_completed INTEGER DEFAULT 0,
consensus_agreements INTEGER DEFAULT 0,
total_earned DOUBLE PRECISION DEFAULT 0,
pending_rewards DOUBLE PRECISION DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
training_tasks — Image labeling tasks for agents
CREATE TABLE training_tasks (
task_id VARCHAR(64) PRIMARY KEY,
detection_id INTEGER NOT NULL,
image_url VARCHAR(512),
ai_guess VARCHAR(128),
ai_confidence DOUBLE PRECISION,
status VARCHAR(32) DEFAULT 'pending', -- pending | consensus | disputed
final_label VARCHAR(128),
labels_received INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW(),
expires_at TIMESTAMP,
consensus_at TIMESTAMP
);
task_labels — Individual agent label submissions
CREATE TABLE task_labels (
id SERIAL PRIMARY KEY,
task_id VARCHAR(64) NOT NULL,
wallet VARCHAR(128) NOT NULL,
label VARCHAR(128) NOT NULL,
confidence DOUBLE PRECISION,
reasoning TEXT,
reward_earned DOUBLE PRECISION DEFAULT 0,
submitted_at TIMESTAMP DEFAULT NOW(),
UNIQUE(task_id, wallet)
);
3.2 On-Device SQLite (adacam-api)
Path: /data/adacam/adacam.db
CREATE TABLE landmarks (
id INTEGER PRIMARY KEY,
class_label TEXT,
class_label_confidence REAL,
overall_confidence REAL,
lat REAL, lon REAL, alt REAL,
azimuth REAL,
width INTEGER, height INTEGER,
ts INTEGER,
forwarded INTEGER DEFAULT 0
);
CREATE TABLE forward_queue (
id INTEGER PRIMARY KEY AUTOINCREMENT,
payload TEXT,
created_at INTEGER DEFAULT (strftime('%s','now'))
);
4. Data Ingest Flow
4.1 Detection Ingest
Endpoint: POST /api/ingest
Headers:
X-AdaMaps-Key: adamaps-ingest-2026
Content-Type: application/json
Payload formats accepted:
Array of detections:
[{
"device_id": "fvhL2I-iCT",
"lat": 33.8677,
"lon": -118.3776,
"class_label": "stop_sign",
"overall_confidence": 0.94,
"ts": 1710345600000,
"wallet_address": "addr1..."
}]
Or wrapped format:
{
"device_id": "fvhL2I-iCT",
"detections": [...]
}
Response:
{"inserted": 5, "device_id": "fvhL2I-iCT"}
4.2 Image Upload
Endpoint: POST /api/images
Headers:
X-AdaMaps-Key: adamaps-ingest-2026
Content-Type: multipart/form-data
Form fields:
detection_id(required): ID of detection this image belongs todevice_id(optional): Device identifierimage(file): JPEG/PNG image file
Response:
{
"status": "ok",
"filename": "fvhL2I-iCT_12345_a1b2c3d4.jpg",
"url": "https://api.adamaps.org/api/images/fvhL2I-iCT_12345_a1b2c3d4.jpg"
}
Storage: /opt/adamaps/images/ on Rackham
4.3 Device → AdaMaps Flow
┌──────────────────┐ Redis ┌──────────────────┐
│ Camera Pipeline │───GNSSFusion30Hz──▶│ adacam-api │
│ (Bee onboard) │ │ (Flask:5000) │
└──────────────────┘ └────────┬─────────┘
│
SQLite + Forward Queue
│
▼
┌──────────────────────────┐
│ AdaMaps API (Rackham) │
│ https://api.adamaps.org │
│ POST /api/ingest │
└──────────────────────────┘
Offline resilience: adacam-api queues detections in SQLite's forward_queue table if Rackham is unreachable. Background thread retries every 30 seconds.
5. Infrastructure
5.1 Rackham VPS
Hostname: rackham.hazedhosting.com
Public IP: 142.44.213.229
VPN IP: 192.168.254.1 (WireGuard)
Provider: OVH (via HazedHosting reseller)
SSH: Port 22, key-based (cobb@, sudo password: T3mLHfzb)
Docker containers:
adamaps-api— Flask API (port 5001 internal)adamaps-postgres— PostGIS database (internal only, accessible via VPN at 192.168.254.112)
Reverse proxy: ISPConfig vhost routes api.adamaps.org → 127.0.0.1:5001
Image storage: /opt/adamaps/images/
5.2 Lucy (Unraid)
Role: Development, database (some configs point here)
IP: 192.168.0.5 (LAN), 192.168.254.112 (VPN)
Services:
- Gitea:
http://192.168.0.5:3001 - Dev nginx:
http://192.168.0.5:9999(artifact serving) - Docker Compose stack (local testing)
5.3 VPN Topology
WireGuard mesh connecting infrastructure:
┌─────────────────┐ ┌─────────────────┐
│ Lucy │◄───────►│ Rackham │
│ 192.168.254.112│ VPN │ 192.168.254.1 │
│ (Unraid/home) │ │ (OVH VPS) │
└────────┬────────┘ └─────────────────┘
│
│ VPN (when device online)
│
┌────────▼────────┐
│ Bee Dashcam │
│ 192.168.254.x │
│ (mobile, LTE) │
└─────────────────┘
Postgres access: Rackham API connects to 192.168.254.112:5432 (Lucy's Postgres via VPN)
5.4 Bee Dashcam (AdaCam)
Hardware:
- Intel Keem Bay SoC (Movidius MV0212)
- Yocto Linux, dm-verity rootfs (verity disabled at runtime)
- eMMC storage (11 partitions, A/B slots)
- LTE modem (LE910C4-NF)
- Dual WiFi (AP + client interfaces)
Key paths:
/data— Persistent storage (survives firmware flash)/data/adacam/— Config, SQLite database/opt/adacam/— adacam-api installation/etc— Overlay from/data/overlay/current/
Services (post-liberation):
adacam-api.service— Flask API (systemd)bee-tunnel.service— SSH tunnel to Lucy
6. Current State
6.1 Deployed & Working
| Component | Status | Notes |
|---|---|---|
| AdaMaps API (Rackham) | ✅ Running | Core endpoints functional |
| PostGIS database | ✅ Running | On Lucy, accessible via VPN |
| Detection ingest | ✅ Working | /api/ingest accepts device data |
| Image upload | ✅ Working | /api/images stores to disk |
| Agent Training API | ⏸️ Code ready | AGENT_API_LIVE = False, awaiting image pipeline |
| Bee Unit 1 | ✅ Liberated | SSH working, tunnel to Lucy |
| Bee Unit 2 | ⚠️ Locked out | SSH key-only, recovery artifact v5 pending test |
| MAP Token | ❌ Not minted | Tokenomics designed, wallets not created |
| DAO/Governance | ❌ Not deployed | Agora governor planned |
6.2 Known Issues
-
Unit 2 SSH lockout — Old liberate.sh hardened sshd_config before writing keys. Recovery artifact
adacam-ssh-fix-v5.menderis built and waiting for testing. -
Agent API disabled —
AGENT_API_LIVE = Falsebecause image upload pipeline isn't consistently flowing yet. Need Bee devices actively uploading images with detections. -
No clustering job — The
signstable is created by a clustering script that hasn't been deployed yet. Detections go in, but no deduplication/consensus runs. -
VPN dependency — Rackham API currently connects to Lucy's Postgres via VPN. If tunnel drops, API returns 503. Should migrate to Postgres running locally on Rackham for production.
6.3 API Endpoints Summary
Live:
GET /api/healthGET /api/statsGET /api/detectionsGET /api/signs(404 if clustering not run)POST /api/ingestPOST /api/imagesGET /api/images/<filename>GET /api/agent/infoGET /api/agent/leaderboard
Gated (returns "coming_soon"):
GET /api/agent/taskPOST /api/agent/submitGET /api/agent/stats(works but no data)
7. Sign Types & Classification
The system recognizes these sign types:
stop-sign, speed-limit, yield, one-way, no-parking,
crosswalk, school-zone, construction, street-name,
highway-sign, traffic-light, turn-restriction,
regulatory-sign, warning-sign, guide-sign,
not-a-sign, unknown
Consensus mechanism:
- 3+ agents must agree on a label
- Agreeing agents earn base + consensus bonus
- Disagreeing agents earn nothing
- If no consensus after 10 labels → human review queue
Rewards:
{
"base_label": 0.5,
"consensus_bonus": 1.0,
"new_type_discovery": 2.0,
"false_positive_id": 0.25
}
8. Appendix
8.1 Environment Variables
AdaMaps API (Rackham):
DB_HOST=192.168.254.112
API_KEY=adamaps-ingest-2026
FLASK_ENV=production
adacam-api (Bee device):
ADAMAPS_KEY=adamaps-ingest-2026
ADAMAPS_API=https://api.adamaps.org
8.2 Docker Compose (Rackham)
services:
adamaps-postgres:
image: postgis/postgis:16-3.4
container_name: adamaps-postgres
environment:
POSTGRES_DB: adamaps
POSTGRES_USER: adamaps
POSTGRES_PASSWORD: adamaps2026
volumes:
- adamaps-pgdata:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
adamaps-api:
build: ./api
container_name: adamaps-api
environment:
DB_URL: postgresql://adamaps:adamaps2026@adamaps-postgres:5432/adamaps
API_KEY: adamaps-ingest-2026
ports:
- "127.0.0.1:5001:5000"
volumes:
- /opt/adamaps/images:/images
8.3 Gitea Access
Base URL: http://192.168.0.5:3001/api/v1
Token: 33a9eb57b58c262f4434c12028bc3a30b1ff7021
Organization: Sulkta-Coop
Repos: adamaps, adacam, adacam-api
8.4 SSH Keys (authorized for Bee devices)
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK87jxvlXvo60pxwdtyJsXeFsb4KsAiFx4FnyXz81kh7 cobb@adacam
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOQxwJU91TCxds34P18D3xRbu7rxlrgTUoml/H8nxeDK kayos@openclaw
Document generated from Gitea repos: adamaps, adacam, adacam-api