
Peer-to-Peer Agent Communication Without an Application Broker
Many agent communication patterns put an application server in the middle. API gateways, message brokers, cloud pub/sub, and webhook relays forward every payload. This article explains the trade-offs and walks through Pilot Protocol's direct-preferred model, including its encrypted relay fallback.
The Problem With Middlemen
Hub-and-spoke architectures are the default for agent communication. Agent A sends a message to a central service. The central service forwards it to Agent B. This pattern has three systemic problems:
1. Latency doubles. Every message takes two hops: agent → server → agent. For real-time agent coordination, this latency compounds with every exchange in a conversation.
2. Single point of failure. If the broker goes down, all agent communication stops. Your agents might be healthy, but they cannot talk. The failure mode is total, not graceful.
3. The server sees everything. Even with TLS, the relay server terminates encryption. It can read, log, modify, or drop any message. For sensitive workloads (medical data, financial models, proprietary research), this is a compliance and trust problem.
The alternative: Agents connect directly when the network permits. When NAT prevents a direct path, the beacon can relay end-to-end encrypted packets without receiving plaintext. The registry and beacon still provide coordination services.
How Direct Connections Work
Pilot Protocol gives every agent a permanent 48-bit virtual address and a hostname. When Agent A wants to talk to Agent B, here is what happens:
- Hostname resolution. Agent A asks the registry for Agent B's virtual address and, when policy permits, its current endpoint.
- NAT traversal. Pilot tries the available path ladder: STUN-assisted direct reachability, coordinated UDP hole-punching, then relay fallback.
- Authorization. Peer trust or shared-network membership must permit the connection.
- Key agreement. Daemon-lifetime X25519 keys derive a peer-specific secret. AES-256-GCM protects subsequent data.
- Tunnel selection. UDP packets flow directly when possible; otherwise the beacon relays opaque encrypted packets.
The registry handles identity and discovery, while the beacon assists with STUN, hole-punching, and relay fallback. Neither service receives tunnel plaintext; a relay can still observe transport metadata such as timing and volume.
NAT Traversal: The Hard Part
The reason you often cannot "just open a socket" between two agents is NAT. Agents frequently run behind address translation or firewalls, so an inbound direct connection is not always available.
Pilot handles this with three tiers of traversal, tried in order. The daemon does not classify the NAT type up front; it tries a direct path, then a beacon-coordinated punch, then the relay (seven dial attempts in all: three direct, four relayed).
Tier 1: STUN Discovery (Full-Cone NAT)
The agent sends a probe to a STUN server (built into the Pilot beacon). The STUN response reveals the agent's public-facing IP and port. For full-cone NAT, this endpoint is valid for all peers - any agent can send UDP packets to it directly.
Tier 2: Hole-Punching (Restricted / Port-Restricted Cone)
For restricted cone NAT, the public endpoint only accepts packets from IPs it has previously sent to. Pilot coordinates simultaneous UDP sends from both agents (via the beacon), creating firewall pinholes that allow direct communication.
Tier 3: Relay (Symmetric NAT)
Symmetric NAT assigns a different port for every destination - hole-punching cannot work. Pilot automatically falls back to relay through the beacon. The relay forwards opaque encrypted packets: it cannot read the content, and AES-256-GCM makes any modification detectable.
To see which path each peer ended up on:
$ pilotctl peers --all
● 2 peers — 2 encrypted+authenticated · 1 relay · 1 direct
NODE ID ENCRYPTED AUTH PATH
238321 yes yes direct
238327 yes yes relay
The key insight: your application code is identical regardless of NAT type. You dial a hostname, Pilot figures out the traversal.
Private by Default: The Trust Model
On most agent platforms, agents are discoverable by default. Anyone can see them, enumerate them, and attempt connections. This is the opposite of what you want for production agents handling sensitive data.
Pilot agents are private by default:
- New agents withhold their reachable endpoint from arbitrary peers, although directory metadata may be visible.
- Peer trust or shared-network membership must authorize communication.
- Trust is bidirectional - both sides must agree. One-sided trust does nothing.
- Revoking peer trust drops the associated connection unless another network policy still grants access.
# Agent A asks Agent B for trust (by address: a private
# agent's hostname does not resolve for strangers)
$ pilotctl handshake 0:0000.0003.A2F7 "pipeline coordination"
handshake request sent to node 238327
# Nothing happens yet - Agent B approves the request
$ pilotctl approve 238321
trust established with node 238321
# Later: revoke trust
$ pilotctl untrust agent-b
trust revoked for node 238327
This model means agents choose who they talk to. No platform, no admin, no configuration file decides on their behalf. This is critical for autonomous agents that may interact with untrusted peers.
Full Walkthrough: Install to Data Exchange
Here is the complete flow from zero to two agents exchanging data.
Machine A (your laptop, behind home NAT):
# 1. Install
curl -fsSL https://pilotprotocol.network/install.sh | sh
# 2. Start daemon with a hostname
pilotctl daemon start --hostname agent-a
# Output:
# Daemon running (pid 48211)
# Address: 0:0000.0003.A2F1
# Hostname: agent-a
# Socket: /tmp/pilot.sock
# Logs: ~/.pilot/pilot-48211.log
Machine B (AWS EC2 instance):
# Same two commands
curl -fsSL https://pilotprotocol.network/install.sh | sh
pilotctl daemon start --hostname agent-b
Establish trust (from either machine):
# On Machine A (B's address, shared out of band)
pilotctl handshake 0:0000.0003.A2F7 "pipeline coordination"
# On Machine B (A's address)
pilotctl handshake 0:0000.0003.A2F1 "pipeline coordination"
# Two crossing requests are approved automatically: mutual trust is established.
Exchange data:
# Agent A sends a message to Agent B (port 1001, the data-exchange service)
pilotctl send-message agent-b --type json --data '{"task":"analyze","payload":"data..."}'
# On Machine B: read it
pilotctl inbox
Or programmatically in Go:
package main
import (
"github.com/pilot-protocol/common/driver"
"github.com/pilot-protocol/common/protocol"
"github.com/pilot-protocol/dataexchange"
)
func main() {
d, _ := driver.Connect("/tmp/pilot.sock")
defer d.Close()
// Resolve the peer's hostname to its pilot address
info, _ := d.ResolveHostname("agent-b")
addr, _ := protocol.ParseAddr(info["address"].(string))
// Port 1001 is the daemon's data-exchange service, which frames
// every message; use its client rather than raw writes
c, _ := dataexchange.Dial(d, addr)
defer c.Close()
// Returns once agent-b acknowledges; the message lands in its inbox
c.SendJSON([]byte(`{"task":"analyze","rows":1842}`))
}
A reply comes back the same way: Agent B sends a message to agent-a, and it lands in Agent A's inbox.
Or in Python:
import pilotprotocol as pilot
with pilot.Driver() as d:
# Frames the message for the data-exchange service on port 1001
result = d.send_message("agent-b", b'{"task": "analyze", "rows": 1842}', "json")
print(result)
Comparison: Direct vs. Hub-and-Spoke
| API Gateway / Broker | Pilot (Direct P2P) | |
|---|---|---|
| Data path | Agent → Server → Agent (2 hops) | Agent → Agent (1 hop when direct; the relay adds one) |
| Latency overhead | Server processing + extra hop | Network RTT on a direct path |
| Encryption | TLS to server, server decrypts | End-to-end, no decryption point |
| Single point of failure | Server down = all comms down | No broker in the data path; new lookups and relayed paths depend on the registry and beacon |
| Infrastructure | Server to deploy, maintain, scale | Daemon and CLI per agent, plus a shared registry and beacon (public or your own) |
| NAT handling | Server must have public IP | Automatic traversal, no public IP needed |
| Cost | Dedicated broker hosting + egress | No separate application broker to provision |
| Setup time | Broker config + credentials | One-command client installation |
When Direct P2P Makes Sense
Direct peer-to-peer is not always the answer. Here is when it shines:
- Sensitive data: Medical records, financial models, proprietary research. No intermediary should see this data.
- Low latency: Real-time agent coordination where every millisecond counts. Trading, robotics, live monitoring.
- Cross-network: Agents on different clouds or behind different NATs can use the shared coordination layer without a common application broker.
- Lower operational overhead: You do not want to deploy and maintain a dedicated broker for every agent workflow.
- Autonomous agents: Agents that need to choose their own peers without platform-level access control.
Hub-and-spoke still makes sense for broadcast patterns (one-to-many notifications), when you need message persistence (guaranteed delivery over days), or when all agents are in the same cloud VPC with millisecond latency requirements.
Get Started
# Install
curl -fsSL https://pilotprotocol.network/install.sh | sh
# Or with Python
pip install pilotprotocol
Install the client, start the daemon, and authorize the peers or networks that should connect.
