From 0db886b1ef9ba9101a4d3723bbc8133725dd5003 Mon Sep 17 00:00:00 2001 From: BootIntel Agent Date: Mon, 28 Sep 2026 06:34:36 +0000 Subject: [PATCH] Release 0.8.0: read the boot chain on the bench, offline Two halves of one workflow, both merged and green: take the U-Boot prompt on a board in front of you (#18), and turn what you pull off it into a verdict (#17). Everything runs on the operator's machine, which is the only version a consultancy under an NDA can use. MINOR rather than PATCH under the policy at the top of CHANGELOG.md: two new subcommands' worth of surface, no behaviour changed for anyone who does not use them. `bootintel-detectors` is pinned by exact version in crates/cli/Cargo.toml, so that bump is part of a release rather than something cargo derives. It fails `cargo update -w` loudly when missed, so it cannot reach a release quietly, but it is the step that gets forgotten; docs/releasing.md now says so in step 1. 349 tests pass, clippy clean under -D warnings, rustfmt clean. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 74 ++++++++++++++++++++++++++++++++++++++++++- Cargo.lock | 4 +-- Cargo.toml | 2 +- crates/cli/Cargo.toml | 2 +- docs/releasing.md | 9 ++++-- 5 files changed, 84 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f125d9..59e7015 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,77 @@ All notable changes to bootintel-cli are documented here. Format follows [Keep a ## [Unreleased] +## [0.8.0] — 2026-09-28 — read the boot chain on the bench, offline + +Two halves of one workflow: take the U-Boot prompt on a board in front of you, +and turn the environment you pull off it into a verdict. Both run entirely on +your machine. A consultancy under an NDA cannot upload a client's capture, and +a U-Boot environment is the most sensitive thing in one, so needing a server to +interpret it would have put this out of reach of the people it is for. + +### Added +- **`bootintel verdict `** reads a `printenv` dump taken at the prompt + and reports what the boot chain permits: whether autoboot is interruptible, + whether images are verified, whether a netboot path is pre-configured, + whether `bootargs` can be rewritten, and whether `saveenv` makes any of it + stick. Text, `--json`, and `--gate-exposed` for CI. + + Every entry names the variable it was read from, because a consultant has to + defend the answer in a client report rather than quote a tool. Absence is + reported as `unknown`, never as `hardened`: U-Boot prints only what is set, so + a missing `bootdelay` means the compiled-in default applies and cannot be read + from a capture. + + Exit codes carry the same discipline: `2` for an empty capture, `3` for + content with no session in it. "Could not assess" must never look like + "nothing is wrong", which is the failure mode that makes a CI gate worse than + no gate. + + `--json` uses the server's key names (`uboot_shell`, `uboot_env`, + `boot_chain_verdict`) so a consumer can move between this and `scan --api` + without remapping anything. + +- **`bootintel analyze --interrupt-autoboot`** interrupts autoboot on + connect, takes the prompt, runs the read-only set `printenv`, `bdinfo`, + `mtdparts`, prints the verdict, and hands the terminal back. + + It hammers the interrupt key from the moment the port opens rather than + waiting to see a countdown. U-Boot's autoboot delay is a loop around + `tstc()`, and `bootdelay=0` means that check happens exactly once; by the time + "Hit any key to stop autoboot" has crossed the wire and been recognised, the + board has already looked. What catches a one-shot check is the byte already + sitting in the UART's receive register, so **power-cycle the board after the + tool says it is hammering**. `--reset-line dtr|rts` pulses a modem line so + the reset instant is the tool's rather than a human's, on the adapters wired + for it. + + The hammer never sends CR or LF, and a key containing either is refused: + hammered bytes accumulate in U-Boot's line buffer, and a newline would + execute whatever they spell on hardware that is not yours. The default key is + a space, a bare CR flushes the accumulated bytes before anything is typed, + and every byte the tool sends is announced so a client transcript shows which + bytes were the tool's. `--at-prompt` replaces the command set entirely. + + A `#` prompt after the kernel handoff is treated as a Linux shell and + ignored, because typing `printenv` into a root shell would have produced a + confident, wrong verdict. A prompt that does not answer re-arms rather than + abandoning the attempt. A window that is never caught is reported, with the + four things worth checking, rather than exiting quietly. + + Not wired into `--tui`; that combination is refused rather than silently + ignored. + +### Changed +- The verdict rules now exist in two places, here and in the bootintel.com + engine, which is the drift problem the cross-implementation detector parity + work fixed. Neither side is the reference: + `crates/detectors/tests/fixtures/boot_chain/expect.txt` is, and the engine + repo holds a byte-identical copy that its own test asserts against. Two of + the three fixtures are real boards whose vendor prompt (`RTL8672 #`) the + engine's stricter pattern skips, so the environment is only provable + retroactively from the `Environment size:` line: the path a tokeniser rewrite + breaks silently. + ## [0.7.0] — 2026-09-27 — the gate survives a pipe, and history is opt-in Renumbered from 0.6.1. Making scan history opt in changes a default, and a @@ -579,7 +650,8 @@ Initial release. All six subcommands live; five branch-based milestones (M1-M5) - PDF report download subcommand — server-side endpoint exists but no client-side wrapper yet. - Windows support — the Rust code compiles for Windows and the release workflow builds it, but install.sh doesn't handle Windows yet (`.ps1` installer is a follow-up). -[Unreleased]: https://github.com/bootintel/cli/compare/cli-v0.7.0...HEAD +[Unreleased]: https://github.com/bootintel/cli/compare/cli-v0.8.0...HEAD +[0.8.0]: https://github.com/bootintel/cli/releases/tag/cli-v0.8.0 [0.7.0]: https://github.com/bootintel/cli/releases/tag/cli-v0.7.0 [0.6.1]: https://github.com/bootintel/cli/releases/tag/cli-v0.6.1 [0.6.0]: https://github.com/bootintel/cli/releases/tag/cli-v0.6.0 diff --git a/Cargo.lock b/Cargo.lock index 0b85fb2..be3aba1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,7 +131,7 @@ checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" [[package]] name = "bootintel" -version = "0.7.0" +version = "0.8.0" dependencies = [ "anyhow", "arboard", @@ -154,7 +154,7 @@ dependencies = [ [[package]] name = "bootintel-detectors" -version = "0.7.0" +version = "0.8.0" dependencies = [ "regex", ] diff --git a/Cargo.toml b/Cargo.toml index f6d7cd7..c330eea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ members = ["crates/detectors", "crates/cli"] resolver = "2" [workspace.package] -version = "0.7.0" +version = "0.8.0" edition = "2021" rust-version = "1.90" license = "Apache-2.0" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 8575d62..0d96f3b 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -24,7 +24,7 @@ path = "src/main.rs" # version when packaging for crates.io, which rejects a bare path dep. # This is what publish = false was working around; publishing # bootintel-detectors first makes the workaround unnecessary. -bootintel-detectors = { path = "../detectors", version = "0.7.0" } +bootintel-detectors = { path = "../detectors", version = "0.8.0" } clap = { version = "4.5", features = ["derive", "wrap_help", "env"] } # Shell-completion emitters for `bootintel completions `. Version # tracks the clap major/minor (4.5). No default features — we only use diff --git a/docs/releasing.md b/docs/releasing.md index 4df96c0..23d6df2 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -37,8 +37,13 @@ having no brew line at all. ## Cutting a release -1. Bump `version` in the workspace `Cargo.toml`, run `cargo update -w` so the - lockfile follows, and promote the `[Unreleased]` changelog section. +1. Bump `version` in the workspace `Cargo.toml` **and the `bootintel-detectors` + version in `crates/cli/Cargo.toml`**, which is pinned exactly because the + published binary crate has to name a version that exists on the index. Then + run `cargo update -w` so the lockfile follows, and promote the + `[Unreleased]` changelog section. Forgetting the second bump fails + `cargo update -w` immediately with "required by package bootintel", so it + cannot reach a release, but it is the step that gets missed. Versioning policy is documented at the top of `CHANGELOG.md`. The crate is pre-1.0, so the leading zero is the major component: a breaking change moves the minor, not the major.