@ruvnet/ruview 0.6.0 turns the harness into a complete npm operator
surface: one policy-checked tool registry behind the CLI, the MCP server,
and a new typed SDK.
- SDK (ADR-369): `@ruvnet/ruview/sdk` createRuView() with doctor,
firmware.{ports,plan,flash,monitor}, training.{plan,run,gate,calibrate},
kernel.{selfTest,load}, guidance/claimCheck/verify/spaces; strict mode
throws RuViewError; TypeScript declarations in src/sdk.d.ts.
- Firmware (ADR-370): cross-platform `python -m esptool` flashing replaces
the Windows-only stub. SHA256SUMS verification (mismatch always fatal),
bundle containment, variant/partition checks, argv-only command, chip_id
preflight that refuses a mismatched chip, boot-log capture;
hardwareValidated only when CSI callbacks appear without a panic.
Read-only ruview_firmware_plan / ruview_firmware_ports.
- Training (ADR-371): ruview_train / ruview_train_plan run pose-smoke,
pose (MM-Fi), or room training from the trusted checkout with in-repo
paths only, logs/checkpoints under v2/target/ruview-train, and libtorch
remedies. ruview_train_gate enforces mean-pose baseline, leakage-free
splits, subject/time disjointness, and evidence tagging.
- Doctor (ADR-372): structured pass/warn/fail/skip checks with remedies
across runtime, integrity, hosts, repo, Rust, Python/esptool, firmware
bundles, serial, sensing server, and the compute kernel; --json/--group;
hardware probe and network checks are CLI/SDK-only.
- Plan tools cannot take `confirm`; acting tools keep grant+confirm over
MCP. Policy files and TOOL_POLICY are drift-tested.
- Unpacked budget 160 KiB → 224 KiB in both npm workflows; CI tarball smoke
now checks the SDK and the training gate.
Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_015H55jmprfz4oHWXSFbX5rf
3.9 KiB
ADR-370: Cross-platform, verified ESP32 firmware flashing from npm
- Status: proposed
- Date: 2026-09-30
- Deciders: RuView maintainers; acceptance pending
- Tags: firmware, esp32, esptool, supply-chain, hardware-evidence
- Related: ADR-028 (capability audit / witness), ADR-369, firmware
README.md§0 and §2
Context
The documented flash procedure is four esptool write_flash offsets. It is
typed by hand, and a wrong chip, wrong flash size, or tampered image can brick
a board or silently run the wrong firmware. The previous ruview_node_flash
was Windows-only and never flashed. Project rules also say that a successful
build or write is not hardware validation; that requires a captured
boot/runtime log from real silicon.
Decision
src/firmware.js implements plan → verify → preflight → write → evidence:
-
Variants:
s3-8mb→esp32s3/ 8MBs3-4mb→esp32s3/ 4MB; prefers*-4mb.binimagesc6→esp32c6/ 4MB
Offsets follow the shipped partition tables: bootloader
0x0, partition table0x8000, optional OTA data0xf000, app0x20000. NVS is never written, so WiFi and node configuration survive. -
Bundle verification. A bundle is an extracted release flash bundle,
release_bins/<variant>, or an ESP-IDFbuild/directory.- Every image must match
SHA256SUMSorSHA256SUMS.txt. A mismatch is always fatal. - Missing checksums are refused unless
allow_unverifiedis set, which is for local builds only. - Images must be regular, non-empty files of at most 16 MiB, inside the bundle; symlinks that escape the bundle are refused.
- An 8 MB variant refuses a 4 MB partition table.
- A bundle without OTA data produces an explicit warning (a node on
ota_1keeps bootingota_1).
- Every image must match
-
Argv, not shell.
- The port must match
COM<n>or/dev/(tty|cu)*. - Baud comes from a fixed set.
- The command is
python -m esptool --chip … --port … --baud … write_flash --flash_mode dio --flash_size … <offset> <file>…. The underscore forms work in both esptool 4.x and 5.x.
- The port must match
-
Chip preflight.
esptool chip_idmust report the variant's chip. The parser covers the 4.x "Chip is" and 5.x "Chip type:" / "Detecting chip type…" formats. A mismatch or missing response stops the flow before any write. -
Evidence. After a write, a read-only pyserial capture (default 15 s, port and duration passed as argv) is summarized: line count, CSI callbacks, MGMT+DATA upgrade, panic markers, firmware version.
hardwareValidated: trueonly when CSI callbacks appear with no panic; the result'sevidenceis thenMEASUREDwith a boot-log reason.- Otherwise the result states the board is not validated.
-
Authority. Without
confirmthe result is a plan. MCP needs thehardware-writegrant plusconfirm.ruview_firmware_planandruview_firmware_portsare read-only. -
Provisioning stays out. WiFi credentials remain in
provision.py, so they never enter harness arguments, logs, or MCP transcripts.
Evidence
npx @ruvnet/ruview doctor --group firmwareverified all four checked-inrelease_binsbundles (c6-adr110, c6-onboarding, s3-adr110, s3-fair-adr110) against theirSHA256SUMS.txtin this change's development container.- The write and preflight paths are covered by tests with injected fake hardware: mismatch refusal, probe failure, flash failure, boot-log validation, and a silent log that yields no validation.
- No real board was flashed during this change, so real-silicon validation is outstanding.
Consequences
- Operators on any OS get one verified command. Agents can review plans without hardware authority.
- The workflow depends on Python, esptool, and pyserial.
doctorreports each one with a fix. - Follow-up: an ADR-028 witness record could store the boot-log summary and image digests.