adacam/docs/research/adacam-project-state.md

43 KiB
Raw Blame History

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:

  1. Ethernet USB tether (same procedure as Device 1, section 7)
  2. UART serial console — physical access, CP2102/CH340 USB-UART adapter. UART pad locations on Hivemapper Bee PCB are NOT yet documented. Needs teardown research.
  3. 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:

  1. Installs SSH key for remote access:
    ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAII5ckRf/4SA84JOrmJtElHBT3dU9RC2Le5GBfqhWWVc8 root@keembay
  2. Masks Mender OTA (systemctl stop/disable/mask mender)
  3. Blocks Mender network access via iptables
  4. 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/status
  • GET /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-4A928016A02C1046 visible but rejecting connections ("connection failure")
  • Root cause: wifi-client.service was 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.8 to /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 /root in 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/mmcblk1p10 may 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.log at 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):

  1. 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 keembay hostname.
  2. 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:

  1. USB-Ethernet tether: connect USB-A to Ethernet adapter to camera USB port, cable to home router. Check router DHCP for keembay hostname. ssh root@<ip>. Once in: echo "ssh-ed25519 AAAA..." >> /root/.ssh/authorized_keys (use same key as Device 1).
  2. If USB tether fails: UART serial console (same as Device 1 emergency path).
  3. If UART: access boot console, can fix /data/overlay/current/ssh/sshd_config directly.
  4. 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.db SQLite, not JSON files
  • Key discovery: root has no password on stock Bee
  • map-ai.service Requires=odc-api.service dependency 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.1 to 192.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.service systemd unit created
  • deploy.sh created
  • 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/.cache exists, map-ai skips projection matrices and landmark writes
  • KEY DISCOVERY: hivemapper-data-logger binary path /dev/ttyAMA1 doesn't exist (GPS on /dev/ttyS2 per 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 (commit 76b177a8e0e8)
  • 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.service failed: mkdir /root in install.sh fails at early boot

~2026-03-24 to 2026-03-26 (CHANGE LOG GAP)

  • hivemapper-data-logger.service was 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.conf overwritten with nameserver 8.8.8.8 (was systemd-resolved symlink)
    • /etc/systemd/system/wifi-client.service created 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.service disabled
  • 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.sh updated (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-hivemapper set 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/health works 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.service boot timing (After=time-sync.target)
  • Fix /etc/fstab duplicate /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_config directly

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.md from 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=5000 to Environment=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 as sign_type
  • overall_confidence → stored as confidence
  • ts (ms) → stored as detected_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 in memory/kayos-agent-identity.json
  • agt_5e7e17c5c0430919 (Opus test agent) — keys in memory/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_source column: edge_ml | agent | oracle
  • task_priority=2 for signs where ML sign_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 cam_heading, attributes, bbox — verify + fix in forwarder RESOLVED: All fields ARE forwarded in current adacam_odc.py (Gitea audit 2026-03-30)
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.