By Sagar Shankaran, Founder of CallSphere
Master transport security for multi-agent systems including TLS configuration, mutual TLS authentication, encrypted message passing between agents, service mesh integration, and zero-trust networking principles.
Key takeaways
In a multi-agent system, agents exchange messages that contain user data, reasoning traces, tool call results, and coordination instructions. If these messages travel over unencrypted channels, any network-level attacker can eavesdrop on sensitive data or inject malicious messages that alter agent behavior.
The threat model for agent-to-agent communication includes passive eavesdropping, message tampering, replay attacks, and impersonation. A compromised agent that can forge messages from a trusted agent can escalate privileges across the entire system. Transport security is the first defense layer.
When agents communicate over HTTP, enforce TLS 1.3 as the minimum protocol version. Configure your agent servers to reject older protocol versions and weak cipher suites:
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
sequenceDiagram
autonumber
participant A as Agent A
participant SPIRE as SPIFFE / SPIRE
participant B as Agent B
A->>SPIRE: Request SVID identity
SPIRE-->>A: Short lived X.509 SVID
B->>SPIRE: Request SVID identity
SPIRE-->>B: Short lived X.509 SVID
A->>B: TLS hello + client cert
B->>B: Verify SPIFFE ID + policy
B-->>A: TLS finished
A->>B: Authenticated RPC
B-->>A: Response
Note over A,B: Tokens rotated automatically<br/>every few minutes
import ssl
import aiohttp
from aiohttp import web
def create_tls_context() -> ssl.SSLContext:
"""Create a strict TLS context for agent servers."""
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.minimum_version = ssl.TLSVersion.TLSv1_3
ctx.load_cert_chain(
certfile="/etc/certs/agent-server.crt",
keyfile="/etc/certs/agent-server.key",
)
ctx.load_verify_locations("/etc/certs/ca.crt")
# Disable compression to prevent CRIME attacks
ctx.options |= ssl.OP_NO_COMPRESSION
return ctx
async def agent_message_handler(request: web.Request) -> web.Response:
"""Handle incoming agent-to-agent messages over TLS."""
payload = await request.json()
agent_id = payload.get("sender_agent_id")
message = payload.get("message")
# Process the inter-agent message
result = await process_agent_message(agent_id, message)
return web.json_response({"status": "ok", "result": result})
async def process_agent_message(agent_id: str, message: dict) -> dict:
return {"acknowledged": True, "from": agent_id}
app = web.Application()
app.router.add_post("/agent/message", agent_message_handler)
# Run with TLS
ssl_ctx = create_tls_context()
# web.run_app(app, ssl_context=ssl_ctx, port=8443)
Standard TLS authenticates only the server. In multi-agent systems, you need mutual TLS (mTLS) so that each agent proves its identity to the other. Both the client and server present certificates signed by a trusted certificate authority:
def create_mtls_server_context() -> ssl.SSLContext:
"""Server context that requires client certificates."""
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.minimum_version = ssl.TLSVersion.TLSv1_3
ctx.load_cert_chain(
certfile="/etc/certs/server.crt",
keyfile="/etc/certs/server.key",
)
ctx.load_verify_locations("/etc/certs/ca.crt")
# Require client certificate
ctx.verify_mode = ssl.CERT_REQUIRED
return ctx
def create_mtls_client_context() -> ssl.SSLContext:
"""Client context that presents its own certificate."""
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.minimum_version = ssl.TLSVersion.TLSv1_3
# Client presents its certificate
ctx.load_cert_chain(
certfile="/etc/certs/agent-client.crt",
keyfile="/etc/certs/agent-client.key",
)
ctx.load_verify_locations("/etc/certs/ca.crt")
return ctx
async def send_secure_message(target_url: str, message: dict) -> dict:
"""Send a message to another agent using mTLS."""
ssl_ctx = create_mtls_client_context()
async with aiohttp.ClientSession() as session:
async with session.post(
target_url,
json=message,
ssl=ssl_ctx,
) as response:
return await response.json()
TLS protects data in transit, but messages may pass through intermediaries such as message queues or logging systems. Add payload-level encryption so messages remain confidential even at rest:
import json
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
class AgentMessageEncryptor:
"""Encrypts and decrypts agent messages using AES-256-GCM."""
def __init__(self, shared_key: bytes):
if len(shared_key) != 32:
raise ValueError("Key must be 32 bytes for AES-256")
self.aesgcm = AESGCM(shared_key)
def encrypt(self, message: dict, associated_data: bytes = b"") -> dict:
"""Encrypt a message dict, returning nonce + ciphertext."""
plaintext = json.dumps(message).encode("utf-8")
nonce = os.urandom(12) # 96-bit nonce for GCM
ciphertext = self.aesgcm.encrypt(nonce, plaintext, associated_data)
return {
"nonce": nonce.hex(),
"ciphertext": ciphertext.hex(),
"aad": associated_data.hex(),
}
def decrypt(self, encrypted: dict) -> dict:
"""Decrypt a message dict."""
nonce = bytes.fromhex(encrypted["nonce"])
ciphertext = bytes.fromhex(encrypted["ciphertext"])
aad = bytes.fromhex(encrypted.get("aad", ""))
plaintext = self.aesgcm.decrypt(nonce, ciphertext, aad)
return json.loads(plaintext.decode("utf-8"))
# Usage
key = os.urandom(32) # In production, load from a secrets manager
encryptor = AgentMessageEncryptor(key)
message = {"tool_result": "Patient record #4521", "confidence": 0.95}
encrypted = encryptor.encrypt(message, associated_data=b"agent-A-to-agent-B")
decrypted = encryptor.decrypt(encrypted)
Zero trust means no agent is trusted by default, regardless of network location. Every request is authenticated, authorized, and encrypted. Implement zero trust by combining mTLS identity with per-request authorization tokens:
import hashlib
import hmac
import time
class ZeroTrustMiddleware:
"""Validates agent identity and request authenticity."""
def __init__(self, signing_secret: str, max_age_seconds: int = 30):
self.signing_secret = signing_secret.encode()
self.max_age = max_age_seconds
def sign_request(self, agent_id: str, payload: bytes) -> dict:
"""Create signed headers for an outgoing agent request."""
timestamp = str(int(time.time()))
message = f"{agent_id}:{timestamp}:".encode() + payload
signature = hmac.new(self.signing_secret, message, hashlib.sha256).hexdigest()
return {
"X-Agent-ID": agent_id,
"X-Timestamp": timestamp,
"X-Signature": signature,
}
def verify_request(self, headers: dict, payload: bytes) -> bool:
"""Verify incoming request authenticity."""
agent_id = headers.get("X-Agent-ID", "")
timestamp = headers.get("X-Timestamp", "0")
signature = headers.get("X-Signature", "")
# Check timestamp freshness to prevent replay attacks
age = abs(int(time.time()) - int(timestamp))
if age > self.max_age:
return False
expected_message = f"{agent_id}:{timestamp}:".encode() + payload
expected_sig = hmac.new(
self.signing_secret, expected_message, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_sig)
Use mTLS when agents run as long-lived services that need continuous authenticated communication — it provides strong identity without per-request credential management. Use API keys when agents are short-lived or when you need simpler key rotation. In high-security environments, combine both: mTLS for transport identity and API keys for application-level authorization.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Use a certificate management system like cert-manager in Kubernetes or Vault PKI. Issue short-lived certificates (hours or days, not years) and rotate them automatically before expiry. Configure your TLS contexts to reload certificates from disk without restarting the process. Service meshes like Istio handle this transparently.
It depends on your threat model. TLS protects data in transit between two endpoints, but if messages are stored in a queue, logged, or pass through a proxy, they are decrypted at those points. Payload encryption provides end-to-end confidentiality between the sending and receiving agents regardless of the transport path.
#TLS #MTLS #AgentCommunication #ZeroTrust #ServiceMesh #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Google donated the Agent-to-Agent (A2A) protocol to the Linux Foundation at Cloud Next 2026. What this means for vendor neutrality and your agent stack.
Agents inherit user privileges in dangerous ways. The 2026 zero-trust patterns for agent permissions — scoped tokens, just-in-time access, audit.
TLS handshakes, WebSocket upgrades, and SIP registrations each cost 100-400ms cold. We design persistent connection pools to ASR, LLM, TTS, and CRM endpoints so the first turn pays nothing.
Run an AI agent fleet behind Istio Ambient with the Gateway API Inference Extension, or Linkerd for the simpler path. mTLS, traffic split, and KV-cache-aware routing.
The 2026 NPRM names network segmentation explicitly. Here is how a HIPAA-aligned AI voice platform segments call workers, ASR, LLM gateways, and EHR connectors with zero-trust micro-segmentation.
How agents communicate in multi-agent systems using direct message passing, shared blackboard, event-driven pub/sub, and MCP-based tool sharing with production code examples.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco