Summary
Wire the TcpServer into TrackerManager, add auto-provisioning of tracker instances for unknown nodes, and handle disconnect/reconnect lifecycle.
Context
Depends on #5 (DetectionSource protocol) and #6 (TcpServer + TcpReceiver).
See design spec: docs/superpowers/specs/2026-03-11-tcp-push-detection-forwarding-design.md in the retina monorepo.
Changes
Updated: TrackerManager (tracker_host/manager.py)
- Start
TcpServer on init if tcp_server.enabled in config
- For
mode: "tcp" trackers in config: create TcpReceiver, register with TcpServer, create TrackerInstance with that receiver
- For
mode: "http" trackers: existing behavior (create DetectionFetcher-backed instance)
- Auto-provisioning callback: register a callback with
TcpServer for unknown node_ids. When a frame arrives from an unknown node with a valid token:
- Auto-assign a local TCP port from
auto_provision_port_start to auto_provision_port_end
- Create a
TcpReceiver and register with TcpServer
- Create a
TrackerInstance with default settings
- Start the instance (spawns retina-tracker subprocess)
- Log the auto-provisioning event
- Reject auto-provisioning when port range is exhausted (log error)
Disconnect/reconnect lifecycle
- On node disconnect:
TcpReceiver.is_healthy becomes False. TrackerInstance keeps subprocess alive for extended_outage_sec (existing behavior via fetch loop).
- On reconnect:
TcpServer routes to existing TcpReceiver. Node's buffered frames drain through. Subprocess continues processing.
- After
extended_outage_sec: subprocess killed, instance stays registered in memory.
- Late reconnect: subprocess respawned automatically when frames arrive again.
Graceful shutdown
- Stop accepting new TCP connections
- Close all node connections (nodes reconnect automatically)
- Drain in-progress frames
- Stop all tracker subprocesses (existing
TrackerManager.stop() behavior)
Updated: config parsing
- Parse
tcp_server block into TcpServerConfig
- Updated
config.yaml with tcp_server section
Acceptance criteria
Summary
Wire the TcpServer into TrackerManager, add auto-provisioning of tracker instances for unknown nodes, and handle disconnect/reconnect lifecycle.
Context
Depends on #5 (DetectionSource protocol) and #6 (TcpServer + TcpReceiver).
See design spec:
docs/superpowers/specs/2026-03-11-tcp-push-detection-forwarding-design.mdin the retina monorepo.Changes
Updated:
TrackerManager(tracker_host/manager.py)TcpServeron init iftcp_server.enabledin configmode: "tcp"trackers in config: createTcpReceiver, register withTcpServer, createTrackerInstancewith that receivermode: "http"trackers: existing behavior (createDetectionFetcher-backed instance)TcpServerfor unknownnode_ids. When a frame arrives from an unknown node with a valid token:auto_provision_port_starttoauto_provision_port_endTcpReceiverand register withTcpServerTrackerInstancewith default settingsDisconnect/reconnect lifecycle
TcpReceiver.is_healthybecomes False. TrackerInstance keeps subprocess alive forextended_outage_sec(existing behavior via fetch loop).TcpServerroutes to existingTcpReceiver. Node's buffered frames drain through. Subprocess continues processing.extended_outage_sec: subprocess killed, instance stays registered in memory.Graceful shutdown
TrackerManager.stop()behavior)Updated: config parsing
tcp_serverblock intoTcpServerConfigconfig.yamlwith tcp_server sectionAcceptance criteria