The problem nobody names correctly
Ask ten engineers "how do AI agents communicate with each other" and you'll get ten different half-answers: "webhooks," "a message queue," "they don't, really," or "whatever the framework does." That vagueness is the actual problem. Agent-to-agent (A2A) communication is still being bolted together out of primitives designed for a different era of software — one where the caller and the callee were both stable, always-on, publicly addressable servers.
Agents aren't that. They spin up in containers, move between clouds, sit behind NAT, restart on crash, and often don't have a stable IP for more than a few minutes. So "how do AI agents communicate with each other" isn't really a protocol question first — it's an addressing and reachability question. Get that wrong and every layer on top (message format, auth, retries) inherits the fragility.
This piece walks through the three patterns people actually reach for — polling, webhooks, and message buses — what breaks in each once agents are the endpoints, and where a persistent, addressed tunnel changes the shape of the problem instead of papering over it.
Pattern 1: Polling
Polling is the first thing everyone tries because it needs nothing new: agent A calls an HTTP endpoint on agent B every N seconds and reads the response.
while True:
resp = requests.get("https://agent-b.example.com/status")
if resp.json()["ready"]:
handle(resp.json())
time.sleep(5)
It works until it doesn't. The failure modes are boring but real:
- Latency is bounded by your poll interval, not by how fast the other agent actually finished. Poll every 5s, you wait up to 5s for no reason.
- Agent B has to be reachable at a fixed address for the whole conversation. If B is behind NAT, in a serverless function, or on a laptop, there's no stable place for A to poll.
- N agents polling M agents is N×M standing HTTP connections (or repeated connection setup), most of which return "nothing new" — pure waste.
Polling is fine for "check once in a while whether a long job finished." It's the wrong shape for anything resembling a conversation.
Pattern 2: Webhooks
Webhooks flip the direction: instead of A asking B repeatedly, B calls A back when something happens. This is strictly better for latency and load — no wasted round trips — but it inherits a harder requirement: A must expose a public, reachable HTTP endpoint that B can call.
That's a reasonable ask for a SaaS platform with a stable domain and a load balancer. It's a much harder ask for an agent:
- If A is behind NAT (most laptops, most containers on internal networks), there's no public endpoint to give B — you now need port forwarding, a reverse proxy, or a tunneling service like ngrok, each with its own auth and lifecycle to manage.
- If A restarts or gets rescheduled onto a different host, its public URL can change, and every peer holding the old webhook URL needs updating.
- Webhook delivery is typically fire-and-forget over plain HTTPS — you're responsible for building your own retry, ordering, and replay-protection logic on both ends, because the transport itself doesn't guarantee any of that.
Webhooks solved the "who calls whom" problem for stable web services. They don't solve the "how does a agent get a stable address in the first place" problem, which is the one agents actually have.
Pattern 3: Message buses (Kafka, RabbitMQ, SQS, etc.)
Message buses look like they solve the addressing problem — agents publish to and subscribe from a topic instead of dialing each other directly, so nobody needs a public endpoint. That's genuinely useful, and it's why buses are a common default in multi-agent frameworks.
But you've traded a reachability problem for an operations problem:
- Someone runs the broker. It's a dependency every agent in the system needs network access to, needs credentials for, and needs to survive if the conversation is going to survive.
- The broker becomes a trust boundary you have to manage separately from the agents themselves. Access control is usually topic-level, not peer-level — agent A publishing to a topic often can't cleanly express "only agent B should ever read this," without extra plumbing (per-consumer queues, encryption at the message level, ACL rules).
- It's a good fit for fan-out and durable queues, a worse fit for a live back-and-forth session between two specific agents, where you mostly want direct, low-latency delivery, not a durable log both sides poll.
None of these three patterns is "wrong" — they're each right for the job they were designed for (checking a status, receiving a one-off event, distributing work at scale). The mismatch shows up when you try to use any of them as the general-purpose transport for two arbitrary agents to have a real conversation, especially when either side might be behind NAT, might restart, and needs the other side to actually trust it before exchanging anything sensitive.
What agents actually need
Strip away the specific technology and the requirements for A2A communication are pretty consistent:
- A stable address that doesn't change when the agent restarts, moves clouds, or changes IPs.
- Reachability despite NAT — most agents are not sitting on a public IP, and shouldn't need to be.
- Encryption in transit without hand-rolling TLS termination and cert management per agent.
- Explicit trust — knowing an address exists is not the same as trusting what's behind it. Membership in a network and permission to talk shouldn't be conflated (that's the mistake a lot of VPN-shaped setups make).
- A way to discover who else is out there and what they can do, without a shared config file that goes stale.
This is where a persistent, addressed overlay tunnel is a genuinely different shape of solution rather than a repackaging of webhooks or a bus. Pilot Protocol takes this angle directly: every agent gets a permanent virtual address that survives restarts and IP changes, agents talk over encrypted UDP tunnels with NAT traversal (STUN + hole-punching + relay fallback) instead of needing a public endpoint, and trust is a separate, explicit per-peer handshake — being findable on the network doesn't mean you're trusted, and being trusted doesn't require any central broker to survive in the middle of the conversation. Discovery works through a rendezvous registry, so agents can find each other and each other's capabilities by name instead of a hardcoded IP or a shared queue config.
It's worth being fair here: Pilot isn't a replacement for Kafka-style durable event logs, and it's not trying to be — if you need guaranteed at-least-once delivery to a hundred downstream consumers with replay, a bus is still the right tool. What it replaces is the layer underneath direct A2A conversation: the part where two agents need to find each other, stay reachable, and trust each other without a human wiring up ngrok tunnels or broker ACLs by hand.
Trying it
If you want to see the addressing model rather than take it on faith, the fastest path is the CLI:
curl -fsSL https://pilotprotocol.network/install.sh | sh
pilotctl daemon start
pilotctl send-message list-agents --data '/data {"search":"weather"}' --wait
That last command is talking to a live agent on the network — no webhook URL configured, no broker credentials, no public IP on either end. The docs at /docs cover the trust handshake model and NAT traversal in more depth if you're evaluating this as infrastructure for a multi-agent system you're building.
FAQ
Do AI agents need a shared protocol format to communicate, or just a transport?
Both matter, but transport comes first. A message format like an "agent card" or a JSON schema is useless if the agents can't reliably reach each other's address in the first place. Solve reachability, then standardize the payload shape.
Is a message bus ever the right choice for A2A communication?
Yes — for fan-out (one agent's output feeding many downstream consumers) or when you need a durable, replayable log of what happened. It's a worse fit for a live, low-latency exchange between two specific agents that need to trust each other directly.
What's the difference between network membership and trust in an agent overlay?
Membership means an agent has an address and is discoverable. Trust means another agent has explicitly agreed to accept messages from it. Conflating the two (as some VPN-style setups do — "joined the network" equals "trusted by everyone on it") removes the ability to have fine-grained, per-peer permissions.
Can agents behind NAT talk to each other at all without a public server in the middle?
Yes, with NAT traversal techniques (STUN to discover the mapping, hole-punching to establish a direct path, relay as a fallback when direct fails). This is standard in real-time communication protocols like WebRTC, and it's the same class of technique that lets agents reach each other without either side needing a public IP or port-forwarding.
Top comments (0)