← Back to Blog

How Pilot Protocol Works

February 19, 2026 architecture deep-dive networking

Every modern agent framework makes the same assumption: your agent has a reachable HTTP endpoint. Google's A2A protocol publishes Agent Cards with URLs. Anthropic's MCP connects to servers over stdio or HTTP. LangChain agents call APIs. The entire ecosystem is built on the premise that agents can reach each other via IP addresses and domain names.

The problem is that most of them cannot.

88% of networks involve NAT. An agent running on a developer's laptop, behind a corporate firewall, inside a Docker container, or on a mobile device has no publicly routable address. It cannot receive incoming connections. It cannot be listed in a service registry. It is invisible to every other agent in the world.

Pilot Protocol is the layer that fixes this. It is a UDP overlay network that gives every AI agent a permanent virtual address, encrypted tunnels through NAT, and a cryptographic trust model -- all in a single binary with zero dependencies.

This post walks through the full architecture: how addresses work, what packets look like on the wire, how NAT traversal happens across three tiers, how encryption is negotiated, how trust is established, and what services run on top of the network.

The Problem: Agents Behind NAT Cannot Talk

Consider a simple scenario: you have two AI agents. Agent Alice is running on your laptop in San Francisco. Agent Bob is running on a colleague's workstation in Berlin. You want them to collaborate on a task -- Alice generates a plan, Bob executes it, and they coordinate results.

With HTTP, this is nearly impossible without infrastructure. Alice's laptop is behind a home router performing NAT. Bob's workstation is behind a corporate firewall. Neither has a public IP address. Neither can accept incoming connections.

The standard solutions all add operational overhead:

All of these solutions treat agent communication as a deployment problem. Pilot Protocol treats it as a networking problem and solves it at the protocol layer.

Addressing: 48-Bit Virtual Addresses

Every agent on the Pilot Protocol network receives a 48-bit virtual address. This address is permanent -- it does not change when the agent moves between networks, when its IP address changes, or when it reconnects after going offline.

The address is composed of two parts:

FieldSizePurpose
Network ID16 bitsIdentifies which network the agent belongs to
Node ID32 bitsUnique identifier assigned by the registry

The text format uses colon-separated hex: N:NNNN.HHHH.LLLL

For example, 0:0000.0000.0001 is node 1 on network 0 (the global backbone). The 16-bit network prefix supports up to 65,535 distinct networks, and within each network, the 32-bit node space supports over 4 billion agents.

Why Not IP Addresses?

IP addresses identify network interfaces, not agents. When an agent moves from Wi-Fi to cellular, its IP changes. When it restarts behind a different NAT, its port changes. When it migrates between cloud regions, everything changes.

Pilot addresses identify agents. An agent's address is derived from its registration with the network's rendezvous server. It persists across restarts, network changes, and migrations. Other agents can always reach it at the same address, regardless of where it physically runs.

Agents can also register hostnames -- human-readable names like alice or data-processor. The protocol's built-in nameserver resolves hostnames to addresses, so you can pilotctl ping alice instead of remembering hex addresses.

Design choice: 48 bits was intentional. It is large enough for global-scale networks but small enough to fit in a fixed-size packet header without variable-length encoding. The 16-bit network prefix enables network isolation -- agents on different networks cannot see each other, even on the same physical infrastructure.

The Packet: 34-Byte Fixed Header

Every Pilot Protocol packet carries a 34-byte fixed header. No variable-length fields, no options, no extensions. This makes parsing trivial and fast -- a single struct read in Go.

OffsetFieldSizeDescription
0Version1 byteProtocol version (currently 1)
1Flags1 byteSYN, ACK, FIN, RST, PSH, URG
2Protocol1 byteUpper-layer protocol identifier
3SrcAddr6 bytesSource virtual address (48-bit)
9DstAddr6 bytesDestination virtual address (48-bit)
15SrcPort2 bytesSource port number
17DstPort2 bytesDestination port number
19SeqNum4 bytesSequence number for ordering
23AckNum4 bytesAcknowledgment number
27Window2 bytesAdvertised receive window (flow control)
29Length2 bytesPayload length in bytes
31Checksum4 bytesCRC32 over header + payload

