feat(fleet): out-of-band power control, a fleet read model, and a terminal interface - #4
Open
wamxx wants to merge 2 commits into
Open
feat(fleet): out-of-band power control, a fleet read model, and a terminal interface#4wamxx wants to merge 2 commits into
wamxx wants to merge 2 commits into
Conversation
…minal interface Implements `plans/fleet-control.md`. Three things exist now that did not: a way to tell a machine to network-boot and power on, a model of the fleet that is a command before it is a screen, and a terminal interface that renders it. **Out-of-band control is off unless `RESCRIPTUM_CONTROLLERS_FILE` names a file.** Unset, there are no credentials, no outbound connections and no reachable code path — the rule `RESCRIPTUM_MEDIA_DIR` and `RESCRIPTUM_INSTALLED_TOKEN` already follow. Redfish only, through `curl`, because there is no TLS in this binary and a megabyte of rustls to double a 2.8 MB binary for one feature is not a trade worth making. Anything Redfish cannot reach is reached by `kind = "command"`: argv only, never a shell, nothing from a request, and a deadline. **`install` is mostly refusing.** Powering on a machine that boots into a broken answer leaves an installer at a prompt in a rack, so it renders every format the machine resolves for first, then checks policy — differently in each unclaimed mode, because one case is dangerous and the other merely useless — then re-arms from the `installed-` archive, arms a one-time network boot and reads it back, and finally powers on *or restarts*, decided by reading the power state. **Two findings from the hardware, verified rather than assumed.** A Redfish `PATCH` can answer `204 No Content` and do nothing: PiKVM's does, which is why the boot override is read back rather than trusted. And `@odata.id` is inconsistent with the path it is served from on that same device, so no URL is ever followed out of a response body — the id is taken and the URL composed. **The guard moved out of `admin`.** "A write cannot leave the answer set broken" is not an HTTP property; `admin` now maps its outcome onto a status code, and the admin suite passed unchanged across the split, which is what proves it was a refactor. **The read model is a command first.** `status`, `machines`, `groups`, each with `--json`, plus `render <id> --format <ext>` which had no first-class form. Building it as a screen would have made it untestable, unscriptable, and gone if the interface were never finished. `GET /fleet` serves the same bytes, asserted. **The interface keeps its state in `src/tui/mod.rs`, which depends on no terminal library** and is tested in every build, `--no-default-features` included. A key press returns an `Action`; the loop performs it between frames, so one unreachable BMC cannot freeze a screen. `tui` is not in `default`, and CI asserts the default dependency graph does not reach ratatui — on the *output* of `cargo tree`, never its exit code, which exits 0 for an optional dependency that is off. Two log changes, both logging rather than instrumentation: `Resolution::how` reports the extension instead of the family, so a boot script and an answer document are no longer the same event; and a 404 names the machine that asked, which a Proxmox POST states only in its body. Cost on armv7 (floor 2.17): +199,408 bytes (+6.9%) and +45 locked packages, with the feature on. Zero with it off. 619 -> 741 tests. Documentation in both languages, links checked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KQnAk5r4fLKcWuAwUY6Pa6
`cargo audit` reads `Cargo.lock`, which carries the whole dependency graph whatever the feature flags say — so ratatui's tree blocks CI for a binary that links none of it. The plan anticipated this and set the rule: ignore an advisory reachable **only** through the `tui` graph, with the reason written beside it. Verified rather than assumed: `lru` and `paste` are absent from the default graph, and `cargo tree -i` shows both arriving solely through ratatui. Two of the three are unsoundness rather than unmaintainedness, so the comment says to revisit when ratatui moves off `lru 0.12`. They stay ignored only while nothing shipped links them — which the gates job asserts separately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KQnAk5r4fLKcWuAwUY6Pa6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
plans/fleet-control.md— all fourteen phases. Not for merging yet: openedso the design can be read before it lands.
What this adds
power list/status/on/off/pxe,install <id>— Redfish throughcurl, or a script you supplystatus,machines,groups, each with--json;render <id> --format <ext>rescriptum tui, and--remote URLagainst a deployment's admin APIGET /fleet, asserted byte-identical tomachines --jsonOff unless configured. No
RESCRIPTUM_CONTROLLERS_FILE, no credentials, no outboundconnections, no reachable code path.
tuiis not indefault, and CI asserts the defaultdependency graph does not reach ratatui.
The parts worth reviewing
installis mostly refusing. Powering on a machine that boots into a broken answerleaves an installer at a prompt in a rack. It renders every format the machine resolves
for, checks policy — differently in each unclaimed mode, because one case is dangerous
and the other merely useless — re-arms from the
installed-archive, arms a one-timenetwork boot and reads it back, then powers on or restarts depending on the state it
reads.
Two vendor behaviours, verified in source rather than assumed. A Redfish
PATCHcananswer
204 No Contentand do nothing — PiKVM's does, while still reporting the overridedisabled, which is why the read-back is mandatory rather than tidy. And its
@odata.iddisagrees with the path it is served from, so no URL is ever followed out of a response
body: the id is taken and the URL composed from
base.The guard moved out of
admin. "A write cannot leave the answer set broken" is not anHTTP property.
adminnow maps the outcome onto a status code, and the admin suitepassed unchanged across the split — which is what proves it was a refactor.
The read model is a command before it is a screen. Built as a screen it would be
untestable, unscriptable, and gone if the interface were never finished. The interface's
own state lives in
src/tui/mod.rs, depends on no terminal library, and is tested in everybuild including
--no-default-features. A key press returns anAction; the loop performsit between frames, so one unreachable BMC cannot freeze a screen.
Cost
armv7 (floor 2.17), with the feature on: +199,408 bytes (+6.9%) and +45 locked
packages. Zero with it off.
Cargo.lockcarries the graph regardless, so an advisoryreachable only through it gets an
--ignorewith that reason written beside it.Numbers
619 → 741 tests.
fmt,clippy --all-targets --all-features -D warnings, and all fourfeature combinations green — including
tuiwithoutboot, which--all-featuresdoesnot cover and which now has its own CI step. Documentation in both languages, links
checked.
Known gap
tui --remoteshows the three screens the admin API actually has — machines, groups,problems. The rest say "local only" rather than growing a read API with its own
auth-exposed surface. It powers nothing, and that is refused in the state machine so no
screen can forget it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01KQnAk5r4fLKcWuAwUY6Pa6