Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

A2A Protocol

Zeph includes an embedded A2A protocol server for agent-to-agent communication. When enabled, other agents can discover and interact with Zeph via the standard A2A JSON-RPC 2.0 API.

Quick Start

ZEPH_A2A_ENABLED=true ZEPH_A2A_AUTH_TOKEN=secret ./target/release/zeph

Endpoints

EndpointDescriptionAuth
/.well-known/agent.jsonAgent discoveryPublic (no auth)
/a2aJSON-RPC endpoint (message/send, tasks/get, tasks/cancel)Bearer token
/a2a/streamSSE streaming endpointBearer token

Set ZEPH_A2A_AUTH_TOKEN to secure the server with bearer token authentication. The agent card endpoint remains public per A2A spec.

Agent Card

The /.well-known/agent.json response includes a protocolVersion field set to "0.2.1". This allows discovery clients to verify compatibility before sending requests.

Configuration

[a2a]
enabled = true
host = "0.0.0.0"
port = 8080
public_url = "https://agent.example.com"
auth_token = "secret"
rate_limit = 60

Network Security

  • Payload limits: a2a.max_body_size caps request body (default: 1 MiB)
  • Rate limiting: per-IP sliding window (default: 60 requests/minute) with TTL-based eviction (stale entries swept every 60s, hard cap at 10,000 entries)

TLS enforcement and SSRF protection apply to outbound A2A connections (zeph --connect <URL>), configured under [a2a_client] — see Remote TUI below. This server section has no security-policy fields of its own.

Task Processing

Incoming message/send requests are routed through TaskProcessor, which implements streaming via ProcessorEvent:

#![allow(unused)]
fn main() {
pub enum ProcessorEvent {
    StatusUpdate { state: TaskState, is_final: bool },
    ArtifactChunk { text: String, is_final: bool },
}
}

The processor sends events through an mpsc::Sender<ProcessorEvent>, enabling per-token SSE streaming to connected clients. In daemon mode, AgentTaskProcessor bridges A2A requests to the full agent loop (LLM, tools, memory, MCP) via LoopbackChannel, providing complete agent capabilities over the A2A protocol.

Invocation-Bound Capability Tokens (IBCT)

IBCT are per-call security tokens that bind each A2A request to a specific task and endpoint. They prevent replayed or forwarded A2A requests from being accepted by other tasks or endpoints.

Enabling IBCT

Gated on the ibct feature flag (enabled in the full feature set):

[a2a]
ibct_ttl_secs = 300          # Token validity window (default: 300 s)

# Option A: inline key (dev/test only — prefer vault ref in production)
[[a2a.ibct_keys]]
key_id = "k1"
key_bytes_hex = "73757065722d73656372657400000000000000000000000000000000000000"

# Option B: vault reference (recommended for production)
ibct_signing_key_vault_ref = "ZEPH_A2A_IBCT_KEY"

When ibct_keys or ibct_signing_key_vault_ref is set, outgoing A2A client calls include an X-Zeph-IBCT header containing a base64-encoded JSON token.

Token Structure

Each token is HMAC-SHA256 signed and contains:

FieldDescription
key_idKey identifier (for rotation without downtime)
task_idA2A task the token is scoped to
endpointTarget endpoint URL
issued_atUnix timestamp of issuance
expires_atExpiry timestamp (issued_at + ibct_ttl_secs)
signatureHMAC-SHA256 over key_id + task_id + endpoint + timestamps

Key Rotation

Multiple keys can be listed in [[a2a.ibct_keys]]. The first key is used for signing; all keys are tried during verification. To rotate:

  1. Add the new key as the first entry (it will be used for new tokens).
  2. Keep the old key in the list temporarily (it will still verify existing tokens).
  3. After ibct_ttl_secs has elapsed, remove the old key.

A2A Client

Zeph can also connect to other A2A agents as a client:

  • A2aClient wraps reqwest, uses JSON-RPC 2.0 for all RPC calls
  • AgentRegistry with TTL-based cache for agent card discovery
  • SSE streaming via eventsource-stream for real-time task updates
  • Bearer token auth passed per-call to all client methods

Remote TUI (--connect)

zeph --tui --connect <URL> attaches a local TUI to a remote daemon’s /a2a/stream endpoint instead of running the agent loop in-process:

zeph --tui --connect http://127.0.0.1:8080/a2a/stream

The --connect client uses its own security policy, configured under [a2a_client]not the [a2a] section above, which only governs this process’s own A2A server. Loopback targets (127.0.0.1, ::1, localhost) always work over plain HTTP with no SSRF check, so the example above works against a fresh/default config with no manual overrides. Non-loopback targets still require HTTPS by default:

[a2a_client]
require_tls = true      # required for non-loopback targets; loopback is always exempt
ssrf_protection = true  # required for non-loopback targets; loopback is always exempt