Expand description
BGP-4 server (RFC 4271).
§Division of labour
Rust owns the session: TCP framing, OPEN validation, capability and hold-time negotiation, the KEEPALIVE cadence, hold-timer expiry, and NOTIFICATION on error. None of that requires reasoning and all of it has to be right on a millisecond budget, so none of it is delegated to the model.
The LLM owns the content: whether to peer with this neighbour at all, and which routes to announce or withdraw. That is why there is no RIB here and why one is not planned — the root CLAUDE.md forbids protocols from implementing storage, and a RIB is storage. Route state, if a deployment wants any, belongs in the generic SQLite facility or in server memory, reached through actions.
§Session flow
TCP accept -> Connect
peer OPEN, validated + negotiated -> bgp_open event -> our OPEN + KEEPALIVE -> OpenConfirm
peer KEEPALIVE -> Established -> bgp_established event
peer UPDATE -> bgp_update event
hold timer expires -> NOTIFICATION 4/0, closeSending a KEEPALIVE immediately after our OPEN is the step RFC 4271 section 8.2.2 requires to reach OpenConfirm, and it was missing: the old implementation only ever sent a KEEPALIVE in reply to one, so a peer that waits for ours before sending its own would deadlock.
§Concurrency
The write half is owned by a dedicated task fed by an unbounded channel, so the keepalive
ticker keeps the session alive while the read loop is blocked in an LLM call. Sharing a
TcpStream behind a lock would have meant holding that lock across the LLM await.
Modules§
- actions
- BGP protocol actions.
- wire
- BGP-4 wire format: encoding, decoding, and the model-facing JSON projection.