adacam/PROJECT_STATUS.md
Kayos ed7ae5ba57 docs+scripts: project status, build scripts, patched usb-updater v5
- PROJECT_STATUS.md: full project log (hardware, partitions, artifacts, lessons learned, next steps)
- scripts/build/build-artifact-from-existing.py: rebuild artifact from existing data tar with new header
- scripts/build/build-v5-patched-updater.sh: patch system.img usb-updater + build artifact
- recovery/usb-updater-v5-patched: patched usb-updater with SSH recovery prepended

adacam-ssh-fix-v5.mender: 403MB, SHA256 acfbd16db9620f23785f8b103ffaeff6aed780f383273a61a23c8002f2bf0980
Status: PENDING TEST on replacement Bee (192.168.0.10)
2026-03-16 09:58:45 -07:00

11 KiB

AdaCam / Hivemapper Bee — Project Status

Last updated: 2026-03-16


Goal

Convert Hivemapper Bee dashcams (HDC-S, Intel Keem Bay platform) into "AdaCam" units:

  • Block Hivemapper telemetry / OTA
  • Install adacam services (GPS forwarding, Wigle logging, etc.)
  • Regain persistent root SSH access
  • Long-term: custom firmware image, signed USB recovery, own update infrastructure

Hardware

Item Value
Platform Intel Keem Bay (keembay)
SoC Movidius MV0212, Yocto Linux
Storage eMMC, 11 partitions
Update mechanism Hivemapper USB updater (usb-updater.service) + custom Mender fork
SSH dropbear or openssh on port 22
AP interface wlp1s0f0 (192.168.0.10 factory)
WiFi client wlp1s0f1
LTE LE910C4-NF (wwan0)

Partition Layout

Partition Size Purpose
mmcblk1p1 32MB ?
mmcblk1p2 512K ?
mmcblk1p3 512K ?
mmcblk1p4 256MB Kernel A (ext4, FIT image)
mmcblk1p5 3GB Rootfs A (ext4)
mmcblk1p6 128MB dm-verity hashtree A
mmcblk1p7 256MB Kernel B
mmcblk1p8 3GB Rootfs B (ext4)
mmcblk1p9 128MB dm-verity hashtree B
mmcblk1p10 64MB /factory
mmcblk1p11 ~52GB /data (rw, persistent)

Filesystem Mounts (critical)

Mount Source Type Notes
/ mmcblk1p5 or p8 ext4 READ-ONLY
/etc overlayfs overlay lower=rootfs/etc, upper=/data/overlay/current/, workdir=/data/overlay/workdir/
/data mmcblk1p11 ext4 rw, persists across ALL firmware flashes
/home /data/home bind persistent
/var /data/var bind persistent
/uboot mmcblk1p4 ext4 U-Boot env partition

KEY INSIGHT: Only /etc is overlaid. /usr, /bin, /usr/bin etc. are directly on the read-only rootfs. The overlay upper dir lives on /data which survives firmware flashes.


Firmware / Update System

Mender Fork

  • Version: f9a29241 (custom Hivemapper fork, not standard Mender)
  • Does NOT call state scripts (confirmed via v4 test — no USB log file written)
  • Artifact format (must match exactly):
    {"payloads":[{"type":"dm-verity-update"}],"artifact_provides":{"artifact_name":"NAME"},"artifact_depends":{"device_type":["keembay"]}}
    
  • NO rootfs-image.version in provides (breaks install if included)
  • NO state scripts
  • NO meta-data file needed

dm-verity-update Module

  • Path: /usr/share/mender/modules/v3/dm-verity-update
  • Streams (in order): system.img → passive_rootfs, syshash.img → passive_hash, boot.img → passive_kernel
  • Blindly cats streams — does NOT recompute hash tree
  • dm-verity is NOT enforced at runtime (verity=0 in /proc/cmdline)
  • syshash is only used by usb-updater for "already up to date" comparison check

