feat(fleetnode): pairing foundation (proto, node pairer, SQL guards)#388
Draft
ankitgoswami wants to merge 3 commits into
Draft
feat(fleetnode): pairing foundation (proto, node pairer, SQL guards)#388ankitgoswami wants to merge 3 commits into
ankitgoswami wants to merge 3 commits into
Conversation
Wire shapes for pairing fleet-node-discovered miners: an AgentCommand oneof carried in ControlCommand.payload (discover today, pair next), FleetNodePairRequest/Target, the ReportPairedDevices gateway RPC with per-device FleetNodePairResult/PairOutcome, and the operator-facing ListFleetNodeDiscoveredDevices and PairDiscoveredDevicesOnFleetNode admin RPCs. Additive only; existing handlers return Unimplemented for the new RPCs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The node now decodes an AgentCommand from ControlCommand.payload and dispatches discover or pair. Discovery migrates into the envelope (server wraps in DiscoverOnFleetNode, node unwraps) in this commit. A new pluginPairer authenticates a batch of targets on the node's local plugins: asymmetric drivers use the node's own miner-signing key, basic-auth drivers use operator-supplied or plugin-default credentials, and per-device outcomes (PAIRED / AUTH_NEEDED / AUTH_FAILED / ERROR) stream back via ReportPairedDevices with PARTIAL semantics. A cross-check test pins the node-derived public key to token.Service.ExtractPublicKeyFromPrivateKey. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… non-node devices Adds ListFleetNodeDiscoveredDevices (the inverse of GetActiveUnpairedDiscoveredDevices, which excludes fleet-node rows) so operators can enumerate pair candidates, including AUTHENTICATION_NEEDED rows for retry. Refines the cloud-dial guards to mean PAIRED AND NOT EXISTS(fleet_node_device): a fleet-node-paired device will also be device_pairing=PAIRED (so it reads as paired in the UI) but is node-dialed, so DeviceHasActiveCloudPairing, the discovery promotion guard (same-node carve-out), and the miner_service dial/credential fetch all exclude node-owned devices. Display and command-selection queries are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment on lines
+3
to
+5
| import ( | ||
| "context" | ||
| "crypto/ed25519" |
🔐 Codex Security Review
Review SummaryOverall Risk: HIGH Findings[HIGH] Pairing Credentials Are Not Redacted From Request Logging
[HIGH] New RPCs Are Mounted Via Generated Stubs But Not Implemented
[HIGH] New Procedures Are Missing From Auth/RBAC Classification
[MEDIUM] ControlCommand Payload Format Is Changed Without Compatibility Fallback
[MEDIUM] Pair Targets Are Not Validated Before Plugin Execution
NotesNo SQL injection, command injection, cryptostealing/pool hijack, or hardcoded wallet/pool redirection was evident in the changed hunks I reviewed. Generated by Codex Security Review | |
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.
Summary
Foundation for pairing miners discovered via fleet nodes. A fleet node can already discover miners (#235), but those
discovered_devicerows are a dead end: the cloudPairpath refuses fleet-node-reported devices (it has no route to a LAN behind a node), andPairDeviceToFleetNodeneeds an already-existingdevicerow that these never get. Per RFC 0001 the node owns miner I/O and holds the per-node miner-signing key, so pairing must happen on the node, orchestrated over the existingControlStream.This PR lands the safe, tested scaffolding. It is behavior-preserving and inert: discovery keeps working, and nothing new is reachable until the operator RPC lands.
What is in here
AgentCommandoneof carried inControlCommand.payload(discover today,pairnext),FleetNodePairRequest/FleetNodePairTarget, theReportPairedDevicesgateway RPC with per-deviceFleetNodePairResult/PairOutcome, and the operator-facingListFleetNodeDiscoveredDevicesandPairDiscoveredDevicesOnFleetNodeadmin RPCs. Existing handlers return Unimplemented for the new RPCs.handleCommanddecodesAgentCommandand dispatches discover or pair. A newpluginPairerauthenticates a batch of targets on the node's local plugins: asymmetric drivers (Proto) use the node's own miner-signing key, basic-auth drivers (Antminer) use operator-supplied or plugin-default credentials, and per-device outcomes stream back viaReportPairedDeviceswith bounded fan-out and PARTIAL semantics, mirroring discovery.ListFleetNodeDiscoveredDevices(inverse of the discovery listing, which excludes fleet-node rows; surfaces AUTHENTICATION_NEEDED for retry) and the cloud-dial guard refinements (see below).Two things reviewers should know
DiscoverRequestto anAgentCommandenvelope insideControlCommand.payload. The server-wrap (DiscoverOnFleetNode) and node-unwrap ship together here, so there is no skew within a deploy; node and server are the same binary and this is pre-release.device_pairingmodel. A fleet-node-paired device will bedevice_pairing.pairing_status = PAIRED(so it reads as paired in the UI and is command-targetable), with the transport dimension expressed byfleet_node_devicemembership. "Cloud-dialed" is therefore refined toPAIRED AND NOT EXISTS(fleet_node_device):DeviceHasActiveCloudPairing, the discovery promotion guard (same-node carve-out so a node can still refresh its own devices), and theminer_servicedial/credential fetch all exclude node-owned devices. Display and command-selection queries are unchanged. These refinements are dormant until PR4 creates node-paired data.Coordination
Aligned with the sibling effort "send commands to miners paired via fleet nodes": the
AgentCommandenvelope is shared (it adds aminer_commandarm), and thedevice_pairing= PAIRED decision resolves that effort's open question about whetherAllDevicesincludes node devices (it does; transport routes viafleet_node_device). Theminer_servicedial exclusion is the seam that effort fills with node routing.Testing
Node tests pass with
-race; control-registry, admin-handler (including discovery dispatch through the new envelope), and fleetnode domain tests pass against the live test DB. New tests cover the node pair dispatch and outcomes, the miner-signing key cross-check, the listing query, and the refined guards under the new PAIRED-and-node-bound model.Still to come
Per-device persistence + registry pair-result routing, the streaming operator RPC that makes pairing reachable, and an end-to-end CLI demo plus fake-rig fixtures.
🤖 Generated with Claude Code