feat(fleetnode): route firmware updates through fleet nodes#598
Conversation
🔐 Codex Security Review
Review SummaryOverall Risk: MEDIUM Findings[MEDIUM] Firmware download can consume the entire node-side update budget
NotesI did not find auth bypasses, SQL injection, command injection, pool hijacking, hardcoded payout/wallet substitutions, or protobuf wire-format breakage in the changed hunks. The firmware artifact path includes purpose, size, in-flight command, metadata, and checksum checks, which addresses the main trust-boundary concerns in this diff. Generated by Codex Security Review | |
There was a problem hiding this comment.
Pull request overview
This PR routes firmware updates for fleet-node–paired miners through the fleet node control plane by treating the uploaded firmware file as a command artifact payload (served directly from firmware storage), propagating firmware identity/metadata (ID + SHA-256 + path) through the SDK and command execution path, and adding node-side download/verify/temp-file installation that reuses the existing plugin firmware update flow.
Changes:
- Added a fleet-node
FirmwareUpdateActionthat carries aCommandArtifactReffor firmware payload downloads. - Extended firmware file handling to expose ID/SHA-256/path metadata and threaded that metadata through SDK/plugin bridges and command execution.
- Implemented fleet-node firmware artifact download admission + streaming from firmware storage, plus node-side download/validation/temp-file lifecycle and dispatch from remote-node miners with artifact expectations.
Reviewed changes
Copilot reviewed 19 out of 24 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| server/sdk/v1/python/proto_fleet_sdk/generated/pb/driver_pb2.pyi | Generated Python SDK typings updated for firmware file metadata fields. |
| server/sdk/v1/python/proto_fleet_sdk/generated/pb/driver_pb2.py | Generated Python SDK protobuf updated for firmware file metadata fields. |
| server/sdk/v1/plugin.go | Populates firmware ID/SHA-256 when bridging UpdateFirmware between gRPC and SDK types. |
| server/sdk/v1/plugin_test.go | Adds coverage to ensure UpdateFirmware preserves firmware metadata through the bridge. |
| server/sdk/v1/pb/driver.proto | Extends FirmwareFileInfo with id and sha256. |
| server/sdk/v1/interface.go | Extends FirmwareFile struct with ID and SHA256. |
| server/internal/infrastructure/files/service.go | Adds an ID→checksum index to avoid repeated checksum work and support metadata lookups. |
| server/internal/infrastructure/files/firmware.go | Adds OpenFirmwareFileWithInfo (returns ID/size/SHA/path), checksum caching, and index maintenance on save/delete/init. |
| server/internal/infrastructure/files/firmware_test.go | Adds tests for OpenFirmwareFileWithInfo, checksum index rebuild, validation, and delete cleanup. |
| server/internal/handlers/fleetnode/gateway/handler.go | Serves firmware payload downloads from firmware storage when purpose is FIRMWARE_PAYLOAD. |
| server/internal/handlers/fleetnode/gateway/handler_controlstream_test.go | Wires file service into the harness for artifact-related tests. |
| server/internal/handlers/fleetnode/gateway/handler_artifact_test.go | Adds test ensuring firmware payload downloads stream from firmware storage under in-flight expectations. |
| server/internal/domain/miner/remotenode/miner.go | Adds artifact-aware dispatch path and implements remote firmware updates via fleet-node commands + download expectations. |
| server/internal/domain/miner/remotenode/miner_test.go | Adds test asserting firmware update dispatch includes artifact ref + expectations. |
| server/internal/domain/command/execution_service.go | Uses OpenFirmwareFileWithInfo and passes ID/SHA/path through sdk.FirmwareFile during execution. |
| server/internal/domain/command/execution_service_test.go | Adds test asserting firmware file metadata is passed into miner firmware update calls. |
| server/cmd/fleetnode/run.go | Prepares a state-dir-scoped firmware temp root at daemon startup for downloads. |
| server/cmd/fleetnode/minercommand.go | Adds firmware command action timeout, download/verify/temp-file install flow, and a download limiter. |
| server/cmd/fleetnode/minercommand_test.go | Adds tests for firmware timeout selection, download/install success, checksum mismatch, oversized artifacts, limiter behavior, and temp dir cleanup. |
| server/cmd/fleetnode/control.go | Passes the gateway client into miner-command handling so firmware downloads can be fetched. |
| proto/fleetnodegateway/v1/fleetnodegateway.proto | Adds FirmwareUpdateAction to the fleet-node gateway MinerCommand action set. |
| client/src/protoFleet/api/generated/fleetnodegateway/v1/fleetnodegateway_pb.ts | Generated TS client updated for the new firmware update action/message. |
40a9723 to
6932b8b
Compare
- Reserve firmware install polling time after fleet-node uploads
mcharles-square
left a comment
There was a problem hiding this comment.
Looks good @ankitgoswami! question no the flow - if one wants to only apply the fw update to a subset of miners connected to a node before running a full node update does that mean that the fw artifact needs to redownload to the node to target the remaining miners?
- Reserve firmware install polling time after fleet-node uploads
c2b6b25 to
ba10cac
Compare
|
@mcharles-square
|
Reviewable diff: +372/-49 across 11 files (excludes generated, test, and story files).
Summary
Operators can now firmware-update miners that are paired behind a fleet node without changing the existing firmware upload flow. Fleetd reuses the uploaded firmware file as the artifact source, issues a fleet-node miner command with a firmware artifact reference, and the node downloads, verifies, and passes a local temp file into the existing plugin firmware update path.
How it works
An operator queues a firmware update the same way they do today, using the existing firmware file ID in the command payload. During command execution, fleetd opens the firmware metadata, attaches the firmware ID, filename, size, SHA-256, and filesystem path to the SDK
FirmwareFile, and the remote-node miner adapter converts that into a fleet-nodeFirmwareUpdateActionplus a download expectation for the target miner.The fleet-node gateway admits the download only while the command is in flight. For
FIRMWARE_PAYLOADartifacts, it serves bytes directly from firmware storage instead of copying them into command-artifact storage. The fleet node downloads the artifact stream, validates the header, size, and SHA-256, writes the payload to a short-lived temp file, calls the local pluginFirmwareUpdate, then deletes the temp file after the plugin call returns.Areas of the code involved
proto/fleetnodegateway/v1FirmwareUpdateActioncarrying aCommandArtifactRef; generated Go/TS updatedserver/internal/domain/commandsdk.FirmwareFileserver/internal/domain/miner/remotenodeserver/internal/handlers/fleetnode/gatewayFIRMWARE_PAYLOADfrom firmware storage when an in-flight expectation matchesserver/cmd/fleetnodeserver/internal/infrastructure/filesserver/sdk/v1FirmwareFileand SDK plugin proto bridge carry firmware ID and SHA-256Key technical decisions & trade-offs
CommandSenderstable and add an optional artifact-aware sender interface to avoid churning existing fakes and command senders.Testing & validation
go test ./internal/infrastructure/files ./internal/domain/miner/remotenode ./sdk/v1 ./cmd/fleetnode -run 'TestOpenFirmwareFile|TestFindFirmwareFileByChecksum|TestDeleteFirmwareFile_RemovesFromChecksumIndex|TestMiner_FirmwareUpdate|TestDriverGRPCServer_UpdateFirmwarePreservesMetadata|TestMinerCommandActionTimeoutUsesFirmwareBudget|TestHandleMinerCommand_FirmwareUpdate'go test ./internal/handlers/fleetnode/gateway -run 'TestCommandArtifact|TestDownloadCommandArtifactServesFirmwarePayload'go test ./internal/domain/command -run TestExecuteCommandOnDevice_FirmwareUpdatePassesFileMetadatagolangci-lint run -c .golangci.yamlinserverjust gen-sdk-protosgit diff --checkjust lintwas also attempted from the repo root.buf lintcompleted, then the client lint step stopped because this worktree does not haveclient/node_modulesinstalled (eslint: command not found).