Comms requires the
comms Cargo feature to be compiled in.Overview
The comms system provides:- Four LLM-facing tools:
send_message,send_request,send_response,list_peers - Three transport layers: Unix Domain Sockets (UDS), TCP, and in-process (
inproc) - Ed25519 cryptographic identity: Every agent has a keypair; all messages are signed
- Trust-based peer model: Agents only accept messages from explicitly trusted peers
- Host mode: An agent processes its initial prompt then stays alive waiting for incoming comms messages
- Inbox with notification: Incoming messages are queued and drained at turn boundaries
Architecture
| Crate | Role |
|---|---|
meerkat-comms | Core comms: identity, trust, transport, router, inbox, runtime, MCP tools, agent integration |
meerkat-tools (builtin::comms) | CommsToolSurface, CommsToolSet, individual BuiltinTool implementations |
meerkat-core | CommsRuntime trait, CommsRuntimeConfig, CommsRuntimeMode enum, agent host-mode impl |
meerkat (facade) | build_comms_runtime_from_config(), compose_tools_with_comms(), factory wiring |
Setup
Enable comms feature
Ensure the
comms Cargo feature is compiled in (enabled by default in the CLI and facade crate).Generate identity
Identity is auto-generated on first run. Keys are stored in
.rkat/identity/ (mode 0600 for private key on Unix).Configure trusted peers
Add peers to
.rkat/trusted_peers.json with their name, public key, and address.Identity and cryptography
Each agent has an Ed25519 keypair managed by theKeypair type (meerkat-comms/src/identity.rs).
- Key generation:
Keypair::generate()creates a new random keypair usingOsRng. - Key persistence:
Keypair::save(dir)writesidentity.key(mode0600on Unix) andidentity.pubto disk.Keypair::load(dir)reads them back.Keypair::load_or_generate(dir)is the canonical entry point. - Public key format:
PubKeyis a 32-byte Ed25519 public key. The canonical string format ised25519:<base64>(standard Base64 with padding). - Default identity directory:
.rkat/identity/(relative to base dir).
Envelope signing
Envelope signing
Every message is wrapped in a signed The signable bytes are computed by serializing
Envelope:(id, from, to, kind) as CBOR, then recursively sorting all map keys by canonical order (RFC 8949) before encoding. This ensures deterministic signing across implementations.Trust model
Agents maintain a list of trusted peers in aTrustedPeers collection (meerkat-comms/src/trust.rs).
TrustedPeer structure and file format
TrustedPeer structure and file format
.rkat/trusted_peers.json:Trust enforcement
Incoming connections are validated inhandle_connection() (meerkat-comms/src/io_task.rs):
Transport layer
All transports use a length-prefixed CBOR framing protocol implemented byTransportCodec.
Wire format: 4 bytes (big-endian) payload length followed by CBOR-encoded Envelope (up to 1 MB max).
Address formats
| Scheme | Format | Use case |
|---|---|---|
uds:// | uds:///path/to/socket.sock | Same-machine, lowest latency |
tcp:// | tcp://host:port | Cross-machine |
inproc:// | inproc://agent-name | In-process sub-agent communication |
Transport details
Transport details
UDS transport: Unix Domain Socket listeners are spawned by
spawn_uds_listener(). The socket file is created at the configured path (existing files are removed first). Parent directories are created automatically.TCP transport: TCP listeners are spawned by spawn_tcp_listener(). Accepts connections and processes each in a dedicated tokio task.Inproc transport: The InprocRegistry (meerkat-comms/src/inproc.rs) is a process-global registry (singleton via OnceLock) that maps agent names and public keys to their inbox senders. Messages are delivered directly in-memory without serialization.InprocRegistry::global()returns the singletonregister(name, pubkey, sender)adds an agent (evicts stale entries on collision)unregister(pubkey)removes an agentsend(from_keypair, to_name, kind)creates a signed envelope and delivers it directly
CommsRuntime is created, it automatically registers itself in the global InprocRegistry. When dropped, it unregisters.Message types
MessageKind and Status
MessageKind and Status
ACK behavior
| Message Kind | Sender waits for ACK? | Receiver sends ACK? |
|---|---|---|
Message | Yes (with timeout) | Yes |
Request | Yes (with timeout) | Yes |
Response | No | No |
Ack | No | No (would loop) |
SendError::PeerOffline.
MessageIntent variants
MessageIntent variants
The
Standard strings are parsed into their enum variants; unknown strings become
MessageIntent enum provides type-safe intent values for requests:| Variant | String | Description |
|---|---|---|
Delegate | "delegate" | Delegate a task |
Status | "status" | Request status update |
Cancel | "cancel" | Cancel an operation |
Ack | "ack" | Request acknowledgment |
Review | "review" | Review something |
Calculate | "calculate" | Request computation |
Query | "query" | Request information |
Custom(String) | (any string) | User-defined |
Custom.LLM-facing tools
Four tools are exposed to the LLM when comms is enabled.send_message
send_message
send_request
send_request
send_response
send_response
Send a response to a previously received request.Response:
Peer name to send response to.
ID of the request being responded to (UUID).
One of
"accepted", "completed", "failed".Response result data. Defaults to
null.{"status": "sent"}list_peers
list_peers
List all trusted peers and their addresses.Input: Empty object
{}Response:Tool availability
Comms tools are conditionally available based on whether any trusted peers are configured. TheCommsToolSurface::peer_availability() function creates an Availability predicate that hides the tools from the LLM when no peers exist.
Inbox
TheInbox (meerkat-comms/src/inbox.rs) is a bounded MPSC channel (default capacity: 1024) with a Notify mechanism for waking waiting tasks.
InboxSender::send(item)enqueues an item and callsnotify.notify_waiters()Inbox::try_drain()returns all currently available items without blockingInbox::recv()blocks until a message is available
CommsMessage for the agent loop, ACKs and messages from unknown peers are filtered out.
Configuration
Config file (.rkat/config.toml)
Config file (.rkat/config.toml)
| Field | Type | Default | Description |
|---|---|---|---|
mode | CommsRuntimeMode | Inproc | Transport mode |
address | Option<String> | None | Listen address (TCP socket address or UDS path) |
auto_enable_for_subagents | bool | false | Whether to auto-enable comms for spawned sub-agents |
CoreCommsConfig (internal)
CoreCommsConfig (internal)
The Paths support
CoreCommsConfig is the internal config used by CommsRuntime:{name} interpolation (replaced with the agent’s comms name). Relative paths are resolved against the base directory via resolve_paths(base_dir).CLI usage
| Flag | Description |
|---|---|
--comms-name <NAME> | Agent name for peer identification. Enables comms if set. |
--comms-listen-tcp <ADDR> | TCP address to listen on (e.g., "0.0.0.0:4200") |
--no-comms | Disable inter-agent communication entirely |
--host | Run in host mode (stay alive for comms messages after initial prompt) |
All comms flags require the
comms feature at compile time (#[cfg(feature = "comms")]).Host mode
Host mode keeps the agent alive after processing the initial prompt, listening for incoming comms messages:Handle incoming messages
When comms messages arrive (via the inbox), they are injected as user messages and the agent runs again.
SDK / programmatic usage
Building a comms runtime
Building a comms runtime
config.comms.mode and creates the appropriate runtime:Inproc— callsCommsRuntime::inproc_only()Tcp— creates a full runtime withCommsRuntime::new()and starts TCP listenersUds— creates a full runtime withCommsRuntime::new()and starts UDS listeners
Composing tools with comms
Composing tools with comms
ToolGateway, registering send_message, send_request, send_response, and list_peers.Using AgentFactory
Using AgentFactory
comms_name, creates the runtime, composes tools, attaches the runtime to the agent, and records host_mode in SessionMetadata.Using CommsAgent directly
Using CommsAgent directly
For low-level control:
CommsBootstrap (sub-agent integration)
CommsBootstrap (sub-agent integration)
PreparedComms contains:runtime: CommsRuntime— ready to useadvertise: Option<CommsAdvertise>— for child agents, contains the name/pubkey/addr to register with the parent
Agent loop integration
Turn-boundary inbox draining
The agent loop callsdrain_comms_inbox() at turn boundaries:
- Calls
comms_runtime.drain_messages()to get formatted message strings - If messages exist, combines them and pushes a
UserMessageinto the session - Returns
trueif any messages were injected
Message injection format
Incoming messages are formatted as text for the LLM:- Message:
[COMMS MESSAGE from <peer>]\n<body> - Request:
[COMMS REQUEST from <peer> (id: <uuid>)]\nIntent: <intent>\nParams: <json>\n\nTo respond, use send_response with peer="<peer>", request_id="<uuid>" - Response:
[COMMS RESPONSE from <peer> (to request: <uuid>)]\nStatus: <status>\nResult: <json>
Security
- All messages are signed with Ed25519 using canonical CBOR encoding
- Trust is explicit: only messages from peers in
trusted_peers.jsonare accepted - Misaddressed messages are dropped: the receiver verifies
envelope.tomatches its own public key - Private keys are stored with restrictive permissions:
identity.keyis written with mode0600on Unix - Secret bytes are zeroized:
Keypair::from_secret()zeroizes the input after copying - ACK validation: ACK signatures, sender, recipient, and
in_reply_toID are all verified
See also
- Built-in tools reference - comms tool parameter details
- Configuration: comms - config file settings
- Examples: multi-agent comms - worked example with two communicating agents