Here is what a SYN packet looks like on the wire, sent from 0:0000.0000.0001 port 1000 to 0:0000.0000.0002 port 7 (echo service):

# Pilot Protocol SYN packet (34 bytes, no payload)
# Offset  Hex                                     ASCII
00000000  01 02 00                                  Version=1 Flags=SYN Proto=0
00000003  00 00 00 00 00 01                          SrcAddr=0:0000.0000.0001
00000009  00 00 00 00 00 02                          DstAddr=0:0000.0000.0002
0000000F  03 E8                                      SrcPort=1000
00000011  00 07                                      DstPort=7 (echo)
00000013  00 00 00 01                                SeqNum=1
00000017  00 00 00 00                                AckNum=0
0000001B  FF FF                                      Window=65535
0000001D  00 00                                      Length=0 (no payload)
0000001F  A3 B2 C1 D0                                Checksum (CRC32)

The fixed header design means that every Pilot node -- from a Raspberry Pi to a cloud VM -- can parse packets with the same code, at the same speed. There is no negotiation, no capability exchange, no handshake to determine header format.

Connection Lifecycle

Connections follow a TCP-like lifecycle: SYN, SYN-ACK, ACK for setup, data exchange with sequence numbers and acknowledgments, and FIN for graceful teardown. The transport layer provides:

This gives application-layer code a reliable, ordered byte stream -- identical to what net.Conn provides in Go. In fact, Pilot connections implement the net.Conn interface directly, which means standard Go HTTP servers and clients work unmodified over Pilot tunnels.

NAT Traversal: Three Tiers

NAT traversal is the hardest part of any peer-to-peer system. Pilot Protocol solves it with a three-tier approach that handles every NAT type, from simple full-cone NATs to the most restrictive symmetric NATs.

The key component is the beacon -- a lightweight server that runs alongside the rendezvous registry on a public IP. The beacon serves three functions: STUN discovery, hole-punch coordination, and relay fallback.

Tier 1: STUN Discovery

When an agent starts, the first thing it does is discover its own public endpoint. It sends a STUN-like request to the beacon, which reflects back the source IP and port as seen from the public internet.

# Agent sends UDP packet to beacon
Agent --[UDP]--> Beacon (public IP)

# Beacon reads source address from UDP header
# and sends it back as the response payload
Beacon --[UDP]--> Agent: "your public endpoint is 203.0.113.42:54321"

The agent now knows its public IP:port mapping. It registers this endpoint with the rendezvous server. Other agents can look up this endpoint and attempt direct connections.

If the agent is behind a full-cone NAT, this endpoint is valid for all peers. Direct UDP packets to this address will be forwarded to the agent. STUN discovery is all that is needed.

Implementation detail: STUN discovery uses a temporary UDP socket, which is closed before the tunnel binds the same port. This avoids a race condition where the STUN response and tunnel data compete for the same socket.

Tier 2: Hole-Punching

For restricted-cone and port-restricted cone NATs, the STUN-discovered endpoint only works if the agent has already sent a packet to the connecting peer. The NAT will drop incoming packets from unknown sources.

Pilot solves this with beacon-coordinated hole-punching:

  1. Agent A wants to connect to Agent B
  2. Agent A sends a MsgPunchRequest to the beacon, naming Agent B as the target
  3. The beacon sends a MsgPunchCommand to both agents simultaneously
  4. Both agents send UDP packets to each other's STUN-discovered endpoints at the same time
  5. The outgoing packets "punch holes" in both NATs, and the incoming packets now have matching entries
  6. Direct communication is established

The timing is critical -- both sides must send nearly simultaneously for the holes to overlap. The beacon coordinates this by sending the punch commands in the same event loop iteration.

Tier 3: Relay Fallback

Symmetric NATs assign a different external port for every destination. The STUN-discovered endpoint is only valid for the beacon, not for other peers. Hole-punching fails because each side sees a different port.

For these cases, Pilot falls back to relay mode. The beacon acts as a packet relay, wrapping and forwarding traffic between agents:

# MsgRelay format: [0x05][senderNodeID(4)][destNodeID(4)][payload...]
Agent A --[relay]--> Beacon --[relay]--> Agent B

Relay mode is detected automatically. When an agent sends a DialConnection, it first attempts 3 direct retries. If those fail, it automatically switches to relay mode and attempts 3 relay retries through the beacon. The application layer never knows the difference -- it gets the same net.Conn interface either way.

The tradeoff is latency: relay adds one extra hop through the beacon. But it guarantees connectivity for agents behind the most restrictive NATs, including carriers that use CGNAT (Carrier-Grade NAT) for mobile networks.

The Tunnel Layer

All Pilot packets travel inside tunnel frames. A tunnel frame wraps a Pilot packet with metadata for the UDP transport layer:

# Tunnel frame format
50 49 4C 54    # Magic bytes: "PILT" (Pilot Tunnel)
XX XX          # Frame length (2 bytes)
[pilot packet] # 34-byte header + payload

The magic bytes serve as a frame delimiter. When reading from a UDP socket, the tunnel layer scans for 0x50494C54 ("PILT") to identify valid frames and discard noise. This makes the protocol robust against packet corruption and injection attempts.

Each agent maintains a single UDP socket for all tunnel traffic. Multiplexing happens at the Pilot layer -- the virtual source and destination addresses in the packet header determine which connection a packet belongs to. This means an agent can maintain thousands of connections to different peers over a single UDP port.

Encryption: X25519 + AES-256-GCM

Pilot Protocol encrypts all traffic by default. There is no unencrypted mode for production use. The encryption system uses X25519 for key exchange and AES-256-GCM for packet encryption, implemented entirely with Go's standard library -- zero external dependencies.

Key Exchange

When two agents establish a tunnel, they perform an X25519 Diffie-Hellman key exchange:

  1. Each side generates an ephemeral X25519 key pair
  2. Public keys are exchanged using PILK frames (magic bytes 0x50494C4B)
  3. Both sides compute the shared secret using their private key and the peer's public key
  4. The shared secret is used to derive the AES-256-GCM encryption key
# Key exchange frame
50 49 4C 4B    # Magic bytes: "PILK" (Pilot Key exchange)
[32 bytes]     # X25519 public key

Packet Encryption

Once the shared key is established, all subsequent tunnel frames use PILS framing (magic bytes 0x50494C53 -- "Pilot Secure") instead of plain PILT:

# Encrypted frame
50 49 4C 53    # Magic bytes: "PILS" (Pilot Secure)
[12 bytes]     # Nonce (random prefix + counter)
[N bytes]      # AES-256-GCM ciphertext + 16-byte auth tag

AES-256-GCM provides both confidentiality (encryption) and integrity (authentication). If a single bit is modified in transit, the GCM authentication tag will fail and the packet is discarded.

Replay Prevention

Each secure connection uses a random nonce prefix that is unique per peer pair. The nonce is constructed as: [4-byte random prefix][8-byte counter]. The random prefix ensures that even if two connections use the same counter value, they produce different nonces. The counter is incremented for every packet, preventing replay attacks.

Authentication Frames

The fourth magic byte variant is PILA (0x50494C41 -- "Pilot Authentication"), used during the initial handshake to verify agent identity using Ed25519 signatures before the encrypted channel is established.

Trust Model: Invisible by Default

Most agent frameworks treat discovery as a solved problem. A2A publishes Agent Cards to well-known URLs. MCP servers are configured by the client. The assumption is that agents want to be found.

Pilot Protocol takes the opposite approach: agents are invisible by default.

When a new agent joins the network, it is registered with the rendezvous server but marked as private. This means:

The Handshake

To establish trust, agents perform a mutual handshake using Ed25519 digital signatures:

  1. Agent A initiates a handshake to Agent B, including a justification message (why they want to connect)
  2. The handshake request is signed with Agent A's Ed25519 private key
  3. The request is relayed through the rendezvous server (since B might not be directly reachable yet)
  4. Agent B receives the request, verifies the signature, and decides whether to approve
  5. If approved, Agent B signs an approval response and sends it back
  6. Both agents now have a mutual trust pair -- they can resolve each other's endpoints and establish tunnels
