Files
roro9stack/site/content/dev/debug/debug-builds.md
T
twislaandClaude Sonnet 5.5 3b4100dc6a
CI / build (pull_request) Successful in 8m38s
Site / build (pull_request) Successful in 9s
Site: the developer docs (phase 4), with the Debug Builds and the Debug Console first
/dev/ has Debug Builds and the Debug Console (builds and the token, the
console and its protocol, files and screenshots, driving the UI, crashes and
Safe Mode, the command reference), Build, test and release (including how an
update works), the architecture decisions and the milestone plans.

Generated from the repository by site/tools/gen_dev_docs.py: the ADRs, the
milestones, the README's sections, and the command reference, read from the
firmware's own `help` text. The pages are committed (Zola cannot read outside
its folder); the Site workflow checks they are current, and now also runs
when src/main.cpp changes. M0, M1 and CONTEXT.md are not published.
README: the gnss commands that the table lacked.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-06 21:25:33 +02:00

4.1 KiB

+++ title = "Debug Builds" description = "What a Debug Build is, how to build and flash one, how its token works, and why you should keep one in the fallback slot." weight = 1 [extra] tag = "Start here" +++

What it is

A Debug Build is built from the same source as a release, with the cardputer-adv-debug environment (it extends cardputer-adv in platformio.ini) and -DRORO_DEBUG. Differences:

  • the Debug Console on TCP 2323 (next page);
  • extra commands, only meant for testing: crash on purpose, fake an installed version, damage a download, inject a LoRa packet, fill a folder with files (see the command reference);
  • the version ends in +debug (scripts/version.py) wherever the version shows: Settings, info, the Update Service. A +debug version compares equal to its release counterpart, so going between the two is never refused as a downgrade.

It is compiled out of release builds, not switched off by a setting. A console that runs commands, presses keys and reboots the device is a remote control; in a release build nothing listens and the code is not there.

Build and flash one

Everything runs in Docker (see Build, test and release). Over USB:

scripts/flash.sh --debug              # builds cardputer-adv-debug, uploads, opens the serial monitor

Once a Debug Build is on the device, every later one can go over Wi-Fi, with no cable:

export RORO_OTA_HOST=10.39.39.12      # the address Settings > Firmware shows
scripts/flash.sh --debug --ota        # builds, signs and pushes; the device installs and restarts

The device's address is on Settings → Firmware ("Push to", which also gives port 3232, the update port). The Debug Console is on the same address, port 2323. See Flash and update for the update side.

The token

The console asks for a secret first. It is 128 random bits, made the first time any build runs (scripts/_docker.sh), kept in ~/.config/roro9stack/debug-token next to the update-signing key, and passed into the build container as RORO_DEBUG_TOKEN. scripts/debug_flags.py compiles it into the firmware, and refuses to build a Debug Build without one, rather than fall back on a default.

  • It is never committed. Releases have no console, so nothing of it is published: CI builds a Debug Build on a pull request to prove it still compiles, but never publishes it, because each one carries its builder's token.
  • It guards against the network, not against someone who holds the device: anyone with USB access can flash anything anyway (ADR 0003).
  • To change it, delete the file and build again; the new token goes into the next Debug Build you flash.

Keep a Debug Build in the other slot

The device has two app slots, so an update never overwrites the running firmware. If a new firmware crashes during Probation, the device goes back to the previous one, whatever that is. As long as you develop on Debug Builds, the firmware a crash falls back to has the console too, so a bad update never costs you remote access. Pushing a release build over a Debug Build leaves the Debug Build in the other slot until the next update overwrites it.

So the habit is: develop on Debug Builds, release by tag, and think twice before pushing a release over the only Debug Build you have.

Releases and Debug Builds

  • A Debug Build shows the latest release in Settings → Firmware but never installs it: that would replace the console with a release that has none. Update a Debug Build from the PC with scripts/flash.sh --debug --ota.
  • A Debug Build still checks and lists releases, which is useful for testing the update path: update pretend makes a released version count as newer (see Drive the UI).
  • Safe Mode (after 3 crashes in a row) keeps the Debug Console running, so a crash loop is something you fix remotely: see Crashes and Safe Mode.

The reasoning is in ADR 0004.