Debug Console: a configurable token, so Debug Builds can be published #68

Closed
opened 2026-10-06 19:30:12 +00:00 by twisla · 1 comment
Owner

Today the Debug Console's token is compiled in: 128 random bits made on the builder's machine (scripts/_docker.sh), passed to the build as RORO_DEBUG_TOKEN and refused-without by scripts/debug_flags.py (ADR 0004). That is why Debug Builds are never published: each one carries its builder's secret, and a published build would hand it to everyone.

Idea: make the token a setting of the device, not of the build. Then a Debug Build contains no secret, CI can publish it next to the release (roro9stack-<version>+debug.ota), and anyone can install a Debug Build from the Install page or Settings → Firmware and get the console, rdbg.py, screenshots and crash decoding without building the firmware.

Questions for a design round (Qnn):

  • Where is the token made? Options: (a) generated on the device on first use and shown on its screen (Settings → Debug Console), typed or pasted into rdbg.py; (b) set by the user in Settings; (c) derived from something per device. (a) is the safest default: no default token exists anywhere.
  • Is the console on by default? A published Debug Build must not listen until the owner switches it on in Settings, with a visible indicator in the Status Bar while it is on.
  • Storage: NVS, like the other settings. Never printed on the console itself; a way to regenerate it, which drops a connected client.
  • Client side: rdbg.py would read a token for each device (~/.config/roro9stack/… per node ID or address), or take --token, or ask. The existing per-builder token file would still work for a local build with a compiled-in default.
  • Brute force: 128 random bits are safe; a short human-chosen token is not. Keep the one-second delay on a wrong token, add a growing delay and a lockout, and refuse tokens shorter than N characters.
  • Plain text on the network: the stream is not encrypted (ADR 0004). A published build raises the stakes a little: does it need TLS or a pairing step, or is "same network, token on the screen" enough?
  • Publishing: CI builds the Debug Build already (and discards it). Publishing it means a release asset with a clear name, signed with the same key, and the site's Install page (and the device's own Check for updates) telling the two apart. A Debug Build still must not install a release by itself (Q171); what about a release installing a Debug Build?
  • Effect on ADR 0004: its "token never committed, a build refuses to compile without one" becomes "no token in the binary"; the fallback-slot advice stays.

Done when (to be refined): a Debug Build compiled with no token, console off until enabled in Settings, a token shown on screen and accepted by rdbg.py, the CI release carrying a signed Debug Build asset, ADR 0004 and the developer docs updated.

Today the Debug Console's token is **compiled in**: 128 random bits made on the builder's machine (`scripts/_docker.sh`), passed to the build as `RORO_DEBUG_TOKEN` and refused-without by `scripts/debug_flags.py` (ADR 0004). That is why **Debug Builds are never published**: each one carries its builder's secret, and a published build would hand it to everyone. Idea: make the token **a setting of the device**, not of the build. Then a Debug Build contains no secret, CI can publish it next to the release (`roro9stack-<version>+debug.ota`), and anyone can install a Debug Build from the Install page or Settings → Firmware and get the console, `rdbg.py`, screenshots and crash decoding without building the firmware. **Questions for a design round (Qnn):** - **Where is the token made?** Options: (a) generated on the device on first use and **shown on its screen** (Settings → Debug Console), typed or pasted into `rdbg.py`; (b) set by the user in Settings; (c) derived from something per device. (a) is the safest default: no default token exists anywhere. - **Is the console on by default?** A published Debug Build must not listen until the owner switches it on in Settings, with a visible indicator in the Status Bar while it is on. - **Storage:** NVS, like the other settings. Never printed on the console itself; a way to regenerate it, which drops a connected client. - **Client side:** `rdbg.py` would read a token for each device (`~/.config/roro9stack/…` per node ID or address), or take `--token`, or ask. The existing per-builder token file would still work for a local build with a compiled-in default. - **Brute force:** 128 random bits are safe; a short human-chosen token is not. Keep the one-second delay on a wrong token, add a growing delay and a lockout, and refuse tokens shorter than N characters. - **Plain text on the network:** the stream is not encrypted (ADR 0004). A published build raises the stakes a little: does it need TLS or a pairing step, or is "same network, token on the screen" enough? - **Publishing:** CI builds the Debug Build already (and discards it). Publishing it means a release asset with a clear name, signed with the same key, and the site's Install page (and the device's own Check for updates) telling the two apart. A Debug Build still must not install a release by itself (Q171); what about a release installing a Debug Build? - **Effect on ADR 0004:** its "token never committed, a build refuses to compile without one" becomes "no token in the binary"; the fallback-slot advice stays. **Done when (to be refined):** a Debug Build compiled with no token, console off until enabled in Settings, a token shown on screen and accepted by `rdbg.py`, the CI release carrying a signed Debug Build asset, ADR 0004 and the developer docs updated.
twisla added this to the R1 Releases milestone 2026-10-06 19:30:12 +00:00
Author
Owner

Done, though not as the title asks: instead of a configurable token so that Debug Builds could be published, there are no Debug Builds any more. Released in v0.12.0 (pull request #70, ADR 0010, decisions Q188 to Q195 in docs/milestones/R1.md).

  • One firmware, with the Debug Console and the test commands in it. Off by default; off, nothing listens and neither its task nor its buffer exists. 30 KB more flash and 88 bytes more static RAM than the release build was.
  • The token is made by the device and shown in Settings → Debug Console. The login is a challenge and an HMAC answer, so the token never crosses the network; five wrong answers close the console for a minute.
  • debug on, debug token <value> and debug token new over USB serial only; debug off [seconds] from anywhere. scripts/rdbg.py takes --token, $RORO_DEBUG_TOKEN or the file.

Checked on the device, the last two on the released v0.12.0 itself: off by default after an update; login; the pause after wrong tokens; Safe Mode with the console; 25 closings and reopenings; the setting and the token surviving an install of v0.12.0 from Gitea, by the device; and rdbg.py crash fetching the release's ELF and decoding a crash to its line.

Not checked: scripts/flash.sh --debug over USB (no device on USB), typing a token on the device, and the lockout's Toast on the screen.

Follow-ups filed separately: a shell on the device (#67) and one help key everywhere (#69).

Done, though not as the title asks: instead of a configurable token so that Debug Builds could be published, **there are no Debug Builds any more**. Released in **v0.12.0** (pull request #70, ADR 0010, decisions Q188 to Q195 in `docs/milestones/R1.md`). - One firmware, with the Debug Console and the test commands in it. Off by default; off, nothing listens and neither its task nor its buffer exists. 30 KB more flash and 88 bytes more static RAM than the release build was. - The token is made by the device and shown in Settings → Debug Console. The login is a challenge and an HMAC answer, so the token never crosses the network; five wrong answers close the console for a minute. - `debug on`, `debug token <value>` and `debug token new` over USB serial only; `debug off [seconds]` from anywhere. `scripts/rdbg.py` takes `--token`, `$RORO_DEBUG_TOKEN` or the file. Checked on the device, the last two on the released v0.12.0 itself: off by default after an update; login; the pause after wrong tokens; Safe Mode with the console; 25 closings and reopenings; the setting and the token surviving an install of v0.12.0 **from Gitea, by the device**; and `rdbg.py crash` fetching the release's ELF and decoding a crash to its line. Not checked: `scripts/flash.sh --debug` over USB (no device on USB), typing a token on the device, and the lockout's Toast on the screen. Follow-ups filed separately: a shell on the device (#67) and one help key everywhere (#69).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: twisla/roro9stack#68