43 KiB
adacam Project State — Definitive Reference
Last updated: 2026-03-30 (Gitea audit pass — Opus)
Written by: Opus audit sub-agent (deep audit of all sources + Gitea audit 2026-03-30)
Status: ⚠️ Camera in truck, AP rejecting connections, Pi at home — access blocked
HARD RULE: Read this file before ANY adacam work. Two devices have been broken by operating without full context.
1. Project Overview
Goal: Repurpose a Hivemapper Bee dashcam to collect mapping data for ADAMaps instead of Hivemapper. The device captures road sign detections (ML inference via VPU) with GPS coordinates and forwards them to https://api.adamaps.org.
Current state (as of 2026-03-30 ~09:52 PDT):
- Truck Bee (Device 1): Camera operational, services running, but WiFi routing conflict prevents internet access → ADAMaps forwarding broken. Camera currently in truck, AP rejecting WiFi connections (hostapd broken — likely caused by wifi-client.service).
- Brick Bee (Device 2): SSH locked out. Hardware alive. No active recovery in progress.
2. Device Inventory
Device 1 — Truck Bee (ACTIVE)
| Field | Value |
|---|---|
| SSID | dashcam-4A928016A02C1046 |
| Serial | 2P007435 |
| Assembly UUID | 3a43e0b9-ea21-5d94-bb45-92bdac8cde94 |
| Firmware | 20260309193836 (March 9, 2026 build) |
| Anonymous ID | fvhL2I-iCT |
| IMEI | 351369652125828 |
| SIM ICCID | 89148000010586449753 |
| WiFi AP IP | 192.168.0.10 |
| WiFi Client IP | 192.168.0.155 (zerocool) |
| USB Bridge IP | 192.168.197.55/28 (br0, down) |
| SSH | root@192.168.0.10 (passwordless, key-based) |
Hardware:
- SoC: Intel Keembay KMB (ARM64 Cortex-A53, 4 cores @ 700MHz)
- RAM: 3.5GB total
- eMMC: Toshiba DG4064 64GB (A/B partition scheme: system_a/system_b)
- LTE Modem: Telit LE910C4-NF (no SIM inserted)
- WiFi: Dual-band dual-interface (wlp1s0f0 = AP, wlp1s0f1 = client)
- Camera: Sony IMX412 stereo pair
- GPS: u-blox NEO-M9N @
/dev/ttyS2 - IMU:
/dev/spidev0.0
OS:
meta-intel-ese Reference Distro 2.0-dunfell (Yocto), Linux 5.4.86-intel-lts-km, Python 3.8, aarch64
Device 2 — Brick Bee (LOCKED OUT)
| Field | Value |
|---|---|
| SSID | dashcam-81B2B81681545109 |
| Serial | 2P021849 (from pre-bricking security audit; unconfirmed) |
| IMEI | 351369652127410 (from security audit; unconfirmed) |
| Status | SSH locked out — key-auth only, no authorized_keys |
| AP | Broadcasting, WiFi connects, SSH fails |
| Physical location | Unknown (was at home during recovery attempts in March 2026) |
How it got bricked:
Liberation script ran PasswordAuthentication no in /data/overlay/current/ssh/sshd_config BEFORE placing SSH keys. Result: requires key auth, no keys authorized, locked out.
Why Mender can't fix it:
Mender only swaps rootfs A/B partitions. The /data partition is NEVER touched. The bad sshd_config lives in /data/overlay/current/ssh/sshd_config and survives ALL firmware flashes. v1-v8 recovery artifacts were tried — none fixed SSH because overlay persists.
Recovery path:
- Ethernet USB tether (same procedure as Device 1, section 7)
- UART serial console — physical access, CP2102/CH340 USB-UART adapter. UART pad locations on Hivemapper Bee PCB are NOT yet documented. Needs teardown research.
- If ethernet gets in: add SSH key to
~/.ssh/authorized_keys, verify, done.
3. Architecture & Service Map
3.1 Full Detection Pipeline
[u-blox GPS /dev/ttyS2] + [IMU /dev/spidev0.0]
↓
[hivemapper-data-logger] (Go binary: /opt/dashcam/bin/datalogger)
↓
[Redis :6379] (GNSSFusion30Hz sorted set, ImuFusion10Hz)
↓
[redis-handler] (C++ binary: /opt/dashcam/bin/RedisHandler)
↓ writes to: ↓ reads from:
[/data/recording/redis_handler/ [map-ai.py / depthai_gate]
fusion-v3-0-0.db, ↓
sensors-v3-0-0.db] [ObjectDetection YOLO v8 on VPU]
↓
[LocateLandmarkNode] ← reads GNSSFusion30Hz
↓
[StoreLandmarksNode]
↓
[/data/recording/odc-api.db] (SQLite, landmarks table)
↓
[adacam-odc.py] (Thread 2: Forwarder)
↓
[https://api.adamaps.org/api/ingest]
[https://api.adamaps.org/api/images]
Critical dependency: If hivemapper-data-logger is not running, Redis has no GPS data, LocateLandmarkNode has nothing to work with, and StoreLandmarksNode writes nothing to SQLite. No GPS = no detections. Do NOT disable this service.
3.2 Service Status Table
| Service | Status | Notes |
|---|---|---|
hivemapper-data-logger.service |
✅ Should be RUNNING | GPS/IMU backbone. Was wrongly disabled ~Mar 24; re-enabled Mar 29. DO NOT DISABLE. |
map-ai.service |
✅ Running | ML pipeline, writes detections to SQLite |
adacam-odc.service |
✅ Running | Flask API on port 5500 + ADAMaps forwarder thread |
depthai_gate.service |
✅ Running | DepthAI ML inference gateway, port 11492 |
redis.service |
✅ Running | Redis state store :6379 |
redis-handler.service |
✅ Running | Sensor fusion → SQLite |
hostapd.service |
⚠️ Running but BROKEN | AP broadcasting but rejecting connections — likely wifi-client.service conflict |
here-plugin.service |
✅ Running | HERE plugin — API endpoints blocked via /etc/hosts |
lte.service |
✅ Running | LTE modem controller (no SIM) |
video-processor.service |
✅ Running | Video encoding |
odc-api.service |
✅ Dead / disabled | Node.js Hivemapper service — NOT needed |
mender.service |
✅ Masked | OTA updates blocked |
bee-tunnel.service |
✅ Removed | Was reverse SSH to Lucy — removed 2026-03-29 |
bee-collector.service |
✅ Removed | Old mapnet prototype — removed 2026-03-29 |
adamaps-persist.service |
❌ Fails at boot | Timing issue: runs before RTC sync (1970 timestamp). Non-critical now that bee-tunnel/collector removed. |
beekeeper-plugin.service |
✅ Dead/disabled | Hivemapper phone-home — disabled |
wifi-client.service |
⚠️ UNKNOWN — LIKELY PRESENT | Created 2026-03-29 (should not have been). Likely causing hostapd failure. Must be removed. |
4. Persistence Mechanism
File: /data/persist/install.sh
Service: adamaps-persist.service (runs install.sh at boot)
Why it works: The /data partition survives RAUC/Mender OTA firmware updates. Everything in /data/ persists across reboots and firmware changes.
Current install.sh does:
- Installs SSH key for remote access:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5ckRf/4SA84JOrmJtElHBT3dU9RC2Le5GBfqhWWVc8 root@keembay - Masks Mender OTA (
systemctl stop/disable/mask mender) - Blocks Mender network access via iptables
- Blocks Hivemapper/HERE APIs via /etc/hosts
NOTE: As of 2026-03-30, install.sh was updated (2026-03-29 session) to remove bee-tunnel and bee-collector references. But the exact current content is not recorded in any workspace file. The service still fails at boot (RTC timing issue), so it may not be executing reliably.
PENDING (not yet done): Add WiFi routing fix to install.sh:
ip route add 192.168.0.1/32 dev wlp1s0f1 2>/dev/null
ip rule add from 192.168.0.155 lookup 100 priority 100 2>/dev/null
ip route add default via 192.168.0.1 dev wlp1s0f1 table 100 2>/dev/null
ip route add 192.168.0.0/24 dev wlp1s0f1 table 100 2>/dev/null
5. Network Topology
Camera Network Interfaces
| Interface | IP | Purpose |
|---|---|---|
wlp1s0f0 |
192.168.0.10/24 |
WiFi AP (hostapd) — phone and Pi connect here |
wlp1s0f1 |
192.168.0.155/24 |
WiFi client (home network, "zerocool") |
br0 |
192.168.197.55/28 |
USB tethering bridge (down when no phone) |
wwan0 |
(none) | LTE modem (no SIM) |
The routing conflict (CRITICAL UNRESOLVED ISSUE):
Both wlp1s0f0 and wlp1s0f1 are on 192.168.0.0/24. When the camera tries to reach the internet via wlp1s0f1 (connected to home router), outbound traffic is correct but return traffic gets confused between the two interfaces. Camera has no internet access. This blocks:
- ADAMaps detection forwarding
- DNS resolution (systemd-resolved broken)
- Any cloud connectivity
Fix (runtime, must also be persisted):
ip route add 192.168.0.1/32 dev wlp1s0f1 2>/dev/null
ip rule add from 192.168.0.155 lookup 100 priority 100 2>/dev/null
ip route add default via 192.168.0.1 dev wlp1s0f1 table 100 2>/dev/null
ip route add 192.168.0.0/24 dev wlp1s0f1 table 100 2>/dev/null
Long-term fix (decision needed): Move AP off 192.168.0.0/24. Candidates: 172.20.0.1/24 or 172.16.100.1/24. (Note: 10.77.0.0/24 is blocked on Android.)
Home Network
| Device | IP | Role |
|---|---|---|
| OPNsense | 192.168.0.1 |
Router/DHCP |
| Lucy (Unraid) | 192.168.0.5 |
Server |
| Pi (blackbox) | 192.168.0.184 |
Access bridge (cobb@192.168.0.184) |
| Camera AP | 192.168.0.10 |
SSH entry point |
| Camera Client | 192.168.0.155 |
Not directly reachable from home LAN |
Access Paths
Normal (Pi at home, camera in driveway):
ssh cobb@192.168.0.184
sudo nmcli connection up 'dashcam-hivemapper' # connect Pi to dashcam AP
sudo ip route add 192.168.0.10/32 dev wlan0 # route camera AP
ssh root@192.168.0.10 # SSH to camera
Pi static IP workaround (if camera not handing out DHCP):
sudo nmcli connection modify 'dashcam-4A928016A02C1046' ipv4.method manual ipv4.addresses 192.168.0.200/24 ipv4.gateway 192.168.0.10
sudo nmcli connection up 'dashcam-4A928016A02C1046'
ssh root@192.168.0.10 # camera still at .10
Pi nmcli profile names:
- zerocool: UUID
c82d926c-5622-42f0-9bfa-6025f4cc5301 - dashcam-hivemapper (working profile): SSID
dashcam-4A928016A02C1046, 5GHz ch36, static 192.168.0.11/24 - dashcam-4A928016A02C1046: UUID
9c30aaa1-a9a0-4a5f-9d87-751e6f21ebcb(may be duplicate)
NEVER use wpa_supplicant directly on Pi — NetworkManager owns wlan0. Use nmcli only. Direct wpa_supplicant broke the brcmfmac driver (2026-03-30 incident).
Blocked Endpoints (via /etc/hosts)
0.0.0.0 hivemapper.com
0.0.0.0 api.hivemapper.com
0.0.0.0 device.api.hivemapper.com
0.0.0.0 account.api.here.com
0.0.0.0 direct.data.api.platform.here.com
0.0.0.0 api-lookup.data.api.platform.here.com
0.0.0.0 edge.hereapi.com
0.0.0.0 docker.mender.io
0.0.0.0 hosted.mender.io
0.0.0.0 downloads.mender.io
6. adacam-odc Service Details
File: /data/adacam/adacam_odc.py
Port: 5500 (not 5000 — master audit was wrong)
Service: adacam-odc.service
Config: /data/adacam/config.json
Forwarder state: /data/adacam/forwarder_state.json
Forwarder log: /data/adacam/forwarder.log
Architecture: Two-thread service
- Thread 1: Flask API (health, WiFi management, USB tethering, live preview)
- Thread 2: ADAMaps Forwarder (polls landmarks table, batches, POSTs to api.adamaps.org)
Key endpoints:
GET /api/1/health→{"status":"ok","version":"adacam-odc-2.0.0"}GET /api/1/forwarder/statusGET /api/1/ml/status- WiFi/tethering management endpoints
ADAMaps config (in /data/adacam/config.json):
{
"device_id": "dashcam-unknown",
"adamaps_api": "https://api.adamaps.org",
"adamaps_key": "adamaps-ingest-2026",
"poll_interval": 30,
"batch_size": 100,
"image_batch_size": 10,
"min_confidence": 0.3,
"upload_images": true,
"forwarder_enabled": true
}
Note: Config keys are adamaps_key and adamaps_api (NOT api_key/api_url — those were the OLD adacam-api format). Confirmed from Gitea adacam_odc.py DEFAULT_CONFIG (Gitea audit 2026-03-30).
Forwarding stats (as of 2026-03-29):
- Total forwarded: 7,814 detections
- Total images: 2,921
- Last forwarder run: ~Mar 22-23 (before routing conflict blocked internet)
- Cursor position: 229787 (max ID — no backlog when camera last checked)
USB tethering: Auto-detect when phone plugged in. Watches for dynamic usb2 interface (created by kernel on plug-in). Runs udhcpc when carrier detected. Provides internet when home WiFi routing fix isn't applied. Last tested working: 2026-03-25 drive session.
7. All Known Issues
CRITICAL (blocking forwarding)
C1 — WiFi routing conflict (pre-existing, NOT session-introduced)
- Both wlp1s0f0 (AP) and wlp1s0f1 (client) on 192.168.0.0/24
- Camera can't reach internet via wlp1s0f1 — return packets confused between interfaces
- Blocks: DNS resolution, ADAMaps forwarding, bee-tunnel (now removed)
- Fix ready (ip rule + ip route table 100), NOT YET PERSISTED
- Was applied runtime-only on 2026-03-30 but lost on power cycle
C2 — hostapd broken / WiFi connections rejected (session-introduced, 2026-03-29)
- AP SSID
dashcam-4A928016A02C1046visible but rejecting connections ("connection failure") - Root cause:
wifi-client.servicewas created during 2026-03-29 session (hard rule violation) - If service runs wpa_supplicant or ip commands on wlp1s0f0, it takes interface from hostapd
- First fix: stop + disable + remove wifi-client.service, then restart hostapd + dnsmasq
- This is the current blocking issue — cannot access camera without fix
HIGH
H1 — DNS broken on camera (pre-existing)
- systemd-resolved running but not resolving hostnames
resolvectl query api.adamaps.org→ "All attempts to contact name servers failed"- Ping to 8.8.8.8 works (IP connectivity fine) but no DNS
- Fix: add
DNS=8.8.8.8to/etc/systemd/resolved.conf, restart systemd-resolved - Cannot apply until camera is accessible
H2 — WiFi routing fix not persisted
- The 4 ip commands were applied runtime-only on 2026-03-30
- Lost on power cycle (camera was power-cycled)
- Must be re-applied AND added to
/data/persist/install.sh
H3 — adamaps-persist.service fails at boot (pre-existing)
- Service runs
/data/persist/install.sh - Fails because RTC not synced at early boot (timestamp shows 1970-01-01)
mkdir /rootin script also fails (root filesystem read-only at that boot stage)- Impact: install.sh may not execute reliably on power cycles
- Fix: add network/time dependency before running, or
After=time-sync.target
MEDIUM
M1 — Root partition at 86% (pre-existing)
/filesystem: 86% full, ~255MB free/dev/mmcblk1p8(system_b, active rootfs): 1.8GB total, 1.5GB used- Main culprit:
/var/log/daeman.log(5.1MB), rsyslog log rotation issues - Fix:
du -sh /var/log/*, truncate/delete oversized logs, configure logrotate
M2 — /etc/fstab duplicate /factory entry (pre-existing)
- Two identical lines for
/dev/mmcblk1p10 /factory auto defaults,nofail,ro 0 0 - Causes systemd generator warning at boot (non-blocking)
- Fix: remove duplicate line from /etc/fstab
M3 — factory.mount failed (pre-existing)
- Likely related to duplicate fstab OR corrupted/missing partition
/dev/mmcblk1p10may not exist (or is a different device from mmcblk0 vs mmcblk1 naming)- Non-blocking
M4 — here-plugin.service returning after reboot
- Was killed (not masked) at some point
- Came back after reboot
- Needs to be MASKED:
systemctl mask here-plugin.service - Currently "running" but API calls are blocked via /etc/hosts
M5 — pam_nologin blocking SSH (recently observed 2026-03-30)
- SSH responds but returns "System is booting up. Unprivileged users not permitted"
- Persisted for 30+ minutes across multiple power cycles on 2026-03-30
- Likely systemd not reaching multi-user.target due to wifi-client.service hanging boot
- Fixing C2 (wifi-client.service) should resolve this
LOW / INFO
L1 — rsyslogd file size limit errors
daeman.logat 5.1MB, rsyslog configured file size limits hitting- Non-critical
L2 — LTE service cycling every ~10 minutes
- 339+ restarts in logs (as of 2026-03-29)
- Likely watchdog/monitor behavior — by design
- No SIM in device; LTE interface (wwan0) is down
L3 — usb-updater.service failed
- Minor, likely non-critical
- Check:
journalctl -u usb-updater --no-pager
8. Recovery Plans
8.1 Device 1 (Truck Bee) — Current Crisis
Situation (as of 2026-03-30 ~09:52 PDT):
- Camera in truck, powered on (truck was running at last contact)
- Pi is at home (not plugged in)
- Camera AP broadcasting but rejecting ALL WiFi connections
- Cannot access camera until:
- Pi is physically brought to camera (or truck brought home), OR
- Cobb SSHes directly from phone when near camera AP
Step-by-step recovery (once SSH access is restored):
Triage first (READ ONLY):
systemctl is-system-running
systemctl list-units --failed
ls -la /etc/systemd/system/wifi-client.service 2>/dev/null
systemctl status hostapd --no-pager -l
ps aux | grep -E 'wpa_supplicant|hostapd'
ip addr show
cat /data/persist/install.sh
df -h /
Fix #1 — Remove wifi-client.service (LIKELY ROOT CAUSE):
systemctl stop wifi-client.service 2>/dev/null
systemctl disable wifi-client.service 2>/dev/null
rm -f /etc/systemd/system/wifi-client.service
systemctl daemon-reload
systemctl restart hostapd
systemctl restart dnsmasq
# Verify:
systemctl status hostapd
Fix #2 — WiFi routing (do after Fix #1, once internet accessible):
ip route add 192.168.0.1/32 dev wlp1s0f1 2>/dev/null
ip rule add from 192.168.0.155 lookup 100 priority 100 2>/dev/null
ip route add default via 192.168.0.1 dev wlp1s0f1 table 100 2>/dev/null
ip route add 192.168.0.0/24 dev wlp1s0f1 table 100 2>/dev/null
# Then test:
ping -I wlp1s0f1 -c 3 8.8.8.8
curl -s https://api.adamaps.org/api/health
Fix #3 — Persist routing fix in install.sh:
Add all 4 ip commands from Fix #2 to /data/persist/install.sh
Fix #4 — DNS resolution:
echo "[Resolve]" >> /etc/systemd/resolved.conf
echo "DNS=8.8.8.8" >> /etc/systemd/resolved.conf
echo "FallbackDNS=1.1.1.1" >> /etc/systemd/resolved.conf
systemctl restart systemd-resolved
resolvectl query api.adamaps.org
Fix #5 — Mask here-plugin (low priority):
systemctl mask here-plugin.service
systemctl stop here-plugin.service
Fix #6 — Verify full pipeline after fixes:
redis-cli zcard GNSSFusion30Hz # Should be > 0
redis-cli get MAP_AI_READY # Should be "True"
curl -s http://localhost:5500/api/1/health
curl -s http://localhost:5500/api/1/forwarder/status
sqlite3 /data/recording/odc-api.db "SELECT MAX(id), MAX(ts) FROM landmarks"
Fix #7 — Pi cleanup (once camera stable):
# Re-enable zerocool autoconnect:
sudo nmcli connection modify zerocool connection.autoconnect yes
# Restore dashcam-hivemapper to DHCP:
sudo nmcli connection modify 'dashcam-hivemapper' ipv4.method auto ipv4.addresses "" ipv4.gateway ""
# Clean up duplicate profile if present:
sudo nmcli connection delete 'dashcam-4A928016A02C1046'
sudo nmcli connection up zerocool
If all else fails (complete SSH lockout):
- USB-Ethernet tether (ASIX AX88179 or Realtek RTL8153 chipset — ASIX more likely supported in Yocto 5.4.x). Bridge to router/switch with DHCP. Camera should get IP as
keembayhostname. - UART serial console — physical access required. CP2102 or CH340 USB-UART adapter. UART pad locations on Bee PCB need research (not yet documented).
8.2 Device 2 (Brick Bee) — Recovery
Situation:
- SSH key-auth only, no authorized_keys, locked out
- AP broadcasting, WiFi connects, SSH always rejected
- Hardware is alive
Recovery order:
- USB-Ethernet tether: connect USB-A to Ethernet adapter to camera USB port, cable to home router. Check router DHCP for
keembayhostname.ssh root@<ip>. Once in:echo "ssh-ed25519 AAAA..." >> /root/.ssh/authorized_keys(use same key as Device 1). - If USB tether fails: UART serial console (same as Device 1 emergency path).
- If UART: access boot console, can fix
/data/overlay/current/ssh/sshd_configdirectly. - Do NOT try more Mender USB flashes — v1-v8 failed. The overlay in
/data/survives all flashes. Mender cannot fix this without modifying the overlay, which requires shell access first.
9. Complete Change Log (Chronological)
2026-03-11 (or earlier)
- bee-tunnel.service created and deployed on Device 1
- Reverse SSH tunnel configured:
ssh -R 2222:localhost:22 -L 19999:localhost:1340 root@192.168.0.5 - State documented in
bee-tunnel-state.md
2026-03-13 / 2026-03-14
- BEE-CAMERA.md written (hardware/firmware reference)
- Hivemapper on-chain security audit completed (CVE MCID15663720, 90-day window closes 2026-06-07)
2026-03-21
- BeeJump (WireGuard) ABANDONED — too many implementation issues
- Remote adacam access = AP-only going forward
2026-03-22
- TWO BEES documented: Device 1 (truck,
dashcam-4A928016A02C1046) confirmed working; Device 2 (dashcam-81B2B81681545109) confirmed bricked (locked out by bad liberation script) - Reverse SSH tunnel investigation: TCP relay fundamentally broken on Bee's OpenSSH — banner timeout even on standalone sshd
- HTTP agent API deployed at
/data/adacam/agent.py:8080 - Deep recon: 9 files in
/root/.openclaw/workspace/recon/ - Key discovery: detections in
/data/recording/odc-api.dbSQLite, not JSON files - Key discovery: root has no password on stock Bee
- map-ai.service
Requires=odc-api.servicedependency removed via override at/etc/systemd/system/map-ai.service - adacam-forwarder-v2.py written (odc-api.db backed), deployed to Bee
- Forwarder caught up: all 4,285 landmarks forwarded (cursor at 222754)
- USB bridge (192.168.197.55) confirmed dead — OTG not triggering, not a viable access path
- Varroa app: default Bee URL changed from
10.77.0.1to192.168.0.10 - bee-security-audit.md written (note: appears to be Device 2, 2P021849, stock firmware)
- Mender v1-v8 recovery artifacts tried on Device 2 — none worked (overlay survives flash)
2026-03-23 (into early Mar 24)
- Full Hivemapper odc-api source audit (docs/hivemapper-odc-api-deep-dive-2026-03-23.md)
- Strip plan written (docs/adacam-odc-strip-plan-2026-03-24.md)
- adacam-odc.py built (~800 lines, Flask + forwarder thread, replaces odc-api + old forwarder)
adacam-odc.servicesystemd unit createddeploy.shcreated- ADAMaps server: migrations 004, 005, 006 applied (detections: cam_heading, attributes, bbox; signs: ml_sign_text, verification_source; agent_registry: manifest)
- Tiered verification, 1-minute task expiry, pure JSON task format implemented
- agents.html deployed at adamaps.org/agents.html
2026-03-24
- adacam-odc.py deployed to Device 1 via
deploy.sh - odc-api killed and disabled
- Important note from MEMORY.md: "adacam-odc Python rebuild (2026-03-24) — Key discovery: map-ai writes directly to SQLite — odc-api never touched landmark writes"
2026-03-25
- USB tethering monitor implemented in adacam-odc.py (dynamic usb2 interface detection)
- USB tether tested: 172.22.22.37 IP, 8.8.8.8 ping works
- odc-api processes killed (were self-restarting):
systemctl stop odc-api; systemctl disable odc-api; killall -9 node - Drive session: Gower & Sunset LA → Redondo Beach; live uploads worked via USB tether
- Forwarder: 500 detections + 10 images per cycle; total at session start: ~6,051 detections, 2,480 images
2026-03-26
- map-ai.py and map-ai.sh saved to Gitea (docs/map-ai/)
- KEY DISCOVERY:
isCacheMode = os.path.exists("/data/.cache")— if/data/.cacheexists, map-ai skips projection matrices and landmark writes - KEY DISCOVERY:
hivemapper-data-loggerbinary path/dev/ttyAMA1doesn't exist (GPS on/dev/ttyS2per prepare-gps.sh) - Camera bridge port conflict discovered: adacam-odc camera bridge was on :9001, same as datalogger HTTP port
- Camera bridge moved 9001→9002 (commit
bd0c4cff008c), then DISABLED entirely (commit76b177a8e0e8) - adacam-odc confirmed running on port 5500 (not 5000)
- Note: GPS was dead, SQLite frozen — incorrectly attributed to odc-api being dead (GPS was the actual issue)
- here-plugin came back after reboot (was killed not masked)
adamaps-persist.servicefailed:mkdir /rootin install.sh fails at early boot
~2026-03-24 to 2026-03-26 (CHANGE LOG GAP)
hivemapper-data-logger.servicewas DISABLED — no log entry records who did this or when- Most likely result of the 2026-03-22 kill list recommendation being acted on
2026-03-29 (morning/afternoon)
- DNS diagnostic: confirmed WiFi connected (zerocool), internet reachable (8.8.8.8 ping), but DNS broken
- Forwarder dead since Mar 23 (HTTP timeouts), not recovering because DNS broken
- HARD RULE VIOLATION: Two changes made after "read only" instruction:
/etc/resolv.confoverwritten withnameserver 8.8.8.8(was systemd-resolved symlink)/etc/systemd/system/wifi-client.servicecreated and enabled- resolv.conf symlink partially restored in session; wifi-client.service NOT yet removed
2026-03-29 (late afternoon/evening)
- ROOT CAUSE of zero detections since Mar 24 FOUND:
hivemapper-data-logger.servicedisabled - hivemapper-data-logger RE-ENABLED: GPS flowing (4Hz), IMU (197Hz), MAP_AI_READY=True, 653 Redis entries confirmed
- map-ai restarted
- bee-tunnel.service REMOVED: service file deleted,
/data/mapnet/directory deleted, SSH tunnel key deleted - bee-collector.service REMOVED: service file deleted
- odc-api confirmed dead, not needed
/data/persist/install.shupdated (removed bee-tunnel/bee-collector references)- master-audit written and committed to Gitea (
a8289028) - RECON.md pushed to Gitea
- adacam-pipeline-map.md written (full pipeline analysis)
- Bug report written (adacam-bug-report-2026-03-29.md)
2026-03-29 end-of-day state:
| Component | Status |
|---|---|
| hivemapper-data-logger | ✅ Running |
| map-ai | ✅ Running |
| adacam-odc | ✅ Running, port 5500 |
| WiFi client | ⚠️ Connected but no internet (routing conflict) |
| WiFi routing fix | NOT APPLIED |
| wifi-client.service | ⚠️ Present on device, not yet removed |
| bee-tunnel | ✅ Removed |
| bee-collector/mapnet | ✅ Removed |
2026-03-30 (morning)
- Recovery plan written (adacam-recovery-plan-2026-03-30.md)
- HARD RULE VIOLATION: wpa_supplicant run directly on Pi wlan0 — corrupted brcmfmac WiFi driver
- Pi reboot fixed driver
- Static IP workaround: nmcli profile
dashcam-hivemapperset to 192.168.0.11/24 static (no gateway/DNS) - Pi connected to dashcam AP via static IP
- Camera SSH returned pam_nologin ("system is booting up") — blocked for 30+ minutes
- Routing fix applied runtime-only (4 ip commands) — LOST on next power cycle
- Camera power-cycled: DHCP broken (camera not handing out IPs) — static IP workaround still works
- Camera AP later seen broadcasting but rejecting all WiFi connections
- Camera left in truck, Pi left at home (~09:52 PDT)
- Current blocked state at session end
10. Open TODOs (Prioritized)
Priority 1 — Restore Camera Access
- Get Pi physically near camera OR bring camera home
- Connect Pi to camera via static IP (192.168.0.200/24 on dashcam-hivemapper profile)
- Wait for camera to fully boot (5+ min after power-on, pam_nologin should clear)
- Triage: confirm wifi-client.service is present and causing hostapd failure
- Remove wifi-client.service (see Recovery Plan 8.1)
Priority 2 — Fix Forwarding Pipeline
- Apply WiFi routing fix (4 ip rule commands)
- Persist routing fix in
/data/persist/install.sh - Fix DNS (add DNS=8.8.8.8 to /etc/systemd/resolved.conf)
- Verify
curl -s https://api.adamaps.org/api/healthworks from camera - Restart adacam-odc, verify forwarder resumes
Priority 3 — Stability / Housekeeping
- Mask
here-plugin.service(was killed not masked, keeps coming back) - Fix
adamaps-persist.serviceboot timing (After=time-sync.target) - Fix
/etc/fstabduplicate /factory entry - Clean up root partition logs (
/var/log/daeman.log, logrotate config) - Decide on AP subnet change (172.20.0.1/24 recommended — Cobb to decide)
- Document exact current content of
/data/persist/install.sh
Priority 4 — Device 2 Recovery
- USB-Ethernet tether attempt
- Research UART pad locations on Hivemapper Bee PCB teardown
- If UART accessible: fix
/data/overlay/current/ssh/sshd_configdirectly
Priority 5 — Documentation
- Update
bee-tunnel-state.md→ mark OBSOLETE (service removed) - Update
BEE-CAMERA.md→ add "HISTORICAL - stock firmware" header - Update
bee-security-audit.md→ add "PRE-LIBERATION, Device 2P021849" header - Archive
adacam-odc-redis-consumer-plan.md→ Redis consumer NOT needed (map-ai writes directly to SQLite) - Fix port in
adacam-master-audit.mdfrom 5000 → 5500 - Investigate Wigle/config API Cobb mentioned (2026-03-24) — still unknown
- Fix Gitea RECON.md — remove incorrect "does not use port 5500 (that was a wrong guess)" note. Update to say Port 5500.
- Fix Gitea adacam-odc.service — change
Environment=PORT=5000toEnvironment=PORT=5500 - Fix Gitea BEE-ACCESS-PLAN.md — remove direct wpa_supplicant instructions (HARD RULE VIOLATION), replace with nmcli procedure
- Commit current /data/persist/install.sh to Gitea once camera accessible — it's never been versioned
11. Access Quick Reference
# Pi (access bridge)
ssh cobb@192.168.0.184
# Camera (via Pi)
ssh cobb@192.168.0.184
# then:
ssh root@192.168.0.10
# Camera health check
curl -s http://localhost:5500/api/1/health
# → {"status":"ok","version":"adacam-odc-2.0.0"}
# Forwarder status
curl -s http://localhost:5500/api/1/forwarder/status
# GPS check (must be > 0)
redis-cli zcard GNSSFusion30Hz
# Detection count
sqlite3 /data/recording/odc-api.db "SELECT COUNT(*), MAX(id) FROM landmarks"
# Routing fix (runtime)
ip route add 192.168.0.1/32 dev wlp1s0f1 2>/dev/null
ip rule add from 192.168.0.155 lookup 100 priority 100 2>/dev/null
ip route add default via 192.168.0.1 dev wlp1s0f1 table 100 2>/dev/null
ip route add 192.168.0.0/24 dev wlp1s0f1 table 100 2>/dev/null
# Key file locations
/data/adacam/adacam_odc.py # Main service
/data/adacam/config.json # ADAMaps config
/data/adacam/forwarder_state.json # Forward cursor
/data/adacam/forwarder.log # Forwarder log
/data/persist/install.sh # Boot persistence
/data/recording/odc-api.db # Detection SQLite DB
/data/ppz.json # Privacy zone (100m radius at home)
/opt/map-ai/map-ai.py # ML pipeline (READ ONLY filesystem)
/opt/dashcam/bin/datalogger # GPS/IMU binary (READ ONLY filesystem)
# ADAMaps API
https://api.adamaps.org/api/health
https://api.adamaps.org/api/ingest (X-AdaMaps-Key: adamaps-ingest-2026)
12. Key Identifiers
| Thing | Value |
|---|---|
| ADAMaps API key (ingest) | adamaps-ingest-2026 |
| Device anonymous ID | fvhL2I-iCT |
| Gitea repo | Sulkta-Coop/adacam |
| Gitea API token | 33a9eb57b58c262f4434c12028bc3a30b1ff7021 |
| Gitea URL | http://192.168.0.5:3001 |
| Lucy SSH | root@192.168.0.5 (key: /root/.ssh/id_ed25519_unraid) |
| Rackham | cobb@142.44.213.229 |
| ADAMaps Rackham deploy | /opt/adamaps/ |
| MAP treasury | addr1wxdy5dkg2serxmf69yczhz004lcqcsupxw9gjr9jrl95rpsgc3hgm |
| ADAMaps payout wallet | addr1vyr6m0yna0676j20krxds8ls7xklc0uvmjw5ac5k9yxsmvgkw743n |
13. ADAMaps — The Server Side
ADAMaps is the cloud backend that receives, stores, clusters, and serves detections forwarded by adacam-odc. This section documents the ADAMaps side of the pipeline so a fresh session knows the full picture without reading separate ADAMaps files.
13.1 Infrastructure
| Component | Location | Details |
|---|---|---|
| API service | Rackham VPS (142.44.213.229) |
Flask/Gunicorn, port 5001 internal, proxied via Apache to api.adamaps.org |
| PostGIS database | Rackham (container adamaps-postgres) |
postgis/postgis:16-3.4, DB adamaps, user adamaps, pw adamaps2026 |
| Lucy replica | Lucy 192.168.0.5 |
Logical replication (puballtables=t), all tables auto-replicate |
| VPN | OpenVPN (NOT WireGuard) | Server: Lucy (Docker); Client: Rackham (native) |
| Gitea | Sulkta-Coop/adamaps, latest commit 5a7d407 (pre-audit; docs updated to 74f787f) |
|
| API health | https://api.adamaps.org/api/health → {"status":"ok","node":"rackham","agent_api":true,"phase":3} |
|
| Live stats (2026-03-29) | 14,523 detections · 1,833 signs · 108 agent-verified · 2,941 images · 7 devices · 0.178m avg accuracy |
IMPORTANT: Detection count (14,523) has been frozen since ~Mar 25 because the camera's routing conflict blocks internet access. New detections are accumulating in camera SQLite but can't be forwarded.
13.2 adacam → ADAMaps Detection Pipeline
Camera SQLite (odc-api.db)
↓
adacam-odc Thread 2 (Forwarder)
↓ POST /api/ingest
ADAMaps Flask API (Rackham:5001)
↓ INSERT
detections table (PostGIS, Rackham)
↓ _update_sign_from_detection()
signs table (40m cluster radius, confidence-weighted centroid)
↓ (background/on-demand)
agent_task_claims → consensus → task_consensus → map_earnings → payout_batches
13.3 adacam-odc Ingest Format
Endpoint: POST https://api.adamaps.org/api/ingest
Auth header: X-AdaMaps-Key: adamaps-ingest-2026
Payload sent by adacam-odc forwarder (confirmed base fields):
{
"device_id": "fvhL2I-iCT",
"detections": [
{
"id": "229787", ← camera's local SQLite ID (NOT ADAMaps sequential ID)
"ts": 1742950000000, ← Unix milliseconds
"lat": 33.8677,
"lon": -118.3776,
"class_label": "road_sign", ← maps to ADAMaps sign_type
"overall_confidence": 0.847, ← maps to ADAMaps confidence
"device_id": "fvhL2I-iCT"
}
]
}
Extended fields (in camera SQLite, forwarding status CONFIRMED by Gitea audit 2026-03-30):
| Field | In camera DB | ADAMaps accepts | Forwarded by adacam-odc? |
|---|---|---|---|
cam_lat, cam_lon |
✅ | ✅ (migration 004) | ✅ Yes (if non-null) |
cam_heading |
✅ | ✅ (migration 004) | ✅ Yes (if non-null) |
azimuth |
✅ | ✅ | ✅ Yes (if non-null) |
attributes JSON |
✅ ({"speed_label":25,"speed_label_conf":0.99}) |
✅ (JSONB column) | ✅ Yes (parsed + forwarded if present) |
x1/y1/x2/y2 bbox |
✅ | ✅ (bbox_x1/y1/x2/y2) |
✅ Yes (if non-null) |
map_feature_id |
✅ | ✅ | ✅ Yes (if non-null) |
speed_label / speed_label_conf |
✅ | ✅ | ✅ Yes (if non-null) |
All extended fields ARE forwarded by adacam_odc.py — confirmed from Gitea code review. The 2026-03-23 codebase audit's "missing fields" finding was based on the OLD standalone adacam-forwarder-v2.py, not the current combined service. The combined service (built 2026-03-24) includes all fields.
API field name mapping (handled by ADAMaps ingest endpoint):
class_label→ stored assign_typeoverall_confidence→ stored asconfidencets(ms) → stored asdetected_at(timestamptz)
ADAMaps ingest response:
{"inserted": 500, "device_id": "fvhL2I-iCT"}
⚠️ Does NOT return the ADAMaps-assigned sequential IDs.
13.4 Image Upload Format
Endpoint: POST https://api.adamaps.org/api/images
Auth: X-AdaMaps-Key: adamaps-ingest-2026
Content-Type: multipart/form-data
Fields: detection_id (camera local SQLite ID as string), device_id, image (JPEG file)
Image-detection linkage CONFIRMED (Gitea audit 2026-03-30): adamaps/app.py at line 1066 matches by raw_json->>'id' = detection_id (the camera's local SQLite ID). This works because the detection ingest stores the full detection JSON (including "id" field) in the raw_json JSONB column. The 2,941 successfully linked images confirm this is working.
13.5 ADAMaps Database Schema (Key Tables)
detections — one row per camera detection event:
id (serial), device_id, detected_at, lat, lon, geom (PostGIS),
sign_type, confidence, image_path, raw_json (JSONB),
bbox_x1/y1/x2/y2 (float), sign_id (FK → signs),
cam_lat, cam_lon, cam_heading, azimuth, attributes (JSONB), map_feature_id
signs — clustered/deduplicated sign locations:
id (serial), lat, lon, sign_type, observation_count, avg_confidence,
confidence_weight, device_count, first_seen, last_seen,
agent_verified (bool), ml_verified (bool), sign_text (Phase 2 result),
ml_sign_text, ml_sign_text_conf, verification_source,
task_priority (1=normal, 2=ML-pre-verified at 40% reward),
location_accuracy_m, merged_into (FK, dedup), merge_dismissed (bool),
image_name, heading
agent_registry — registered AI agents:
id (serial), agent_id (VARCHAR "agt_"+blake2b), cardano_address,
public_key_hex, api_key_hash (SHA256), tier, reputation_score (0-100),
total_submissions, consensus_agreements, map_earned (NUMERIC, display),
groundtruth_passed (bool), is_oracle (bool),
agent_manifest (JSONB: agent_type, model, runtime, operator_wallet)
map_earnings — individual earning events (feeds payout system):
id (serial), agent_id (FK), amount (BIGINT, raw × 1,000,000),
reason, sign_id, phase, payout_item_id (FK, NULL until batched), paid_at
payout_batches / payout_items — Cardano payout state machine
13.6 ADAMaps API Endpoints (Phase 3 — Live)
Ingest (device → server):
POST /api/ingest X-AdaMaps-Key Batch detection upload
POST /api/images X-AdaMaps-Key Image file upload
Public / rate-limited (READ_KEY: adamaps-read-2026):
GET /api/health None System health
GET /api/stats None Aggregate counts
GET /api/detections Read key Recent detections list
GET /api/signs Read key Clustered sign list
GET /api/signs/tasks Read key Agent training task feed
GET /api/images/<filename> Rate limited Serve image
GET /api/images/<fn>/crop Rate limited Serve cropped sign (needs bbox)
GET /api/leaderboard None Top agents
Agent (X-Agent-Key auth — HMAC):
POST /api/agent/challenge None Request auth nonce
POST /api/agent/register None Register (Ed25519 sig + manifest)
GET /api/agent/me X-Agent-Key Agent profile
GET /api/agent/groundtruth X-Agent-Key 5 oracle signs for test (3/5 pass)
POST /api/agent/groundtruth/submit Grade test, assign tier
GET /api/agent/tasks X-Agent-Key Phase 1 or 2 task feed
POST /api/agent/claim/<sign_id> 60-second claim window
POST /api/agent/submit X-Agent-Key+Sig Submit with HMAC signature
GET /api/signs/dedup-tasks Read key Sign dedup voting pairs
POST /api/agent/dedup/vote X-Agent-Key Vote same/different
POST /api/agent/rotate-key X-Agent-Key Rotate API key
Admin (X-AdaMaps-Key: adamaps-ingest-2026):
POST /api/admin/payouts/trigger Manual payout run
GET /api/admin/payouts/status Payout system state
POST /api/admin/signs/recalibrate Backfill sign_id on detections
13.7 Agent System Summary
Registration flow:
POST /challenge → sign nonce with Ed25519 → POST /register with signature + manifest (agent_type, model required) + 5 ADA Koios stake check (non-blocking)
Tiers:
| Tier | Rep | Access |
|---|---|---|
probation |
0-29 | Can submit, can't form consensus quorum |
standard |
30-59 | Phase 1 tasks |
trusted |
60-79 | Phase 1 + Phase 2 |
expert |
80-100 | Full, max trust weight |
Ground truth test: 5 oracle-curated signs, need 3/5 correct to exit probation (30-min token TTL).
Task lifecycle:
Feed → Claim (60s window, -2 rep on expiry) → Submit with HMAC signature → Consensus check
Reward multipliers:
conf_mult = 1.5 - confidence # 0.5 conf → 1.5x reward, 0.95 → 0.55x
obs_mult = 1.25 if obs ≤ 2 else (1.0 if obs ≤ 5 else 0.75)
reward = base * conf_mult * obs_mult
# ML pre-verified signs: base * 0.4 (40% — confirmation not discovery)
Live agents:
agt_ffea50ac782f78c6(Kayos, is_oracle=TRUE, expert) — keys inmemory/kayos-agent-identity.jsonagt_5e7e17c5c0430919(Opus test agent) — keys inmemory/opus-agent-identity.json
13.8 Payout System
Schedule: Every Monday 10:00 UTC via APScheduler (file lock ensures single Gunicorn worker)
Library: PyCardano + Ogmios (Rackham container, port 1337, fully synced mainnet)
Minimum payout: 1 MAP (1,000,000 raw)
Max outputs per tx: 50
Min ADA per output: 1.5 ADA
Hot wallet (automated payouts): addr1vyr6m0yna0676j20krxds8ls7xklc0uvmjw5ac5k9yxsmvgkw743n
Balance: 1,000,000 MAP + 1.5 ADA — ⚠️ needs 50+ ADA top-up before high-volume payout runs
Treasury (2-of-2 multisig, Jacob + Kayos): addr1wxdy5dkg2serxmf69yczhz004lcqcsupxw9gjr9jrl95rpsgc3hgm
Balance: 49,000,001 MAP + ~1.78 ADA
MAP policy ID: 24bd9e7b9ae3a61df79eca72fd8355d0f7767e4c55a04a0d919c019c
MAP decimals: 6 — 1 MAP display = 1,000,000 raw BIGINT in map_earnings.amount
13.9 Sign Clustering Logic
- Cluster radius: 40m (detections within 40m of same type → update existing sign)
- Minimum confidence to create new sign: 0.30
- Confidence-weighted centroid: new sign lat/lon = weighted average of all detections
- Location accuracy: 0.178m average (computed from centroid deviation)
verification_sourcecolumn:edge_ml|agent|oracletask_priority=2for signs where MLsign_text_conf ≥ 0.99(pre-verified → 40% reward)
13.10 Honeypot Canaries
5 fake detections in the ADAMaps DB (planted 2026-03-22) for scraping/theft detection:
| ID | Coords | Type | Device |
|---|---|---|---|
| 12256 | 33.9912345, -118.4423456 | speed-limit | honeypot-canary |
| 12257 | 33.9834521, -118.4401234 | stop-sign | honeypot-canary |
| 12258 | 33.9756789, -118.4512345 | yield | honeypot-canary |
| 12259 | 33.9678901, -118.4478901 | speed-limit | honeypot-canary |
| 12260 | 33.9590123, -118.4534567 | do-not-enter-sign | honeypot-canary |
Any query results containing device_id=honeypot-canary = data exfiltration detected.
13.11 ADAMaps Open Issues / TODOs
| Priority | Issue | Notes |
|---|---|---|
| P1 | Detection forwarding broken | Camera WiFi routing conflict — fix adacam first |
| P1 | Airdrop hot wallet needs ADA top-up | 1.5 ADA only covers ~1 payout; needs 50+ ADA |
| P2 | adacam-odc extended fields not forwarded | |
| P2 | api/app.py legacy file in adamaps repo | Should be deleted — causes confusion |
| P3 | adacam-api Gitea repo not archived | ALREADY DONE — confirmed archived since 2026-03-14 (Gitea audit 2026-03-30) |
| P3 | adacam-odc-redis-consumer-plan.md | Superseded; should be archived |
| P3 | docs/ADAMAPS-TECHNICAL.md | Completely outdated; should be deleted or archived |
| P4 | Cardano db-sync / Blockfrost integration | Rewire Cardano Chain API away from abandoned db-sync |
Generated by deep audit. Sources: all memory/ files, all daily notes 2026-03-01 through 2026-03-30, docs/ directory, docs/ADAMAPS-TECHNICAL.md, adacam-codebase-audit-2026-03-23.md, adacam-cross-verify-2026-03-24.md, adamaps-audit-2026-03-29.md, adamaps-docs-update-2026-03-30.md, BEE-CAMERA.md, bee-security-audit.md, bee-ssh-diagnostic-report.md.