usb-updater Script

  • Path: /usr/bin/usb-updater
  • Triggered by usb-updater.service (enabled, not masked by liberate.sh)
  • Flow:
    1. Create swapfile at /data/swap if not exists (2GB)
    2. Find .mender file in /mnt/usb/hivemapper_update/
    3. Extract outer tar to USB tmp dir
    4. Extract syshash.img from data/0000.tar.gz
    5. Compare USB syshash with active hash partition (mmcblk1p6 or p9)
    6. If same → "OS up to date", exit
    7. If different → mender --install artifact.mender
    8. If success → mender --commitreboot
    9. If fail → "Update failed", exit
  • USB dir cleaned up after syshash compare (before mender runs)
  • mender-client.service being masked does NOT affect CLI mender --install

A/B Slot Logic

U-Boot var Meaning
mender_boot_part=5 Slot A active
mender_boot_part=8 Slot B active
upgrade_available=1 Update pending commit
upgrade_available=0 Committed / stable

Post-install rollback trap: usb-updater calls mender --commit before reboot. After reboot, ArtifactVerifyReboot in the mender state machine checks upgrade_available=1 — but commit already set it to 0 → exits 1 → mender triggers rollback procedure. However ArtifactRollback condition also fails (upgrade_available != 1) so boot_part doesn't change. Device ends up on new firmware but mender thinks it rolled back. Harmless in practice.

dm-verity Parameters (for veritysetup)

Hash algorithm: sha384
Salt: 3f3e0633b0a2cbf5066cd50af1a178ff8f50f660382f6c7508f58566cec64142
Data blocks: 524288
Block size: 4096

Device Status

