Expand description
CAN bus (SocketCAN) server — NetGet as an ECU on somebody’s vehicle bus.
CAN is a broadcast bus, not a network. There is no addressing, no routing, no session and no authentication: every frame reaches every node, and each node decides for itself whether the 11- or 29-bit identifier is one it answers. Arbitration is by identifier — the numerically lowest wins the bus — which is a priority scheme, not a security one.
That is what makes an LLM-driven ECU simulator worth building, and it is the same property
that makes it dangerous. A NetGet instance on a real vehicle bus can claim any identifier
and impersonate any controller, and nothing on the bus can tell. See CLAUDE.md.
§Two transports, one codec
The frame format lives in frame and is pure. This module is the thin layer that moves
those bytes, and transport is the thinner one that touches the OS:
transport: "socketcan"(the default) — a realAF_CANsocket, which exists only in the Linux kernel. On macOS and Windowsspawn()returns an error naming the reason rather than reporting a server that is up and hearing nothing. This code has never been compiled or run; seeCLAUDE.md.transport: "udp"— a testing transport carrying the samestruct can_frame/struct canfd_frameoctets as UDP datagram payloads, so the whole event → handler/LLM → action → frame path runs unprivileged. No real CAN node speaks it.
§LLM failure → silence, and there is no alternative
CAN has no error reply. The thing called an error frame is not a message: it is six dominant bits transmitted on top of a frame in flight, destroying it for every node on the bus, after which the transmitting controller increments an error counter. Enough of them make it error-passive and then bus-off, at which point it has disconnected itself. Emitting one to signal “the backend is down” would corrupt other nodes’ traffic and could take this node — or with enough repetition, another — off the bus entirely.
So a failure transmits nothing, and nothing derived from the error ever reaches the wire.
Silence is also completely ordinary on CAN: almost every node ignores almost every frame. The
distinction therefore survives only in the log, tagged decision= the way
src/server/radius/ tags its own:
| Tag | Meaning |
|---|---|
decision=no_policy | No instruction and no handler — nothing is transmitted and no LLM call is made |
decision=model_reject | The model answered no_response — a real decision |
decision=model_silent | The model returned nothing usable |
decision=fail_closed_overloaded | The call failed and the backend is saturated (retryable) |
decision=fail_closed_llm_error | The call failed otherwise |
Modules§
- actions
- CAN bus actions and events — what the model sees, and what it can do about it.
- frame
- The CAN frame: representation, validation and the SocketCAN wire encoding.
- transport
- Where CAN frames actually travel — and the one platform on which they can.
Structs§
- CanServer
- CAN bus server.