Messaging
Send messages, transfer files, pipe data, and inspect your inbox.
On this page
Communication models
Pilot Protocol provides four ways to move data between agents. Each serves a different purpose:
| Model | Port | Use case | Delivery | Storage |
|---|---|---|---|---|
Streamconnect / send | Any (default 1000) | Interactive request-response, piping data | Synchronous, bidirectional | Not stored |
Data Exchangesend-message / send-file | 1001 | Typed messages, file transfer | Async, stored on arrival | ~/.pilot/inbox/ and ~/.pilot/received/ |
Pub/Subsubscribe / publish | 1002 | Fan-out events, monitoring | Real-time to active subscribers | Not stored |
Datagramdgram / SDK | Any | Fire-and-forget packets | Unreliable, no connection | Not stored |
This page covers stream, data exchange, and datagram. For pub/sub, see the Pub/Sub page.
Prerequisites
Trust or shared network required. Before any messaging works, both agents must either have mutual trust (via handshake) or belong to the same network. Without this, connection attempts are silently dropped.
connect
When to use: Quick one-shot queries. Send a message, get a response, done. The simplest way to talk to another agent.
Opens a stream connection to the target on port 1000 (stdio), sends the message, reads one response, and exits.
pilotctl connect other-agent --message "hello"
# Connect on a specific port
pilotctl connect other-agent 3000 --message "status?"
# With a timeout
pilotctl connect other-agent --message "ping" --timeout 10s
Returns: target, port, sent, response
send & recv
When to use: Targeting a specific service on a known port. Functionally identical to connect, but you must specify the port explicitly.
Sending to a specific port
pilotctl send other-agent 1000 --data "hello from my-agent"
Opens a connection to the specified port, sends the data, reads one response, exits.
Receiving messages
# Wait for one message on port 1000
pilotctl recv 1000
# Wait for 5 messages with timeout
pilotctl recv 1000 --count 5 --timeout 60s
Returns: messages [{seq, port, data, bytes}], timeout (bool)
Pipe mode
When to use: Feeding structured input (files, command output) to a remote agent. Without --message, connect reads from stdin - ideal for piping data from other commands.
echo "hello" | pilotctl connect other-agent
cat query.json | pilotctl connect other-agent 3000
echo '{"action":"status"}' | pilotctl connect other-agent 1000
Stdin must have data piped to it - interactive terminal input is not supported.
send-message
When to use: Structured typed messages that the recipient can read later. Unlike stream connections, messages are persisted to the inbox and survive disconnections. Use this when delivery matters more than real-time response.
Uses the data exchange protocol (port 1001). Messages are saved to ~/.pilot/inbox/ on the target.
# Text message (default)
pilotctl send-message other-agent --data "task complete"
# JSON message
pilotctl send-message other-agent --data '{"task":"analyze","input":"data.csv"}' --type json
# Binary message
pilotctl send-message other-agent --data "binary-payload" --type binary
Returns: target, type, bytes, ack
Inbox file format
Each message is stored as a JSON file in ~/.pilot/inbox/:
{
"type": "JSON",
"from": "0:0000.0000.0005",
"data": "{"task":"analyze"}",
"bytes": 18,
"received_at": "2026-01-15T10:30:00.123456-07:00"
}
The data field is the raw payload coerced to a JSON string — fine for text/JSON frames, but bytes > 0x7F are replaced with the Unicode replacement character (U+FFFD) by Go's JSON encoder. The type field reflects the frame type sent (TEXT, JSON, BINARY, FILE, or TRACE). received_at is RFC3339Nano in the daemon's local timezone.
For binary payloads (e.g. zlib-compressed envelopes) start the daemon with -dataexchange-b64. The inbox JSON then carries an additional data_b64 field with the lossless base64 encoding of the same bytes:
{
"type": "BINARY",
"from": "0:0000.0000.0005",
"data": "...mangled bytes...",
"data_b64": "eJzLSM3JyVcozy/KSQEAGAsEAQ==",
"bytes": 18,
"received_at": "2026-01-15T10:30:00.123456-07:00"
}
The flag is off by default to keep the inbox JSON compact for the common text/JSON case.
send-file
When to use: Transferring files directly to another agent. Files are delivered as typed frames with filename metadata and saved to ~/.pilot/received/ on the target.
pilotctl send-file other-agent ./report.pdf
pilotctl send-file other-agent ./data.json
Returns: filename, bytes, destination, ack
Maximum file size: 16 MB (data exchange protocol limit).
Inbox & received
Messages and files are stored locally and can be inspected at any time.
Check received files
pilotctl received # List received files
pilotctl received --clear # Delete all received files
Files are saved to ~/.pilot/received/.
Check inbox messages
pilotctl inbox # List inbox messages
pilotctl inbox --clear # Delete all messages
Messages are saved to ~/.pilot/inbox/.
dgram
When to use: Fire-and-forget signals — telemetry, heartbeats, real-time status. No ACK, no retry, no ordering, the receiver may miss the packet. If you care about delivery, use send-message or send instead.
pilotctl dgram other-agent 1234 --data "tick"
Sends a single UDP-style packet to the given port on the target. The receiver picks it up with pilotctl listen <port>.
broadcast
Send a datagram to every member of a network at once. The daemon fans the message out to each known member; delivery is best-effort (UDP-style — no per-recipient ACK).
pilotctl broadcast <network_id> <message> [--port <port>]
Returns the network ID, port used, and the number of bytes sent.