The problem nobody designed for
Most agent frameworks were built assuming a single agent talking to a single model, maybe calling a few tools. Nobody sat down early on and asked: how do AI agents communicate with each other, agent to agent, on an ongoing basis, across networks and NATs and restarts? That question got bolted on later, and it shows. If you've tried to wire two autonomous agents together — one triggering the other, or a swarm coordinating on a task — you've probably reached for one of three patterns, and hit the same wall each time.
This post walks through why those three patterns strain under real agent-to-agent (A2A) workloads, and what a persistent-tunnel approach changes.
Pattern 1: Polling
The simplest thing that works. Agent B exposes an endpoint (or a shared file, or a queue row) and Agent A checks it every N seconds.
while True:
result = requests.get("https://agent-b.example.com/status")
if result.json()["done"]:
break
time.sleep(5)
Polling is easy to write and easy to reason about, which is why almost every agent demo starts here. The problems only show up once you have more than one agent doing it: you're paying a request-response round trip for the common case (nothing changed yet), you're guessing at an interval that's either too slow (bad UX) or too fast (wasted cycles and rate limits), and every agent pair needs its own polling loop. It doesn't compose — N agents polling each other is O(N²) loops, all slightly out of sync.
Pattern 2: Webhooks
Webhooks flip the direction: instead of asking "are you done yet," Agent B calls Agent A's endpoint when something happens. This is strictly better for freshness — no polling delay — but it inherits every operational headache of "be a reachable HTTP server":
- Agent A needs a public URL, which means a public IP, a reverse proxy, or a tunnel service (ngrok, Cloudflare Tunnel) bolted on.
- If Agent A is behind NAT — a laptop, a container on a private network, a Raspberry Pi — it isn't reachable at all without extra infrastructure.
- Retries, signature verification, replay protection, and idempotency all become your responsibility, per integration.
- The moment Agent A moves (new IP, redeployed container, different cloud), every webhook consumer needs to be told the new URL.
Webhooks work well for one-directional, low-frequency events (a CI system pinging a chat channel). They get painful fast as a general A2A substrate, because "agent" implies bidirectional, ongoing, and often mobile — exactly what static webhook URLs are bad at.
Pattern 3: Message buses
A message bus (RabbitMQ, Kafka, Redis Streams, SQS) solves the reachability problem: agents publish and subscribe to a broker instead of talking to each other directly. No public IPs needed, decent durability, real backpressure handling.
The trade-off is that you're now running and operating a broker — and every agent that wants in needs broker credentials, topic/queue provisioning, and network access to the broker itself. It's a great pattern inside one deployment you control. It's a much harder sell for open agent-to-agent communication across organizational boundaries, where you don't want to hand a stranger's agent write access to your Kafka cluster, and they don't want to hand you access to theirs.
What these three have in common
Polling, webhooks, and message buses all solve transport — how bytes move — but none of them solve identity and trust: who is this agent, is it still the same agent it was yesterday, and do I actually want it talking to me. That gets patched on separately every time, usually with API keys or shared secrets passed out of band.
A different shape: a persistent, addressable tunnel
Pilot Protocol approaches A2A communication as a networking problem rather than an application-integration problem. Each agent gets a permanent virtual address that survives restarts, IP changes, and moving across clouds — so "how do I reach Agent B" stops being a per-integration question and becomes "dial its address," the way you'd reach a host on any normal network.
Under the hood:
- Transport is an encrypted UDP tunnel (X25519 key exchange, AES-GCM), with userspace reliability handled over UDP — no broker to run, no public port to expose.
- NAT traversal is built in: STUN plus hole-punching, with relay fallback when a direct path isn't available. An agent on a laptop behind a home router is reachable the same way as one on a cloud VM.
- Trust is explicit and mutual. Two agents handshake, and both sides have to approve before anything flows. This is the piece polling/webhooks/buses leave to you: membership on the network and trust to actually communicate are deliberately separate, so being reachable doesn't mean being open to everyone.
- Discovery works by name — a rendezvous registry and nameserver let you find an agent or a capability without hardcoding an IP.
Once two agents have a mutual handshake, sending a message is a single command, no broker or public endpoint required:
pilotctl handshake <peer-hostname> "collaborating on task X"
pilotctl send-message <peer-hostname> --data "status check"
That's the full loop — dial the address, send data, read the reply from your inbox. No polling interval to tune, no public URL to expose, no broker cluster to operate.
Where each approach still fits
To be fair to the alternatives: polling is genuinely fine for low-stakes, infrequent checks where simplicity beats freshness. Webhooks are a solid choice for one-directional notifications into a system that's already publicly reachable, like triggering a CI pipeline from a git push. Message buses remain the right call inside a single trust domain you control, where durability and fan-out matter more than cross-boundary reachability.
Where those patterns strain is the case that's becoming more common: independent agents, in different environments, that need to talk to each other on an ongoing basis without a shared operator standing up shared infrastructure first. That's the specific gap a permanent-address, trust-gated overlay network is built to close.
Trying it
Pilot Protocol is open source (Go, AGPL-3.0) with SDKs for Go, Python, Node, and Swift, plus an MCP server for agents already built on MCP. The full architecture — addressing, transport, NAT traversal, and the trust model — is written up in the documentation.
curl -fsSL https://pilotprotocol.network/install.sh | sh
FAQ
Do AI agents need a shared framework to communicate?
No. Agent-to-agent communication is a networking and trust problem, not a framework problem. Any two agents that can establish an encrypted, addressed channel and a mutual trust relationship can communicate, regardless of what each one is built with.
Is polling ever the right choice for A2A?
Yes, for low-frequency, low-stakes checks where simplicity matters more than latency. It just doesn't scale as the number of agent pairs or the required freshness increases.
What's the difference between a webhook and a persistent tunnel?
A webhook is a one-way HTTP callback to a fixed public URL — it breaks if the receiving agent moves or sits behind NAT. A persistent tunnel is bidirectional and addressed by identity rather than location, so it survives IP changes and doesn't require a public endpoint.
Does trust come for free just by joining a network?
Not in Pilot Protocol's model — membership and trust are deliberately decoupled. An agent can be reachable on the network without any other agent being able to talk to it until both sides approve a handshake.
Top comments (0)