Skip to content
This repository was archived by the owner on Jul 14, 2026. It is now read-only.
This repository was archived by the owner on Jul 14, 2026. It is now read-only.

Add TcpServer and TcpReceiver for inbound node connections #6

Description

@jonnyspicer

Summary

Add a TCP server that accepts inbound connections from radar nodes and routes detection frames to per-node TcpReceiver instances.

Context

Depends on #5 (DetectionSource protocol).

Radar nodes will push detection frames over persistent TCP connections instead of being polled over HTTP. The TcpServer handles all inbound connections on a single port and routes frames by node_id to the correct TcpReceiver.

See design spec: docs/superpowers/specs/2026-03-11-tcp-push-detection-forwarding-design.md in the retina monorepo.

Changes

New: TcpServer (tracker_host/tcp_server.py)

Single asyncio TCP server listening on one port. Responsibilities:

  • Accept connections, set TCP_NODELAY
  • Accumulate data per-connection, split on \n
  • Enforce max message size (1 MB per line)
  • Parse JSON, extract node_id and token
  • Validate token on first message per connection; close socket on invalid token
  • Route detection frames to registered TcpReceiver by node_id
  • Discard heartbeat messages (messages with "type": "heartbeat")
  • Track per-connection state, clean up on disconnect
  • Notify TcpReceiver of connection/disconnection events (for is_healthy)

New: TcpReceiver (tracker_host/tcp_receiver.py)

Implements DetectionSource protocol. Push-based counterpart to DetectionFetcher.

  • Backed by bounded asyncio.Queue(maxsize=2000)
  • Drop oldest frame when queue is full (log warning)
  • Timestamp dedup (skip if timestamp unchanged)
  • is_healthy reflects whether TcpServer has an active connection for this node
  • receive() returns next frame from queue, or None after a short timeout

New: TcpServerConfig (tracker_host/config.py)

@dataclass
class TcpServerConfig:
    enabled: bool = False
    host: str = "0.0.0.0"
    port: int = 30050
    token: str = ""
    auto_provision: bool = True
    auto_provision_port_start: int = 31000
    auto_provision_port_end: int = 31099

Protocol

All messages are newline-delimited JSON, node-to-server only:

Detection frame:

{"node_id":"radar3","token":"secret","timestamp":1768932172478,"delay":[40.46],"doppler":[-27.99],"snr":[12.08],"adsb":[null]}

Heartbeat:

{"node_id":"radar3","token":"secret","type":"heartbeat"}

Acceptance criteria

  • TCP server accepts multiple concurrent node connections
  • Token validated on first message per connection
  • Invalid tokens cause socket close + warning log
  • Frames routed to correct TcpReceiver by node_id
  • Heartbeats discarded (don't reach TcpReceiver)
  • Queue bounded at 2000, drops oldest on overflow
  • TCP_NODELAY set on accepted sockets
  • Max 1MB per message enforced
  • Connection/disconnection events update TcpReceiver health
  • Unit tests for all above scenarios

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions