Expand description
Unix-socket IPC transport for the MAVLink and state sockets.
Mirrors the semantics of ADOSDroneAgent/src/ados/core/ipc.py:
- The owning service binds a Unix socket (perms
0o660, groupados-operator, stale socket removed first) and broadcasts byte buffers to every connected client. The trusted local plane is root plus that group and nothing wider: two of these sockets forward inbound bytes straight to the flight controller. Plugins run as theadosuser, which is never in the operator group, and every accept re-checks the peer’s kernel credentials (seeOperatorListener). - Each client has a bounded outbound queue. A client whose queue fills is dropped rather than allowed to grow unbounded (slow-client policy).
- The state socket additionally replays the last buffer to a client the moment it connects, so a late subscriber does not wait for the next publish.
- The MAVLink socket is bidirectional: a frame written by a client is a command toward the flight controller, surfaced here on an inbound channel.
Framing is the caller’s concern. broadcast
sends the exact bytes given (the caller frames via crate::frame or
crate::state). The inbound reader, used only by the MAVLink socket,
decodes 4-byte big-endian length-prefixed frames.
§Who wrote this
An inbound payload arrives as an InboundCommand, which carries the
writing connection’s IpcPeer alongside the bytes. The owning service
used to receive a bare buffer and had no way to tell one writer from
another, so it recorded a single hardcoded provenance for every one of
them. That reading was wrong for the writer that matters most: a process
that accepts bytes from off the node and forwards them here is not the same
as a process that produced them itself, and a service that cannot see the
difference reports remote traffic as locally originated.
Structs§
- Inbound
Command - One inbound payload plus the identity of the connection that wrote it.
- Injector
Claim - A writer’s declared autonomous-injector identity (see
IPC_DECLARE_INJECTOR_PREFIX). Carried raw to the FC-write path, which verifiesticketagainst the pairing key before treatingclient_idas the attested injector; aNoneticket is an unverified claim that can never win the lane away from a human holder. - IpcBroadcast
- A Unix-socket broadcast server. Drops slow clients; optionally replays the last buffer on connect; optionally decodes inbound length-prefixed frames.
- IpcPeer
- Who wrote an inbound payload, as the accepting socket can tell.
- Operator
Listener - A bound command-plane socket.
acceptyields only peersoperator_peer_allowedadmits (plus, on a socket bound withbind_reader_socket, members of its reader group); any other peer is closed before a byte of it is read.
Enums§
- Peer
Class - What an admitted peer on a command socket is.
Constants§
- CAPTURE_
GROUP - The group that may write
/run/ados/capture(the capture-active marker directory,2775 root:ados-capture) and the capture root, which the supervisor’s janitor and the installer keep at3775 root:ados-capture. A capture plugin joins it only while it holdscapture.storage.write; it guards nothing else. - IPC_
DECLARE_ INJECTOR_ PREFIX - Control-frame prefix a writer uses to declare that it is an AUTONOMOUS
INJECTOR: a producer of commands generated on the node rather than by a
human operator (the plugin host driving guided setpoints, the MSP relay).
The rest of the payload is the writer’s
client_id, optionally followed by;ticket=<ticket>— the attestation the FC-write path verifies against the pairing key so a spoofedclient_idcannot claim the injector lane. - IPC_
DECLARE_ OFF_ BOX_ SOURCE - The control payload a writer sends to declare that the bytes it is about to forward did not originate on this node.
- LEGACY_
PLUGIN_ USER - The shared service user plugin processes ran as before each plugin got its own user. Still treated as plugin plane wherever it exists.
- OPERATOR_
GROUP - The group that owns every agent command-plane socket: the control socket,
the
*-cmd.sockcommand sockets, the MAVLink/MSP/state lanes, and the rest of the sockets bound throughbind_command_socket. The installer creates it and adds the human operator; the plugin user is never a member, whatever capabilities it holds (a sensor-bus reader joinsSENSORS_GROUP). - PLUGIN_
GROUP - The group every plugin unit runs in as its primary group. Each plugin has
its own system user; this shared group owns only the per-plugin sockets
(
bind_plugin_socket), which gate every request on the plugin’s own capability token, and it is what the plugin loopback guard and the router match a plugin process by. - SENSORS_
GROUP - The reader group of the sensor bus (
/run/ados/sensing/sensors.sock), bound withbind_reader_socket. A member may connect and subscribe; what it publishes is dropped. A plugin joins it only while it holdssensor.stream.read, which is how a sandboxed plugin reads the bus without joiningOPERATOR_GROUP. The installer also adds the human operator, whose operator membership still decides what it may publish.
Functions§
- bind_
command_ socket - Bind a command-plane socket: create the parent directory, remove a stale
socket left by a prior run (so
bindcannot fail withEADDRINUSE), bind, set the file mode, and hand the file toOPERATOR_GROUP. Every accept on the returned listener re-checks the peer (seeOperatorListener). - bind_
plugin_ socket - Bind a socket on the plugin-reachable plane: the same hygiene as
bind_command_socket, owned byPLUGIN_GROUPso a plugin’s unit can connect. There is no peer check here, so it is only for surfaces a plugin may legitimately reach: the per-plugin sockets, where the server gates every request on the plugin’s own capability token, and the log ingest sink, which only accepts log frames. Never use it for an agent command socket. - bind_
reader_ socket - Bind a command socket that
reader_groupmay also open: the hygiene ofsuper::bind_command_socket, but the file is handed toreader_group(so its members canconnect()), and the listener admits service peers, operators and reader-group members, telling them apart throughOperatorListener::accept_classified. An operator that is not also inreader_groupcannot open the file; the installer adds the human operator to the reader groups it creates. - classify_
peer - Classify a peer from its kernel-reported credentials, or
Nonewhen the socket must refuse it. Service first, then the operator group, then the reader group, so a peer in both groups is an operator. With a group absent from the host (its gidNone) nobody is admitted through it. - connect_
with_ retry - Connect to a Unix socket with bounded retry (mirrors the Python client’s connect-with-retry). Returns the stream once connected.
- encode_
injector_ declaration - Build the
IPC_DECLARE_INJECTOR_PREFIXcontrol frame a writer sends to declare itself an autonomous injector. The inverse ofparse_injector_declaration, kept beside it so a producer’s declaration and the router’s parse cannot drift on the wire format.ticketis the pairing-key attestation forclient_id; omit it on an unpaired node. - is_
operator_ peer - Whether the process on the other end of
streamis on the operator plane: root, this process’s own uid, or a member ofOPERATOR_GROUP(the same policyOperatorListenerenforces). For a socket that must also accept plugin-plane peers (the log ingest sink), this tells the two apart so what a plugin writes is recorded as the plugin’s. Unreadable credentials read as not-operator, which can only narrow trust, except for the development-host casepeer_hung_up_before_acceptdocuments. - is_
plugin_ peer - Whether a connecting peer is on the plugin plane: the shared plugin user,
or any process carrying
PLUGIN_GROUPas its primary or a supplementary group. Such a writer is autonomous by construction, so it is gated as an injector whether or not it declares itself one; a self-declaration only binds the cooperating ones. - operator_
peer_ allowed - Whether a connecting peer may use a command-plane socket.
- parse_
injector_ declaration - Parse an
IPC_DECLARE_INJECTOR_PREFIXcontrol frame into anInjectorClaim.Nonewhen the payload is not an injector declaration or carries an empty client id (a declaration with no identity is not a claim). - read_
length_ prefixed - Read one length-prefixed frame from a stream. Returns
Ok(None)on clean EOF. Used to consume the MAVLink broadcast on the client side. - read_
newline_ line - Read one newline-terminated line (state socket v1) from a stream. Returns
Ok(None)on clean EOF. The returned buffer excludes the trailing newline. - serve_
rpc - Serve a one-shot request/response command socket.