# Initiate a handshake
$ pilotctl handshake bob "I need access to your data processing service"

# On Bob's side: approve the request
$ pilotctl approve 0:0000.0000.0003

Agents can also configure auto-approval rules -- for example, automatically trusting any agent that provides a specific justification pattern, or auto-approving all agents on the same network.

Revocation

Trust can be revoked instantly with pilotctl untrust. This removes the trust pair, tears down any active tunnels, and notifies the peer. The revoked agent can no longer resolve the revoker's endpoint or establish new connections.

For a deeper exploration of the trust model, see Why Agents Should Be Invisible by Default.

What Runs on Top: Port-Based Services

Like TCP/IP, Pilot Protocol uses port numbers to multiplex multiple services over a single agent address. Several well-known ports are defined:

PortServiceDescription
7EchoPing/pong for connectivity testing and latency measurement
53DNSHostname resolution within the overlay network
80HTTPStandard HTTP services over Pilot tunnels
443SecureEncrypted services (X25519 + AES-GCM per connection)
1000StdioInteractive terminal sessions between agents
1001Data ExchangeStructured data and file transfer
1002Event StreamPub/sub event distribution with topic routing
1003Task SubmitTask delegation with capability matching

The port system means agents can expose multiple services simultaneously. An agent might accept task submissions on port 1003, publish status updates on port 1002, and serve an HTTP API on port 80 -- all on the same virtual address.

HTTP Over Pilot

Because Pilot connections implement Go's net.Conn interface, standard HTTP servers work without modification:

// Standard Go HTTP server running on a Pilot port
listener := pilot.Listen(daemon, 80)
http.Serve(listener, myHandler)

The gateway component bridges Pilot services to the local network. It assigns a loopback IP alias for each Pilot address and listens on common ports, proxying traffic between local HTTP clients and Pilot tunnels. This means existing tools -- curl, browsers, REST clients -- can interact with agents on the Pilot network without any modification. See the Gateway documentation for details.

Pub/Sub and Event Streams

Port 1002 provides a built-in publish/subscribe system with topic routing and wildcard subscriptions. Agents can subscribe to event streams from trusted peers and publish events to subscribers without external message brokers. See Pub/Sub documentation for the full API.

Task Submission

Port 1003 implements a task submission protocol. Agents can advertise capabilities by setting a task-ready flag during registration. Other agents discover capable peers, submit tasks, and receive results -- all over encrypted Pilot tunnels. This is the foundation for building decentralized agent marketplaces without a central orchestrator.

Putting It All Together

Here is the full flow when Agent Alice sends a message to Agent Bob:

  1. Alice starts -- her daemon performs STUN discovery, registers with the rendezvous server, and gets virtual address 0:0000.0000.0003
  2. Trust -- Alice and Bob have already completed a mutual handshake (Ed25519 signed)
  3. Resolve -- Alice asks the rendezvous server for Bob's endpoint. Because they are trusted, the server returns Bob's public IP:port
  4. Tunnel -- Alice's daemon opens a UDP tunnel to Bob's endpoint. If NAT blocks direct connection, the beacon coordinates hole-punching or falls back to relay
  5. Encrypt -- X25519 key exchange produces a shared secret. All subsequent frames use AES-256-GCM with PILS framing
  6. Connect -- Alice opens a connection to Bob on port 1001 (Data Exchange). SYN/SYN-ACK/ACK over the encrypted tunnel
  7. Send -- The message is segmented if needed, transmitted with sequence numbers, acknowledged, and reassembled on Bob's side
  8. Receive -- Bob's application reads the message from the net.Conn interface, exactly as if it were a regular TCP connection

All of this happens in milliseconds. The agent developer writes standard Go networking code. The Pilot daemon handles everything else: address resolution, NAT traversal, encryption, flow control, and reliable delivery.

226 tests validate every layer of the stack, from packet serialization to end-to-end NAT traversal. The protocol is implemented in pure Go with zero external dependencies. See the Core Concepts documentation for the complete technical reference.

Try Pilot Protocol

Set up a multi-agent network in under 5 minutes. One binary, zero dependencies.

View on GitHub