Unit 1 — Original Bee (LIBERATED )

  • IP: 192.168.0.155 (from Lucy's perspective, via SSH tunnel)
  • SSH: works, root access
  • Tunnel: bee-tunnel.service → Lucy (when powered on)
  • Status: Fully liberated, adacam services running
  • mender_boot_part: 8 (Slot B active)
  • Artifact: Release-20260309193836

Unit 2 — Replacement Bee (LOCKED OUT ⚠️)

  • IP: 192.168.0.10 (factory AP)
  • SSH: key-only, no authorized keys → Permission denied (publickey)
  • Cause: liberate.sh (old version, before v0.4) ran Phase 4 SSH hardening (PasswordAuthentication no) then failed at mkdir /root/.ssh (read-only rootfs). Hardened sshd_config was written to /data/overlay/current/ssh/sshd_config before the failure.
  • Overlay file /data/overlay/current/ssh/sshd_config has hardened config and survives every flash
  • usb-updater.service: enabled, NOT masked
  • mender-client.service: masked (by Phase 1 of liberate.sh — harmless for USB updates)

Artifacts Built

File Size Description Result
ssh-recovery.mender small State script only, no firmware State scripts not called → failed
hivemapper-forced.mender 431MB Factory firmware, random syshash Infinite USB loop (hash mismatch)
adacam-ssh-fix.mender 443MB Modified sshd_config in system.img, correct syshash, no state scripts Overlay overrides system.img changes → still locked
adacam-ssh-fix-v2.mender 444MB State script + wrong header (rootfs-image.version) mender --install likely failed
adacam-ssh-fix-v3.mender 444MB State script + correct header format State scripts not called by this mender fork
adacam-ssh-fix-v4.mender 444MB State script + USB log debug Confirmed: NO log file = state scripts never run
adacam-ssh-fix-v5.mender 403MB Modified usb-updater in system.img + correct syshash PENDING TEST

All artifacts at: /mnt/cache/appdata/openclaw/config/workspace/projects/adacam/recovery/

v5 Approach (current)

  • Modified /usr/bin/usb-updater in system.img to prepend SSH recovery code
  • /usr/bin is on rootfs, NOT in /etc overlay → change survives flash
  • On next USB insert after reboot into v5 firmware, usb-updater:
    1. Writes open sshd_config to /etc/ssh/sshd_config (via overlay to /data/overlay/current/ssh/sshd_config)
    2. Writes authorized_keys to /home/root/.ssh/authorized_keys
    3. Restarts sshd
    4. Continues normal usb-updater behavior
  • SHA256: acfbd16db9620f23785f8b103ffaeff6aed780f383273a61a23c8002f2bf0980
  • Served via nginx on Lucy: http://192.168.0.5:9999/adacam-ssh-fix-v5.mender

Build Infrastructure

Files on Lucy

/mnt/cache/appdata/openclaw/config/workspace/
  projects/
    adacam/
      firmware/
        extracted/
          core.mender                          # original factory firmware
          mender/data_extracted/
            system.img                         # original 2GB rootfs
            syshash.img                        # original 33MB hash tree
            boot.img                           # original 256MB kernel (ext4 + FIT image)
      recovery/
        ssh-recovery.mender
        hivemapper-forced.mender
        adacam-ssh-fix.mender
        adacam-ssh-fix-v2.mender
        adacam-ssh-fix-v3.mender
        adacam-ssh-fix-v4.mender
        adacam-ssh-fix-v5.mender              # CURRENT
    build_v2.py                                # artifact builder (reuses existing data tar)
    build_v5.sh                                # system.img patcher + artifact builder
    patch-mender-artifact.py
    build-mender-artifact.py

/tmp/bee_v5_work/
  system.img                                   # patched (usb-updater prepended)
  syshash.img                                  # recomputed for patched system.img
  usb-updater-patched                          # the patched script (reference copy)

Build Commands

# SSH to Lucy
ssh -i ~/.openclaw/id_ed25519_unraid -o StrictHostKeyChecking=no root@192.168.0.5

# Run Python via Docker (python3 not available on Lucy bare metal)
docker run --rm \
  -v /mnt/cache/appdata/openclaw/config/workspace:/workspace \
  -v /tmp/bee_v5_work:/v5work \
  mingc/android-build-box python3 /v5work/build.py

# Compute syshash
veritysetup format \
  --hash=sha384 \
  --salt=3f3e0633b0a2cbf5066cd50af1a178ff8f50f660382f6c7508f58566cec64142 \
  --data-block-size=4096 \
  --hash-block-size=4096 \
  system.img syshash.img

# Serve artifact
docker restart adacam-http   # nginx on Lucy:9999

Keys (authorized_keys)

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK87jxvlXvo60pxwdtyJsXeFsb4KsAiFx4FnyXz81kh7 cobb@adacam
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOQxwJU91TCxds34P18D3xRbu7rxlrgTUoml/H8nxeDK kayos@openclaw

Lessons Learned

  1. Never harden SSH before confirming key auth works. Write keys first, test, then restrict.
  2. The /etc overlay survives firmware flashes. Any config change via /etc goes to /data and persists. Flashing new sshd_config in system.img does nothing if the overlay has a conflicting file.
  3. This Mender fork does NOT call state scripts. Don't use state scripts with dm-verity-update type.
  4. rootfs-image.version in artifact_provides breaks mender --install. Match the exact original header format.
  5. usb-updater cleans up its tmp dir AFTER the syshash comparison. Seeing tmp on the USB mid-process is normal; it doesn't indicate failure.
  6. /usr/bin is NOT overlaid. Changes to files in system.img outside of /etc are not overridden and take effect after flash.
  7. Build data tars to disk, not BytesIO. Lucy has ~761MB free RAM — compressing 2GB in memory OOMs.

Next Steps (when resuming)

Immediate

  1. Test adacam-ssh-fix-v5.mender on Unit 2
    • Flash via USB
    • After reboot: plug in any USB drive
    • Check if sshd_config overlay is cleared + SSH opens
    • If SSH opens: run liberate.sh v0.4

If v5 fails

  • UART access as last resort (physical header on PCB, unknown pinout — no public schematics found)
  • Consider Hivemapper community forums / Discord for UART pinout info

After SSH is restored (Unit 2)

  • Run liberate.sh v0.4 (safe — /data-only writes, usb-updater preserved, no SSH hardening)
  • Verify adacam services install correctly

Long-term (Path A — Custom Firmware Image)

  • Mount clean system.img
  • Bake in: adacam services, correct sshd_config, authorized_keys, modified usb-updater
  • Generate Mender artifact signing keypair
  • Bake artifact_verify_key into custom image
  • Sign all future artifacts with our key (locks out Hivemapper OTA permanently)
  • Retire liberate.sh

Repository

  • Gitea: http://192.168.0.5:3001/Sulkta-Coop/adacam
  • liberate.sh current version: v0.4 (commit bddc1507)
  • API token: 33a9eb57b58c262f4434c12028bc3a30b1ff7021