add BEE_DATA_PIPELINE.md from research sprint

This commit is contained in:
kayos 2026-03-22 11:09:45 -07:00
parent cacdce1f34
commit b2e3a3a360

967
docs/BEE_DATA_PIPELINE.md Normal file
View file

@ -0,0 +1,967 @@
# Hivemapper Bee → ADAMaps Data Pipeline
> Complete data flow documentation for liberating Hivemapper Bee dashcams to ADAMaps
> Last Updated: 2026-03-22
---
## Executive Summary
The Hivemapper Bee is an Intel Keem Bay-based dashcam with on-device AI detection. This document maps the complete data pipeline from camera capture to ADAMaps storage, identifying interception points for data liberation.
**Key Insight**: Detections are stored in `/data/recording/landmarks/` files, NOT in SQLite. The SQLite database only contains sensor data (GPS, IMU, magnetometer).
---
## System Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│ HIVEMAPPER BEE HARDWARE │
│ │
│ ┌─────────────┐ ┌───────────────┐ ┌──────────────┐ │
│ │ OV10640 │ │ Intel Keem │ │ WiFi │ │
│ │ Camera │ ───▶ │ Bay VPU │ │ wlp1s0f0 │◀──▶ AP (192.168.0.10) │
│ │ 2028x1024 │ │ (Myriad X) │ │ wlp1s0f1 │◀──▶ Client (zerocool) │
│ └─────────────┘ └───────────────┘ └──────────────┘ │
│ │ │ │
│ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │ ARM │ │ LTE │ │
│ │ Cortex-A53 │ │ Modem │ │
│ │ (4 core) │ │ (Optional) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────────────┘
```
---
## 1. Camera Capture Pipeline
### Service: depthai_gate
```
Path: /usr/bin/depthai_gate
Config: systemd (no external config file)
Logs: journald
PID: 666 (typical process list)
CPU: ~1% idle, ~98% inference
```
**systemd Unit:**
```ini
[Unit]
Description=DepthAI Gate
StartLimitIntervalSec=600
StartLimitBurst=40
[Service]
ExecStart=/usr/bin/depthai_gate
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
```
### VPU Inference Architecture
```
┌──────────────────────────────────────────────────────────────────────────────┐
│ DEPTHAI_GATE SERVICE │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────────────────┐ │
│ │ Camera │ │ DepthAI │ │ Intel Keem Bay VPU │ │
│ │ OV10640 │────▶│ Pipeline │────▶│ (16 SHAVE cores via NCE) │ │
│ │ 2028x1024 │ │ Manager │ │ │ │
│ └─────────────┘ └──────────────┘ │ ┌─────────────────────┐ │ │
│ │ │ model.blob │ │ │
│ │ │ (OpenVINO compiled) │ │ │
│ │ │ ~6MB, YOLOv8-nano │ │ │
│ │ └─────────────────────┘ │ │
│ └─────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ OUTPUT STREAMS │ │
│ │ │ │
│ │ Raw Frames ─────────▶ /tmp/recording/pics/ │ │
│ │ Detection Boxes ────▶ Redis (via IPC) │ │
│ │ Encoded Video ──────▶ video-processor │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
```
### Frame Capture Output
```
Path: /tmp/recording/pics/
Format: {timestamp_ms}_{frame_id}_{sequence}.jpg
Example: 1744153672750_858894_001.jpg
└──────┬─────┘ └──┬──┘ └┬┘
unix_ms frame# seq
Retention: Managed by hivemapper-folder-purger (8GB limit)
Resolution: 2028x1024 @ 30fps
Encoding: JPEG (via VAAPI hardware encoder)
```
### VPU Firmware Location
```
/tmp/gate_fw_o2n73qwp/{hash}/
├── depthai-device-kb # VPU device binary (98% CPU during inference)
├── depthai-gate-stdout.log # Service output
└── resources/
└── depthai-vpu-fw-*.bin # VPU firmware blob
```
---
## 2. AI Detection Pipeline
### Service: map-ai
```
Path: /opt/map-ai/map-ai.sh (wrapper)
/opt/map-ai/map-ai.py (main Python script)
Config: Reads from Redis, SQLite config
Logs: /data/recording/map-ai.log
CPU: ~28% (Python inference post-processing)
```
**systemd Unit:**
```ini
[Unit]
Description=map-AI
Requires=odc-api.service # ← CRITICAL: This dependency must be removed
Requires=redis.service
[Service]
ExecStart=/opt/map-ai/map-ai.sh
ExecStartPost=-/usr/bin/redis-cli SET MAP_AI_READY "False"
ExecStopPost=-/usr/bin/redis-cli SET MAP_AI_READY "False"
Restart=always
RestartSec=5s
StandardOutput=append:/data/recording/map-ai.log
StandardError=append:/data/recording/map-ai.log
[Install]
WantedBy=multi-user.target
```
### Detection Pipeline Architecture
```
┌──────────────────────────────────────────────────────────────────────────────┐
│ MAP-AI SERVICE │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────┐ ┌─────────────────┐ ┌──────────────────┐ │
│ │ depthai_gate │───▶│ Frame Buffer │───▶│ LocateLandmark │ │
│ │ (VPU output) │ │ (Redis queue) │ │ Node │ │
│ └────────────────┘ └─────────────────┘ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ DETECTION PROCESSING │ │
│ │ │ │
│ │ Input: Raw detection boxes from VPU │ │
│ │ GPS/IMU data from Redis │ │
│ │ │ │
│ │ Process: │ │
│ │ 1. NMS (Non-Maximum Suppression) │ │
│ │ 2. Confidence thresholding (min 0.3) │ │
│ │ 3. GPS fusion (attach lat/lon to detections) │ │
│ │ 4. Temporal tracking (link same objects across frames) │ │
│ │ │ │
│ │ Output: Landmark observations with: │ │
│ │ - class_label (road_sign, lane_marking, etc.) │ │
│ │ - overall_confidence (0.0-1.0) │ │
│ │ - lat, lon (from GPS fusion) │ │
│ │ - timestamp (unix_ms) │ │
│ │ - bounding_box (x1, y1, x2, y2) │ │
│ │ - observation_images (cropped chips) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ StoreLandmarks │───────────────────▶│ /data/recording/ │ │
│ │ Node │ │ landmarks/ │ │
│ └────────┬─────────┘ └──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Redis (ephemeral)│◀─── Detection events published here │
│ └──────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
```
### Detected Object Classes
Based on the OpenVINO model (YOLOv8-nano variant):
| Class | Label | Description |
|-------|-------|-------------|
| 0 | `road_sign` | Traffic signs (stop, yield, speed limit, etc.) |
| 1 | `lane_marking` | Road lines, arrows, symbols |
| 2 | `traffic_light` | Traffic signal heads |
| 3 | `face` | Privacy detection (for blurring) |
| 4 | `license_plate` | Privacy detection (for blurring) |
| 5 | `road_marker` | Mile markers, reflectors |
| 6 | `construction` | Construction signs/barriers |
| 7 | `vegetation` | Overgrown vegetation blocking signs |
### Confidence Scores
```python
# Typical thresholds (from map-ai.py)
MIN_DETECTION_CONFIDENCE = 0.3 # Minimum to store
PRIVACY_BLUR_THRESHOLD = 0.5 # When to blur faces/plates
HIGH_CONFIDENCE = 0.8 # Likely true positive
```
---
## 3. Data Storage on Bee
### Directory Structure
```
/data/recording/
├── landmarks/ # Detection files (1.7MB observed)
│ ├── {timestamp}_*.json # Individual landmark observations
│ └── ...
├── framekm/ # Compressed video bundles
│ ├── {bundle}.tar.gz
│ └── ...
├── unprocessed_framekm/ # Pending processing (8GB limit)
├── metadata/ # GPS/IMU session data
├── ml_metadata/ # ML inference metadata (20MB limit)
├── gps/ # Raw GPS logs (200MB limit)
├── imu/ # Raw IMU logs (360MB limit)
├── raw/ # Raw sensor data (5MB limit)
├── cached_observations/ # Landmark observation images (cropped chips)
├── plugin_cache/ # Plugin data (500MB limit)
├── odc-api.log # Node.js API logs
├── map-ai.log # Python ML logs
├── video-processor.log # Video encoding logs
└── redis-handler.log # Sensor fusion logs
```
### Landmark File Format
```
Location: /data/recording/landmarks/
Pattern: {timestamp_ms}_{observation_id}.json
Example File Content:
{
"id": 2945056,
"class_label": "road_sign",
"overall_confidence": 0.847,
"lat": 33.841234,
"lon": -118.391234,
"timestamp": 1746377552043,
"bounding_box": {
"x1": 1234,
"y1": 456,
"x2": 1456,
"y2": 678
},
"image_path": "/data/recording/cached_observations/1746377552_043000_2945056.jpg",
"speed_mph": 35.2,
"heading_deg": 127.4,
"hdop": 1.2,
"num_satellites": 12
}
```
### FrameKM Bundle Format
```
/data/recording/framekm/{bundle_name}.tar.gz
├── frames/
│ ├── frame_001.jpg
│ ├── frame_002.jpg
│ └── ...
├── metadata.json # Bundle metadata
├── gnss.json # GPS track for bundle
└── imu.json # IMU data for bundle
```
### SQLite Schema: odc-api.db
```sql
-- Location: /data/odc-api.db
-- GPS/GNSS data (30Hz)
CREATE TABLE gnss (
rowid INTEGER PRIMARY KEY,
lat_deg REAL,
lon_deg REAL,
alt_m REAL,
speed_mps REAL,
heading_deg REAL,
hdop REAL,
num_satellites INTEGER,
fix_type INTEGER,
unix_ms INTEGER
);
-- GPS authentication data
CREATE TABLE gnss_auth (
rowid INTEGER PRIMARY KEY,
auth_data BLOB,
unix_ms INTEGER
);
-- IMU data (10Hz)
CREATE TABLE imu (
rowid INTEGER PRIMARY KEY,
accel_x REAL,
accel_y REAL,
accel_z REAL,
gyro_x REAL,
gyro_y REAL,
gyro_z REAL,
unix_ms INTEGER
);
-- Magnetometer data
CREATE TABLE magnetometer (
rowid INTEGER PRIMARY KEY,
mag_x REAL,
mag_y REAL,
mag_z REAL,
unix_ms INTEGER
);
-- FrameKM metadata
CREATE TABLE framekms (
rowid INTEGER PRIMARY KEY,
name TEXT,
start_ms INTEGER,
end_ms INTEGER,
frame_count INTEGER,
size_bytes INTEGER,
uploaded INTEGER DEFAULT 0
);
-- Configuration
CREATE TABLE config (
key TEXT PRIMARY KEY,
value TEXT
);
-- Service state
CREATE TABLE state (
service TEXT PRIMARY KEY,
healthy INTEGER,
last_update INTEGER
);
-- NOTE: NO landmarks/detections table!
-- Detections are stored in files, not SQLite
```
### Redis Keys and Purposes
```
┌─────────────────────────┬─────────┬──────────────────────────────────────────┐
│ Key │ Type │ Purpose │
├─────────────────────────┼─────────┼──────────────────────────────────────────┤
│ MAP_AI_READY │ string │ "True" when inference active │
│ GNSSFusion30Hz │ zset │ 30Hz GPS: lat/lon/alt/speed/hdop │
│ ImuFusion10Hz │ zset │ 10Hz IMU: accel/gyro vectors │
│ MagnetometerData │ list │ Magnetometer readings │
│ GnssFreqHz │ string │ Current GPS update frequency │
│ LTE_INIT_IMEISV │ string │ LTE modem IMEISV │
│ LTE_INIT_IMEI │ string │ LTE modem IMEI │
│ REDIS_HANDLER_SESSION_ID│ string │ Current session identifier │
└─────────────────────────┴─────────┴──────────────────────────────────────────┘
```
**GNSSFusion30Hz Entry Format:**
```json
{
"lat_deg": 33.841234,
"lon_deg": -118.391234,
"alt_m": 45.2,
"speed_mps": 15.7,
"heading_deg": 127.4,
"hdop": 1.2,
"num_satellites": 12,
"unix_milliseconds": 1746377552043
}
```
---
## 4. odc-api Service
### Service Details
```
Path: /opt/odc-api/odc-api-bee.js
Runtime: Node.js
Port: 5000
Logs: /data/recording/odc-api.log
CPU: ~14% (high for a REST API)
Memory: ~144MB RSS
```
**systemd Unit:**
```ini
[Unit]
Description=ODC-API
[Service]
ExecStart=node /opt/odc-api/odc-api-bee.js
Restart=always
RestartSec=5s
StandardOutput=append:/data/recording/odc-api.log
StandardError=append:/data/recording/odc-api.log
Environment="ODC_VERSION=5.7.88"
[Install]
WantedBy=multi-user.target
```
### REST API Architecture
```
┌──────────────────────────────────────────────────────────────────────────────┐
│ ODC-API SERVICE (Port 5000) │
├──────────────────────────────────────────────────────────────────────────────┤
│ │
│ External Request │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ EXPRESS.JS ROUTER │ │
│ │ │ │
│ │ /api/1/config/* → Config management │ │
│ │ /api/1/db/* → SQLite queries │ │
│ │ /api/1/gps/* → GPS data access │ │
│ │ /api/1/recordings/* → Frame access │ │
│ │ /api/1/framekm/* → FrameKM bundles │ │
│ │ /api/1/cache/* → Data caching │ │
│ │ /api/landmarks/* → Landmark detection access │ │
│ │ /api/1/upload/* → Model/plugin upload │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Redis │ │ SQLite │ │ Filesystem │ │
│ │ Client │ │ odc-api.db │ │ /data/... │ │
│ └──────────┘ └──────────────┘ └──────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
```
### Landmark API Endpoints
```http
# Get N most recent landmarks
GET http://192.168.0.10:5000/api/landmarks/n/{count}
→ Returns: Array of landmark objects
# Get landmarks since ID
GET http://192.168.0.10:5000/api/landmarks/from/{id}
→ Returns: Landmarks with id > {id}
# Get landmark images
GET http://192.168.0.10:5000/landmarks/images/{landmark_id}
→ Returns: Array of image file paths
# Get landmark bounding box
GET http://192.168.0.10:5000/landmarks/boundingBox/{landmark_id}
→ Returns: {boundingBox: {x1, y1, x2, y2}, chip_id}
```
### Data Flow: How odc-api Reads Landmarks
```
Request: GET /api/landmarks/n/100
┌─────────────────────────────────────────────────┐
│ odc-api reads from: │
│ │
│ 1. /data/recording/landmarks/*.json │
│ (Primary source - file-based) │
│ │
│ 2. Redis ephemeral cache │
│ (For real-time streaming) │
│ │
│ 3. In-memory LRU cache │
│ (For repeated queries) │
└─────────────────────────────────────────────────┘
JSON Response to Client
```
---
## 5. Data Egress (Liberation)
### Current Hivemapper Upload Path (TO BLOCK)
```
┌───────────────────────────────────────────────────────────────────────────────┐
│ HIVEMAPPER DATA EXFILTRATION │
│ │
│ ┌───────────┐ ┌──────────────┐ ┌─────────────────────────────────┐ │
│ │ map-ai │────▶│ odc-api │────▶│ mitmproxy (localhost:8888) │ │
│ │ │ │ (port 5000) │ │ rewrite_to_cloudflare.py │ │
│ └───────────┘ └──────────────┘ └─────────────┬───────────────────┘ │
│ │ │
│ ┌───────────┐ ▼ │
│ │hivemapper │ ┌──────────────┐ ┌─────────────────────────────────┐ │
│ │-data- │────▶│ beekeeper- │────▶│ HIVEMAPPER CLOUD │ │
│ │ logger │ │ plugin │ │ api.hivemapper.com │ │
│ └───────────┘ └──────────────┘ │ beemaps.com │ │
│ │ s3.hivemapper.com │ │
│ └─────────────────────────────────┘ │
│ │
│ Services to KILL: │
│ - odc-api.service (Node.js API - can replace with file read) │
│ - mitmproxy.service (Proxy to Cloudflare) │
│ - beekeeper-plugin.service (Hivemapper control plane) │
│ - hivemapper-data-logger (Binary data uploader) │
│ - mender-client.service (OTA updates - blocks liberation) │
│ - here-plugin.service (HERE Maps integration) │
│ - model-zoo.service (Model updates from Hivemapper) │
│ │
│ Domains to BLOCK in /etc/hosts: │
│ - hivemapper.com, api.hivemapper.com │
│ - beemaps.com, api.trybeekeeper.ai │
│ - docker.mender.io, s3.mender.io │
│ - direct.data.api.platform.here.com │
│ - dashcam-firmware.s3.us-west-2.amazonaws.com │
│ - cfapi.cloudflare.com │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
```
### New ADAMaps Upload Path (TO CREATE)
```
┌───────────────────────────────────────────────────────────────────────────────┐
│ ADAMAPS DATA LIBERATION │
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ SERVICES TO KEEP │ │
│ │ │ │
│ │ depthai_gate.service - Camera capture & VPU inference │ │
│ │ map-ai.service - Detection processing (mod service file) │ │
│ │ redis.service - Sensor data store │ │
│ │ redis-handler.service - Sensor fusion │ │
│ │ jpeg-recorder.service - (optional) Raw frame capture │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ ADACAM SERVICES (NEW) │ │
│ │ │ │
│ │ ┌─────────────────────────┐ ┌────────────────────────────────┐ │ │
│ │ │ adacam-forwarder.service│ │ bee-agent-api.service │ │ │
│ │ │ │ │ │ │ │
│ │ │ Reads: │ │ Provides: │ │ │
│ │ │ - /data/recording/ │ │ - /status (health check) │ │ │
│ │ │ landmarks/*.json │ │ - /shell (remote commands) │ │ │
│ │ │ - Redis GNSSFusion30Hz │ │ - /landmarks (detection query) │ │ │
│ │ │ │ │ - /redis (key inspection) │ │ │
│ │ │ Writes: │ │ │ │ │
│ │ │ - POST to ADAMaps │ │ Port: 8080 │ │ │
│ │ │ /api/ingest │ │ Auth: X-Agent-Key header │ │ │
│ │ └────────────┬────────────┘ └────────────────────────────────┘ │ │
│ │ │ │ │
│ └───────────────│──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ BEE-TUNNEL SERVICE │ │
│ │ │ │
│ │ SSH reverse tunnel: Bee → Lucy → OpenClaw/ADAMaps │ │
│ │ │ │
│ │ ExecStart=/usr/bin/ssh -i /data/ssh/bee_tunnel_key │ │
│ │ -R 2222:127.0.0.1:22 │ │
│ │ -N -o ServerAliveInterval=30 │ │
│ │ -o StrictHostKeyChecking=no │ │
│ │ root@192.168.0.5 │ │
│ │ │ │
│ │ NOTE: Must bind to 127.0.0.1:22 NOT 192.168.0.10:22 │ │
│ │ (Hairpin AP routing breaks 192.168.0.10 target) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ ADAMAPS INGEST (Cloud/Self-Hosted) │ │
│ │ │ │
│ │ Endpoint: https://api.adamaps.org/api/ingest │ │
│ │ Auth: X-AdaMaps-Key: adamaps-ingest-2026 │ │
│ │ Method: POST │ │
│ │ │ │
│ │ Payload: │ │
│ │ { │ │
│ │ "device_id": "dashcam-4A928016A02C1046", │ │
│ │ "detections": [ │ │
│ │ { │ │
│ │ "ts": 1746377552043, │ │
│ │ "lat": 33.841234, │ │
│ │ "lon": -118.391234, │ │
│ │ "class_label": "road_sign", │ │
│ │ "overall_confidence": 0.847 │ │
│ │ } │ │
│ │ ] │ │
│ │ } │ │
│ │ │ │
│ │ Image Upload (separate): │ │
│ │ POST /api/images │ │
│ │ Content-Type: multipart/form-data │ │
│ │ Fields: detection_id, device_id, image (file) │ │
│ └──────────────────────────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────────────┘
```
### adacam-forwarder Service Design
```python
# Key Design Points:
1. READ FROM FILES DIRECTLY (not odc-api REST):
- Path: /data/recording/landmarks/*.json
- Avoids dependency on odc-api.service
2. STATE TRACKING:
- /data/adacam/forwarder_state.json
- Track last_landmark_id to avoid re-sending
3. RETRY QUEUE:
- /data/adacam/forward_queue.db (SQLite)
- Failed POSTs queued for retry
4. GPS FALLBACK:
- If landmark missing GPS, query Redis GNSSFusion30Hz
5. BATCH POSTING:
- Max 200 detections per POST
- Respects rate limits (429 → exponential backoff)
```
### bee-tunnel.service Design
```ini
[Unit]
Description=SSH Tunnel to Lucy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
# CRITICAL: Must add host route before tunnel
ExecStartPre=/sbin/ip route add 192.168.0.5/32 dev wlp1s0f1
ExecStart=/usr/bin/ssh -i /data/ssh/bee_tunnel_key \
-R 2222:127.0.0.1:22 \
-N \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-o StrictHostKeyChecking=no \
-o ExitOnForwardFailure=yes \
root@192.168.0.5
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
```
**Why 127.0.0.1:22?**
- sshd.socket listens on all interfaces (including 127.0.0.1)
- Using 192.168.0.10:22 causes hairpin routing through the AP radio
- Packets destined for own AP IP don't return correctly
- Results in "Error reading SSH protocol banner"
---
## 6. ADAMaps Ingest
### Expected Payload Format
```json
POST /api/ingest
Content-Type: application/json
X-AdaMaps-Key: adamaps-ingest-2026
{
"device_id": "dashcam-4A928016A02C1046",
"detections": [
{
"ts": 1746377552043,
"lat": 33.841234,
"lon": -118.391234,
"class_label": "road_sign",
"overall_confidence": 0.847,
"heading_deg": 127.4,
"speed_mph": 35.2
},
{
"ts": 1746377553123,
"lat": 33.841456,
"lon": -118.391567,
"class_label": "lane_marking",
"overall_confidence": 0.923
}
]
}
```
**Response:**
```json
{
"ok": true,
"accepted": 2,
"detection_ids": [12345, 12346]
}
```
### Image Upload Flow
```
Step 1: POST detections → receive detection_ids
Step 2: For each detection with image:
POST /api/images
multipart/form-data:
- detection_id: 12345
- device_id: dashcam-4A928016A02C1046
- image: <binary JPEG>
```
**Response:**
```json
{
"ok": true,
"image_id": "img_abc123",
"url": "https://cdn.adamaps.org/observations/img_abc123.jpg"
}
```
### Device Registration
```json
POST /api/devices/register
Content-Type: application/json
X-AdaMaps-Key: adamaps-ingest-2026
{
"device_id": "dashcam-4A928016A02C1046",
"device_type": "hivemapper_bee",
"firmware_version": "5.7.88",
"owner_id": "user_cobb",
"location_hint": "Redondo Beach, CA"
}
```
**Response:**
```json
{
"ok": true,
"device_id": "dashcam-4A928016A02C1046",
"api_key": "device-specific-key-here",
"registered_at": "2026-03-22T18:00:00Z"
}
```
---
## Complete Data Flow Diagram
```
┌─────────────────────────────────────────────────────────────────────────────────────────────────┐
│ COMPLETE DATA PIPELINE │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ╔═══════════════════════════════════════════════════════════════════════════════════════════╗ │
│ ║ HIVEMAPPER BEE ║ │
│ ╠═══════════════════════════════════════════════════════════════════════════════════════════╣ │
│ ║ ║ │
│ ║ CAPTURE LAYER ║ │
│ ║ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────────┐ ║ │
│ ║ │ Camera │──▶│ depthai_ │──▶│ VPU Blob │──▶│ Raw Frames │ ║ │
│ ║ │ OV10640 │ │ gate │ │ Inference │ │ + Detections │ ║ │
│ ║ │ 2028x1024 │ │ │ │ YOLOv8-nano │ │ │ ║ │
│ ║ └─────────────┘ └──────────────┘ └──────────────┘ └───────┬────────┘ ║ │
│ ║ │ ║ │
│ ║ ─────────────────────────────────────────────────────────────────┼─────────────────────── ║ │
│ ║ ▼ ║ │
│ ║ PROCESSING LAYER ║ │
│ ║ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ║ │
│ ║ │ GPS │──▶│ Redis │──▶│ map-ai │ ║ │
│ ║ │ (u-blox) │ │ Fusion │ │ Python │ ║ │
│ ║ └──────────────┘ └──────────────┘ │ │ ║ │
│ ║ │ Nodes: │ ║ │
│ ║ ┌──────────────┐ ┌──────────────┐ │ -Locate │ ║ │
│ ║ │ IMU │──▶│ Redis │──▶│ -Store │ ║ │
│ ║ │ (SPI) │ │ Handler │ │ -Privacy │ ║ │
│ ║ └──────────────┘ └──────────────┘ └──────┬───────┘ ║ │
│ ║ │ ║ │
│ ║ ─────────────────────────────────────────────┼────────────────────────────────────────── ║ │
│ ║ ▼ ║ │
│ ║ STORAGE LAYER ║ │
│ ║ ┌───────────────────────────────────────────────────────────────────────────────────┐ ║ │
│ ║ │ /data/recording/ │ ║ │
│ ║ │ │ ║ │
│ ║ │ landmarks/*.json ◀── Detection files (CLASS, CONF, LAT, LON, TS) │ ║ │
│ ║ │ cached_observations/ ◀── Cropped detection images │ ║ │
│ ║ │ framekm/*.tar.gz ◀── Video bundles with metadata │ ║ │
│ ║ │ │ ║ │
│ ║ │ odc-api.db (SQLite) ◀── gnss, imu, magnetometer, config, state │ ║ │
│ ║ │ (NO detections in SQLite!) │ ║ │
│ ║ └───────────────────────────────────────────────────────────────────────────────────┘ ║ │
│ ║ │ ║ │
│ ║ │ ║ │
│ ╠════════════════════════════════════════════════╪═══════════════════════════════════════════╣ │
│ ║ EGRESS LAYER │ ║ │
│ ║ ▼ ║ │
│ ║ ┌─────────────────────────────────────────────────────────────┐ ║ │
│ ║ │ │ ║ │
│ ║ │ ┌─────────────────┐ ┌─────────────────┐ │ ║ │
│ ║ │ │ BLOCK: │ │ KEEP: │ │ ║ │
│ ║ │ │ odc-api │ │ adacam-forwarder│───────────────┐ ║ │
│ ║ │ │ mitmproxy │ │ │ │ │ ║ │
│ ║ │ │ beekeeper-plugin│ │ Reads files │ │ │ ║ │
│ ║ │ │ mender-client │ │ directly, no │ │ │ ║ │
│ ║ │ │ hivemapper- │ │ odc-api dep │ │ │ ║ │
│ ║ │ │ data-logger │ │ │ │ │ ║ │
│ ║ │ └─────────────────┘ └─────────────────┘ │ │ ║ │
│ ║ │ │ │ ║ │
│ ║ └─────────────────────────────────────────────────────────────┘ │ ║ │
│ ║ │ ║ │
│ ╚═══════════════════════════════════════════════════════════════════════════════╪════════════╝ │
│ │ │
│ ┌───────────────────────────────────────────────────────────────────────────────┼────────────┐ │
│ │ │ │ │
│ │ bee-tunnel.service │ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │ │
│ │ │ SSH -R 2222:127.0.0.1:22 root@192.168.0.5 (Lucy) │ │ │ │
│ │ │ (WiFi wlp1s0f1 → zerocool network) │ │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ └───────────────────────────────────│───────────────────────────────────────────│────────────┘ │
│ │ │ │
│ ▼ │ │
│ ╔═══════════════════════════════════════════════════════════════════════════════╪════════════╗ │
│ ║ LUCY (192.168.0.5) │ ║ │
│ ║ ┌────────────────────────────────────────────────────────────────────────────┼──────────┐║ │
│ ║ │ Tunnel endpoint: localhost:2222 │ │║ │
│ ║ │ → ssh -p 2222 root@localhost # Access Bee shell │ │║ │
│ ║ │ → curl localhost:2222/status # (if HTTP tunnel) │ │║ │
│ ║ └────────────────────────────────────────────────────────────────────────────┼──────────┘║ │
│ ╚═══════════════════════════════════════════════════════════════════════════════╪════════════╝ │
│ │ │
│ │ │
│ │ │
│ ╔═══════════════════════════════════════════════════════════════════════════════╪════════════╗ │
│ ║ ADAMAPS INGEST │ ║ │
│ ║ │ ║ │
│ ║ ┌────────────────────────────────────────────────────────────────────────────┼──────────┐║ │
│ ║ │ POST /api/ingest │ │║ │
│ ║ │ Header: X-AdaMaps-Key: adamaps-ingest-2026 │ │║ │
│ ║ │ │ │║ │
│ ║ │ { ◀──────────┘║ │
│ ║ │ "device_id": "dashcam-4A928016A02C1046", ║ │
│ ║ │ "detections": [ ║ │
│ ║ │ {"ts": ..., "lat": ..., "lon": ..., "class_label": ..., "confidence": ...} ║ │
│ ║ │ ] ║ │
│ ║ │ } ║ │
│ ║ │ ║ │
│ ║ │ POST /api/images (multipart: detection_id, device_id, image) ║ │
│ ║ └───────────────────────────────────────────────────────────────────────────────────────┘║ │
│ ║ ║ │
│ ╚═══════════════════════════════════════════════════════════════════════════════════════════╝ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────────┘
```
---
## Quick Reference: Service States
### Services to KEEP (Liberation)
| Service | Purpose | Notes |
|---------|---------|-------|
| `depthai_gate` | Camera capture + VPU inference | Core functionality |
| `map-ai` | Detection processing | Remove odc-api dependency |
| `redis` | Sensor data store | Required by map-ai |
| `redis-handler` | Sensor fusion | Required by datalogger |
| `hostapd` | WiFi AP | 192.168.0.10 access |
| `sshd` | SSH access | Remote management |
### Services to KILL (Liberation)
| Service | Purpose | Why Kill |
|---------|---------|----------|
| `odc-api` | REST API | Bloated, can read files directly |
| `mitmproxy` | Proxy to Cloudflare | Hivemapper data exfil |
| `beekeeper-plugin` | Hivemapper control | Phone home |
| `hivemapper-data-logger` | Data uploader | Exfiltration |
| `mender-client` | OTA updates | Blocks liberation |
| `here-plugin` | HERE Maps | Unnecessary |
| `model-zoo` | Model updates | Hivemapper models |
| `collectd` | Metrics | Unnecessary |
| `cpu-mem-logger` | Logging | Unnecessary |
| `vnstat/vnstatd` | Network stats | Unnecessary |
| `rm_vpu_daemon` | VPU daemon | Unknown purpose |
### New Services to INSTALL (ADAMaps)
| Service | Purpose | Location |
|---------|---------|----------|
| `adacam-forwarder` | Detection forwarding | /data/adacam/ |
| `bee-agent-api` | Remote management API | /data/adacam/ |
| `bee-tunnel` | SSH reverse tunnel | systemd |
---
## Appendix: File Locations Reference
```
PERSISTENT (/data/ survives OTA):
├── /data/adacam/ # ADAMaps config/state
│ ├── config.json # Device config
│ ├── forwarder_state.json # Forwarding state
│ ├── forward_queue.db # Retry queue
│ └── agent-config.json # Agent API config
├── /data/recording/ # All recording data
│ ├── landmarks/ # Detection JSON files ← KEY
│ ├── cached_observations/ # Detection images ← KEY
│ ├── framekm/ # Video bundles
│ ├── odc-api.db # SQLite (sensors only)
│ └── *.log # Service logs
├── /data/ssh/ # SSH keys
│ └── bee_tunnel_key # Tunnel key
└── /data/odc-api.db # SQLite database
VOLATILE (/tmp/, cleared on reboot):
├── /tmp/recording/pics/ # Live frames
└── /tmp/gate_fw_*/ # VPU firmware
READ-ONLY (/opt/, /usr/):
├── /opt/map-ai/ # Detection Python code
├── /opt/odc-api/ # Node.js API (TO KILL)
├── /opt/dashcam/bin/ # Binary tools
└── /usr/bin/depthai_gate # Camera binary
```
---
*Document generated for Sulkta-Coop/adacam liberation project*
*Source: bee-recon data, odc-api analysis, firmware extraction*