Skip to content

proto: discovery + pairing wire contracts#257

Merged
ankitgoswami merged 6 commits into
mainfrom
ankitg/discovery-pairing-proto
May 19, 2026
Merged

proto: discovery + pairing wire contracts#257
ankitgoswami merged 6 commits into
mainfrom
ankitg/discovery-pairing-proto

Conversation

@ankitgoswami

Copy link
Copy Markdown
Contributor

Summary

fleetnodegateway/v1

  • ReportDiscoveredDevices unary batch RPC, DiscoveredDeviceReport message (max_items=1024).
  • command_id field on ReportDiscoveredDevicesRequest (optional, max_len=128) so an agent reporting batches in response to a server-issued ControlCommand can echo the id, letting the server route batches to the operator stream waiting on DiscoverOnFleetNode.
  • ControlStream bidi already in the file (unchanged); listed here so reviewers know both halves of the wire are present.

fleetnodeadmin/v1

  • PairDeviceToFleetNode, UnpairDevice, ListFleetNodeDevices unary (session-only auth).
  • DiscoverOnFleetNode(DiscoverOnFleetNodeRequest) returns (stream pairing.v1.DiscoverResponse).
    • Operator triggers discovery on a specific fleet node.
    • Reuses pairing.v1.DiscoverRequest so the operator UX is uniform with combined-mode pairing.PairingService.Discover.
    • Server-streaming back: server proxies the request to the agent over ControlStream (handler shipping in feat(fleetnode): server-initiated discovery via ControlStream #235) and forwards pairing.v1.DiscoverResponse batches as they arrive.

Why this is safe to merge alone

  • The new RPCs don't have handlers yet; clients calling them on main get CodeUnimplemented.
  • No code on main calls these RPCs.
  • ReportDiscoveredDevices.command_id is optional (proto3 default-empty); existing callers (none yet) unaffected.
  • Generated code is in standard locations and changes only the two affected services' bindings.

Merge order

  1. This PR.
  2. After merge, feat(fleetnode): server-initiated discovery via ControlStream #235 and feat(fleetnode): discovery scan + ReportDiscoveredDevices agent #256 rebase onto main (drops their duplicated proto+generated commits cleanly) and add the implementations. feat(fleetnode): server-initiated discovery via ControlStream #235 and feat(fleetnode): discovery scan + ReportDiscoveredDevices agent #256 are independent of each other after that.

Test plan

  • PATH=./bin:$PATH just _gen-protos — clean regen, only affected files diff (gateway + admin proto + Go + TS).
  • PATH=./bin:$PATH go build ./server/... — compiles (handlers stay on UnimplementedFleetNodeGatewayServiceHandler / UnimplementedFleetNodeAdminServiceHandler for the new RPCs).
  • PATH=./bin:$PATH go vet ./server/... — clean.
  • cd server && PATH=../bin:$PATH golangci-lint run -c .golangci.yaml ./generated/grpc/fleetnodegateway/... ./generated/grpc/fleetnodeadmin/... — 0 issues.
  • Existing handler tests for the affected services pass against the new bindings.

🤖 Generated with Claude Code

Adds the protocol surface for RFC 0001 phase 2 ahead of the
server-side and agent-side implementation PRs. All RPCs return
Unimplemented on the server until those PRs land; nothing on main
calls them yet.

fleetnodegateway/v1:
- ReportDiscoveredDevices unary RPC + DiscoveredDeviceReport message
  (batch up to 1024). command_id field on the request correlates a
  batch to a server-issued ControlCommand so the gateway handler can
  forward it to the operator stream waiting on DiscoverOnFleetNode.
- ControlStream bidi (already present; unchanged).

fleetnodeadmin/v1:
- PairDeviceToFleetNode, UnpairDevice, ListFleetNodeDevices unary
  RPCs (session-only).
- DiscoverOnFleetNode server-streaming RPC: operator triggers
  discovery on a specific fleet node; server proxies the request to
  the agent over ControlStream and forwards pairing.v1.DiscoverResponse
  batches back as they arrive. Reuses pairing.v1.DiscoverRequest so
  the operator UX is uniform with combined-mode pairing.PairingService.Discover.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 18, 2026 23:56
@ankitgoswami ankitgoswami requested a review from a team as a code owner May 18, 2026 23:56
@github-actions github-actions Bot added javascript Pull requests that update javascript code client server shared labels May 18, 2026
@github-actions

github-actions Bot commented May 18, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated security-focused code review generated by Codex.
It should be used as a supplementary check alongside human review.
False positives are possible - use your judgment.

Scope summary

  • Reviewed pull request diff only (fcd233c3dcc5ec441c449c2c9276861e82b9cf77...593ae2a09e454c5ad34b36d3394fb99a5718a034, exact PR three-dot diff)
  • Model: gpt-5.5

💡 Click "edited" above to see previous reviews for this PR.


Review Summary

Overall Risk: HIGH

Findings

[HIGH] New admin RPCs are exposed but not implemented

  • Category: Reliability
  • Location: server/generated/grpc/fleetnodeadmin/v1/fleetnodeadminv1connect/fleetnodeadmin.connect.go:285
  • Description: The PR adds PairDeviceToFleetNode, UnpairDevice, ListFleetNodeDevices, and DiscoverOnFleetNode to the admin service, and the generated handler now routes them. However, the production fleetnodeadmin.Handler still only implements the pre-existing four methods and satisfies the expanded interface through the embedded UnimplementedFleetNodeAdminServiceHandler. These new RPCs therefore return CodeUnimplemented.
  • Impact: Clients generated from this proto will compile and call these APIs, but every new admin operation fails at runtime. This blocks fleet-node device assignment and delegated discovery flows.
  • Recommendation: Implement the four methods on server/internal/handlers/fleetnodeadmin.Handler, wire the required domain services, and add handler/client tests that call the generated Connect routes. Consider avoiding Unimplemented... embedding in production handlers so future RPC additions fail at compile time until explicitly implemented.

[HIGH] Discovery report RPC is exposed but not implemented

  • Category: Reliability
  • Location: server/generated/grpc/fleetnodegateway/v1/fleetnodegatewayv1connect/fleetnodegateway.connect.go:298
  • Description: The PR adds ReportDiscoveredDevices to the fleet-node gateway service and includes it in the fleet-node auth/sensitive-body interceptor lists, but the production gateway handler still relies on the generated unimplemented method.
  • Impact: Fleet nodes cannot return discovery results for DiscoverOnFleetNode; any operator stream depending on this command/report loop will hang, fail, or never receive device data depending on the eventual caller behavior.
  • Recommendation: Implement ReportDiscoveredDevices on server/internal/handlers/fleetnodegateway.Handler, validate the command_id against an outstanding server-issued command for the authenticated fleet node, enforce org/fleet-node ownership, and add an integration test covering command dispatch through report ingestion.

Notes

No SQL, migration, infrastructure, plugin, Rust, or pool-address changes were present in the reviewed diff. The new interceptor entries look directionally correct: admin RPCs are session-only, the node report RPC is fleet-node authenticated, and discovery payload bodies are suppressed from debug logging.


Generated by Codex Security Review |
Triggered by: @ankitgoswami |
Review workflow run

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds protocol contracts for fleet-node discovery and pairing flows, with generated Go and TypeScript bindings updated for the gateway and admin services.

Changes:

  • Adds ReportDiscoveredDevices to the fleet-node gateway API.
  • Adds fleet-node device pairing/listing and remote discovery admin RPCs.
  • Regenerates Go Connect/protobuf and ProtoFleet TypeScript descriptors.

Reviewed changes

Copilot reviewed 2 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
proto/fleetnodegateway/v1/fleetnodegateway.proto Defines the new discovery report RPC and report messages.
proto/fleetnodeadmin/v1/fleetnodeadmin.proto Defines new admin pairing, listing, and remote discovery RPCs.
server/generated/grpc/fleetnodegateway/v1/fleetnodegateway.pb.go Generated Go protobuf bindings for gateway additions.
server/generated/grpc/fleetnodegateway/v1/fleetnodegatewayv1connect/fleetnodegateway.connect.go Generated Go Connect client/server bindings for the gateway RPC.
server/generated/grpc/fleetnodeadmin/v1/fleetnodeadmin.pb.go Generated Go protobuf bindings for admin additions.
server/generated/grpc/fleetnodeadmin/v1/fleetnodeadminv1connect/fleetnodeadmin.connect.go Generated Go Connect client/server bindings for admin RPCs.
client/src/protoFleet/api/generated/fleetnodegateway/v1/fleetnodegateway_pb.ts Generated TypeScript descriptors for gateway additions.
client/src/protoFleet/api/generated/fleetnodeadmin/v1/fleetnodeadmin_pb.ts Generated TypeScript descriptors for admin additions.

Comment thread proto/fleetnodeadmin/v1/fleetnodeadmin.proto Outdated
Comment thread proto/fleetnodegateway/v1/fleetnodegateway.proto
Comment thread proto/fleetnodeadmin/v1/fleetnodeadmin.proto Outdated
- Wrap DiscoverOnFleetNode's response in DiscoverOnFleetNodeResponse
  so buf-lint's RPC_RESPONSE_UNIQUE rule is satisfied
  (pairing.PairingService.Discover already uses DiscoverResponse).
- Add gte=0 validation on ListFleetNodeDevicesRequest.fleet_node_id
  so negative filter values are rejected at the API boundary
  (Copilot review).
- Add the new node-facing RPC (ReportDiscoveredDevices) to
  FleetNodeAuthenticatedProcedures and the four new admin RPCs
  (PairDeviceToFleetNode, UnpairDevice, ListFleetNodeDevices,
  DiscoverOnFleetNode) to SessionOnlyProcedures. Without these the
  interceptor would route bearer-token requests through the wrong
  auth path. Codex HIGH + reviewer comment.
- Reformat regenerated TS files with prettier (Client Lint CI was
  failing on two files).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7073c53c5d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread proto/fleetnodegateway/v1/fleetnodegateway.proto Outdated
CI's generated-code check runs goimports -w . after buf generate
and the import grouping differs from what my local just _gen-protos
left. Re-running goimports locally so the committed bindings match
what CI regenerates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ankitgoswami ankitgoswami marked this pull request as draft May 19, 2026 16:07
ankitgoswami and others added 3 commits May 19, 2026 10:38
- ReportDiscoveredDevicesRequest.command_id is now required
  (min_len: 1, max_len: 128). The new server-initiated design only
  accepts reports issued in response to a ControlCommand; empty
  command_id can never bind to an operator stream and shouldn't be
  on the wire.
- DiscoveredDeviceReport tightens defense-in-depth on the
  fleet-node-to-server trust boundary:
    - ip_address: string.ip = true (must parse as IPv4 or IPv6).
    - port: CEL expression bounds the value to 1..65535 and rejects
      non-numeric strings.
    - url_scheme: enum'd to {http, https} via string.in.
    Handler-side validateReport still enforces RFC1918/RFC4193 in
    PR B; these proto-level rules just bounce the worst inputs
    before the validator.Interceptor passes them down.
- Rename DiscoveredDeviceReport.type to driver_name. The schema and
  domain code use driver_name as the plugin routing key
  (discovered_device.type was dropped); the wire contract needs to
  match so downstream pairing/control can resolve plugins for
  remotely discovered devices. Codex P1.
- ReportDiscoveredDevices and DiscoverOnFleetNode added to
  SensitiveBodyProcedures so request/response bodies stay out of
  debug logs (LAN topology, device inventory, scan targets).
  Codex LOW.
- Trim verbose proto comments per project style.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mode support on DiscoverOnFleetNode:
- IPListMode: agent runs TCP probe via plugin manager.
- IPRangeMode: server expands to IPList before dispatch; agent sees
  an IPList.
- NmapMode: agent shells out to the nmap binary bundled in the
  fleetnode release artifact.
- MDNSMode: rejected with FailedPrecondition. Multicast UDP doesn't
  traverse the NAT/VLAN topology agents typically deploy into.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ankitgoswami ankitgoswami marked this pull request as ready for review May 19, 2026 19:47
@ankitgoswami ankitgoswami merged commit 19a0305 into main May 19, 2026
68 of 69 checks passed
@ankitgoswami ankitgoswami deleted the ankitg/discovery-pairing-proto branch May 19, 2026 20:32
ankitgoswami added a commit that referenced this pull request May 19, 2026
PR #257 landed the wire contracts on main, so this branch drops the
proto + generated bindings + interceptor config entries that PR A
now owns. What's left is the server-side implementation that
consumes those contracts.

What stays from the previous branch state:
- Migration adding discovered_device.discovered_by_fleet_node_id +
  partial index. Renumbered to 000051 to avoid collision with
  000050_add_curtailment_event_created_by which landed on main.
- fleetnodepairing domain service + sqlstore + integration tests:
  PairDevice (CONFIRMED-only + intra-TX lock), UnpairDevice,
  ListPairs, ListDevicesForFleetNode, UpsertDiscoveredDevices with
  auto:* CTE reconciliation by (fleet_node, ip, port).
- fleetnodegateway handler: ReportDiscoveredDevices (uses
  fleetnodeauth.Subject for org/node).
- fleetnodeadmin handler: PairDeviceToFleetNode, UnpairDevice,
  ListFleetNodeDevices.
- pairing.PairDevices SSRF guard rejecting agent-reported rows.
- RevokeFleetNode cleanup (deletes pairings, clears attribution).
- GetActiveUnpairedDiscoveredDevices filter.
- minerdiscovery model + discovered_device store DiscoveredByFleetNodeID
  plumbing.

What adapted to the new wire:
- DiscoveredDeviceReport.Type renamed to DriverName everywhere
  (model, store, handler, tests) to match the proto rename.
- fleetd/main.go: wire fleetnodepairing.Service and pass it to both
  fleetnodegateway and fleetnodeadmin handler constructors.

The new server-initiated components (fleetnodecontrol.Registry,
ControlStream handler, DiscoverOnFleetNode admin handler) follow
in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ankitgoswami added a commit that referenced this pull request May 19, 2026
Implement the server-initiated discovery flow that pairs with the
proto contracts merged in #257. Operators trigger
DiscoverOnFleetNode(fleet_node_id, pairing.v1.DiscoverRequest); the
server dispatches a ControlCommand to the agent's open ControlStream,
the agent reports devices via ReportDiscoveredDevices with the
matching command_id, and the server forwards each batch back to the
operator stream until the agent's ControlAck closes the call.

- fleetnodecontrol.Registry: in-memory map[fleet_node_id]stream plus
  per-command_id event channels. Single-instance fleetd only.
- ControlStream handler: Hello -> Accepted -> bidi pump of outgoing
  ControlCommand and incoming ControlAck. PublishAck wires acks to
  the in-flight command channel.
- DiscoverOnFleetNode admin RPC: validates session + fleet node org
  scope, expands IPRange to IPList server-side, rejects MDNS/Nmap,
  encodes payload, streams batches until ack.
- ReportDiscoveredDevices: when command_id is set, publishes each
  persisted batch to the registry so the operator stream wakes up.
- Export ipscanner.GenerateIPsFromCIDR so other packages can reuse
  CIDR enumeration.

Tests cover registry register/send/ack/disconnect, ControlStream
hello/dispatch/ack/duplicate-stream rejection, DiscoverOnFleetNode
happy path + no-stream + MDNS reject + IPRange expansion + viewer
gate, and the ReportDiscoveredDevices command_id correlation path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ankitgoswami added a commit that referenced this pull request May 27, 2026
PR #257 landed the wire contracts on main, so this branch drops the
proto + generated bindings + interceptor config entries that PR A
now owns. What's left is the server-side implementation that
consumes those contracts.

What stays from the previous branch state:
- Migration adding discovered_device.discovered_by_fleet_node_id +
  partial index. Renumbered to 000051 to avoid collision with
  000050_add_curtailment_event_created_by which landed on main.
- fleetnodepairing domain service + sqlstore + integration tests:
  PairDevice (CONFIRMED-only + intra-TX lock), UnpairDevice,
  ListPairs, ListDevicesForFleetNode, UpsertDiscoveredDevices with
  auto:* CTE reconciliation by (fleet_node, ip, port).
- fleetnodegateway handler: ReportDiscoveredDevices (uses
  fleetnodeauth.Subject for org/node).
- fleetnodeadmin handler: PairDeviceToFleetNode, UnpairDevice,
  ListFleetNodeDevices.
- pairing.PairDevices SSRF guard rejecting agent-reported rows.
- RevokeFleetNode cleanup (deletes pairings, clears attribution).
- GetActiveUnpairedDiscoveredDevices filter.
- minerdiscovery model + discovered_device store DiscoveredByFleetNodeID
  plumbing.

What adapted to the new wire:
- DiscoveredDeviceReport.Type renamed to DriverName everywhere
  (model, store, handler, tests) to match the proto rename.
- fleetd/main.go: wire fleetnodepairing.Service and pass it to both
  fleetnodegateway and fleetnodeadmin handler constructors.

The new server-initiated components (fleetnodecontrol.Registry,
ControlStream handler, DiscoverOnFleetNode admin handler) follow
in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ankitgoswami added a commit that referenced this pull request May 27, 2026
Implement the server-initiated discovery flow that pairs with the
proto contracts merged in #257. Operators trigger
DiscoverOnFleetNode(fleet_node_id, pairing.v1.DiscoverRequest); the
server dispatches a ControlCommand to the agent's open ControlStream,
the agent reports devices via ReportDiscoveredDevices with the
matching command_id, and the server forwards each batch back to the
operator stream until the agent's ControlAck closes the call.

- fleetnodecontrol.Registry: in-memory map[fleet_node_id]stream plus
  per-command_id event channels. Single-instance fleetd only.
- ControlStream handler: Hello -> Accepted -> bidi pump of outgoing
  ControlCommand and incoming ControlAck. PublishAck wires acks to
  the in-flight command channel.
- DiscoverOnFleetNode admin RPC: validates session + fleet node org
  scope, expands IPRange to IPList server-side, rejects MDNS/Nmap,
  encodes payload, streams batches until ack.
- ReportDiscoveredDevices: when command_id is set, publishes each
  persisted batch to the registry so the operator stream wakes up.
- Export ipscanner.GenerateIPsFromCIDR so other packages can reuse
  CIDR enumeration.

Tests cover registry register/send/ack/disconnect, ControlStream
hello/dispatch/ack/duplicate-stream rejection, DiscoverOnFleetNode
happy path + no-stream + MDNS reject + IPRange expansion + viewer
gate, and the ReportDiscoveredDevices command_id correlation path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ankitgoswami added a commit that referenced this pull request May 28, 2026
PR #257 landed the wire contracts on main, so this branch drops the
proto + generated bindings + interceptor config entries that PR A
now owns. What's left is the server-side implementation that
consumes those contracts.

What stays from the previous branch state:
- Migration adding discovered_device.discovered_by_fleet_node_id +
  partial index. Renumbered to 000051 to avoid collision with
  000050_add_curtailment_event_created_by which landed on main.
- fleetnodepairing domain service + sqlstore + integration tests:
  PairDevice (CONFIRMED-only + intra-TX lock), UnpairDevice,
  ListPairs, ListDevicesForFleetNode, UpsertDiscoveredDevices with
  auto:* CTE reconciliation by (fleet_node, ip, port).
- fleetnodegateway handler: ReportDiscoveredDevices (uses
  fleetnodeauth.Subject for org/node).
- fleetnodeadmin handler: PairDeviceToFleetNode, UnpairDevice,
  ListFleetNodeDevices.
- pairing.PairDevices SSRF guard rejecting agent-reported rows.
- RevokeFleetNode cleanup (deletes pairings, clears attribution).
- GetActiveUnpairedDiscoveredDevices filter.
- minerdiscovery model + discovered_device store DiscoveredByFleetNodeID
  plumbing.

What adapted to the new wire:
- DiscoveredDeviceReport.Type renamed to DriverName everywhere
  (model, store, handler, tests) to match the proto rename.
- fleetd/main.go: wire fleetnodepairing.Service and pass it to both
  fleetnodegateway and fleetnodeadmin handler constructors.

The new server-initiated components (fleetnodecontrol.Registry,
ControlStream handler, DiscoverOnFleetNode admin handler) follow
in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ankitgoswami added a commit that referenced this pull request May 28, 2026
Implement the server-initiated discovery flow that pairs with the
proto contracts merged in #257. Operators trigger
DiscoverOnFleetNode(fleet_node_id, pairing.v1.DiscoverRequest); the
server dispatches a ControlCommand to the agent's open ControlStream,
the agent reports devices via ReportDiscoveredDevices with the
matching command_id, and the server forwards each batch back to the
operator stream until the agent's ControlAck closes the call.

- fleetnodecontrol.Registry: in-memory map[fleet_node_id]stream plus
  per-command_id event channels. Single-instance fleetd only.
- ControlStream handler: Hello -> Accepted -> bidi pump of outgoing
  ControlCommand and incoming ControlAck. PublishAck wires acks to
  the in-flight command channel.
- DiscoverOnFleetNode admin RPC: validates session + fleet node org
  scope, expands IPRange to IPList server-side, rejects MDNS/Nmap,
  encodes payload, streams batches until ack.
- ReportDiscoveredDevices: when command_id is set, publishes each
  persisted batch to the registry so the operator stream wakes up.
- Export ipscanner.GenerateIPsFromCIDR so other packages can reuse
  CIDR enumeration.

Tests cover registry register/send/ack/disconnect, ControlStream
hello/dispatch/ack/duplicate-stream rejection, DiscoverOnFleetNode
happy path + no-stream + MDNS reject + IPRange expansion + viewer
gate, and the ReportDiscoveredDevices command_id correlation path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ankitgoswami added a commit that referenced this pull request May 28, 2026
PR #257 landed the wire contracts on main, so this branch drops the
proto + generated bindings + interceptor config entries that PR A
now owns. What's left is the server-side implementation that
consumes those contracts.

What stays from the previous branch state:
- Migration adding discovered_device.discovered_by_fleet_node_id +
  partial index. Renumbered to 000051 to avoid collision with
  000050_add_curtailment_event_created_by which landed on main.
- fleetnodepairing domain service + sqlstore + integration tests:
  PairDevice (CONFIRMED-only + intra-TX lock), UnpairDevice,
  ListPairs, ListDevicesForFleetNode, UpsertDiscoveredDevices with
  auto:* CTE reconciliation by (fleet_node, ip, port).
- fleetnodegateway handler: ReportDiscoveredDevices (uses
  fleetnodeauth.Subject for org/node).
- fleetnodeadmin handler: PairDeviceToFleetNode, UnpairDevice,
  ListFleetNodeDevices.
- pairing.PairDevices SSRF guard rejecting agent-reported rows.
- RevokeFleetNode cleanup (deletes pairings, clears attribution).
- GetActiveUnpairedDiscoveredDevices filter.
- minerdiscovery model + discovered_device store DiscoveredByFleetNodeID
  plumbing.

What adapted to the new wire:
- DiscoveredDeviceReport.Type renamed to DriverName everywhere
  (model, store, handler, tests) to match the proto rename.
- fleetd/main.go: wire fleetnodepairing.Service and pass it to both
  fleetnodegateway and fleetnodeadmin handler constructors.

The new server-initiated components (fleetnodecontrol.Registry,
ControlStream handler, DiscoverOnFleetNode admin handler) follow
in the next commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ankitgoswami added a commit that referenced this pull request May 28, 2026
Implement the server-initiated discovery flow that pairs with the
proto contracts merged in #257. Operators trigger
DiscoverOnFleetNode(fleet_node_id, pairing.v1.DiscoverRequest); the
server dispatches a ControlCommand to the agent's open ControlStream,
the agent reports devices via ReportDiscoveredDevices with the
matching command_id, and the server forwards each batch back to the
operator stream until the agent's ControlAck closes the call.

- fleetnodecontrol.Registry: in-memory map[fleet_node_id]stream plus
  per-command_id event channels. Single-instance fleetd only.
- ControlStream handler: Hello -> Accepted -> bidi pump of outgoing
  ControlCommand and incoming ControlAck. PublishAck wires acks to
  the in-flight command channel.
- DiscoverOnFleetNode admin RPC: validates session + fleet node org
  scope, expands IPRange to IPList server-side, rejects MDNS/Nmap,
  encodes payload, streams batches until ack.
- ReportDiscoveredDevices: when command_id is set, publishes each
  persisted batch to the registry so the operator stream wakes up.
- Export ipscanner.GenerateIPsFromCIDR so other packages can reuse
  CIDR enumeration.

Tests cover registry register/send/ack/disconnect, ControlStream
hello/dispatch/ack/duplicate-stream rejection, DiscoverOnFleetNode
happy path + no-stream + MDNS reject + IPRange expansion + viewer
gate, and the ReportDiscoveredDevices command_id correlation path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client javascript Pull requests that update javascript code server shared

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants