Skip to content

Flash a module from the card

This is the argument behind Flash a module from the card, with the measurements that back it. The feature page is the short version.

Writing an operating system to a compute module means moving about two gigabytes. Doing it from a browser means moving those bytes twice — up to the board and then onto the module — over a management network, with an interface holding the upload open the whole time. Doing it from the board's own SD card means moving them once.

tpi flash --local has read images off that card for as long as the fork has existed. What the interface could not do is see what was on it. The field was free text labelled "File (remote or local)", the operator had to know the path, and a wrong one was reported as a failure some minutes after the most destructive button on the board had been pressed.

What the daemon answers

GET /api/bmc/sdcard/files lists the card. Every entry carries its path, its size, whether it can be written to a module, and — when it cannot — why not.

Entries on bmc-1 16
Flashable 2
Refused, with a reason 14

The reasons are the point. a directory. BMC firmware, not a node image — use the firmware page. An operator who cannot find their image wants to see it listed and told why it was rejected, not to wonder where it went.

The interface never decides what is flashable

Every row renders flashable and reason exactly as the board sends them. There is no filename check in the interface, deliberately.

An interface that guessed would agree with the daemon only until one of them changed, and the disagreement would not show up as a mismatch: it would show up as a flash that the operator started and the board refused, minutes later, after they had already committed. The interface's job here is to show what the board decided, not to have an opinion.

The card's free space is on the dialog

GET /api/bmc/sdcard reports capacity, and the picker says how much is left before anything is chosen. A full card is a normal outcome on a 32 GB card that also holds staged firmware, and it should be visible before a transfer starts rather than after it fails.

Two bugs this found on the way

The install button threw before sending anything. Both flash forms read form.elements.namedItem("file-url") — a field neither had had for several releases. namedItem answers null, reading .value off null throws, and the handler died inside the confirmation modal. Pressing Install did nothing at all, every time, and the only trace was a TypeError in the browser console. The firmware page's candidate list is the path people actually use, which is why nobody reported it.

The capacity endpoint answers an array of one. The document says minItems: 1, maxItems: 1 and the board agrees. Read as an object it yields an undefined free, which reaches the byte formatter and throws "Invalid number" — blanking the whole tab behind an error boundary, with the cause three layers from the message.

Both were found by driving a browser against a real board rather than by reading types, which is the only reason either is in this release.

What this does not do yet

The checksum is not computed on the board. Doing it means reading a multi-gigabyte file on a machine with about 87 MB of usable memory, so it has to be on demand, reported as progress, and cached against size and modification time — none of which the daemon offers yet. Uploading, renaming and moving files on the card are not here either. Both are tracked in SQU-198.