Skip to main content
Version: Latest (1.3.x dev)

A2A Governance Plane

Since 1.3.0, DVARA governs the Agent-to-Agent (A2A) protocol as a first-class plane — a peer to the LLM plane (the gateway) and the MCP plane (the MCP Proxy), with its own front door: the A2A Proxy on port 8075.

When one agent delegates to another — "researcher, summarize this document" — that call is a hop. DVARA sits in the middle of the hop and governs it: it authenticates the caller, resolves the target agent from a registry, checks a per-hop policy, scans the message for PII, watches for runaway delegation loops, forwards the call to the peer with the right credentials, and writes a tamper-evident audit record of the whole thing. The peer's reply flows back to the caller.

The boundary: one hop, not the swarm

DVARA governs one hop — agent A calling agent B — and returns B's reply to A. It makes no onward call and runs no agent loop. Orchestration — deciding who calls whom next, and how many times — stays with your agent framework. DVARA is the governed checkpoint each delegation passes through, not the brain of the swarm.

This is a deliberate line. It keeps the plane stateless per hop (so it scales horizontally across pods), keeps governance auditable (every hop is a discrete, signed record), and keeps DVARA out of the business of orchestration logic that belongs to your application.

The governed hop

An A2A client sends a JSON-RPC 2.0 message/send to the A2A Proxy:

POST /a2a/{agentId}/message:send
Authorization: Bearer dvara_... # the calling agent's DVARA API key
X-Session-Id: <session> # correlates a chain of hops
X-Dvara-Delegated-Authorization: Bearer … # optional on-behalf-of token

Every hop runs through the same ordered governance chain before it reaches the peer:

StageWhat it enforces
AuthenticationThe caller's DVARA API key is resolved to a tenant and principal. Anonymous hops are rejected.
Registry resolution{agentId} is resolved to a registered peer agent (tenant-scoped). The credential (or on-behalf-of token) for the peer is prepared here.
Audit — intentAn A2A_HOP_INTENT record is written before the hop is forwarded, so an interrupted call still leaves a trace.
PolicyA per-hop policy decides whether this source agent may invoke this skill on this target. A denial stops the hop.
PIIEach outbound message part is scanned. Depending on tenant policy, PII is redacted before it reaches the peer, or the hop is blocked outright. The peer's reply is scanned on the way back.
Loop detectionA runaway delegation chain (A→B→A cycles, repetition, rate) is detected and the session is killed before it can spiral.
Upstream connectorThe hop is forwarded to the peer's endpoint with the right auth scheme injected. Upstream errors are surfaced fail-soft, never silently swallowed.
Audit — resultAn A2A_HOP_RESULT (or A2A_HOP_DENIED) record and a per-hop metering row close out the hop.

The A2A plane is fully independent of the MCP plane: it has its own governance engines, its own registry, and its own audit chain, all expressed in agent/skill/message terms rather than server/tool. A2A checks can evolve without touching MCP, and vice-versa.

Streaming hops (message/stream)

Long-running delegations stream. When agent B works incrementally — "researching…", "drafting…", partial results — it streams back over Server-Sent Events instead of making the caller block for the whole reply. DVARA governs streaming hops too, at POST /a2a/{agentId}/message:stream:

  • The same pre-stream governance runs before the peer is contacted — authentication, registry resolution, per-hop policy, PII scan of the outbound message, loop detection, and the session kill-switch. A rejection is surfaced as a single SSE error event; the peer is never called.
  • The peer's streamed events are then PII-enforced in flight: personal data is redacted before it reaches the client, or — if the tenant's policy is to block — the stream is terminated with a content_filter event so leaked PII never gets through.
  • At stream end DVARA writes an A2A_STREAM_SUMMARY audit record and a per-hop metering row.

So a streaming hop carries the same PII and audit guarantees as a synchronous one — streaming is a transport choice, not a governance gap.

Task lifecycle (tasks/*)

A long-running delegation produces a task the caller can follow up on — get its state, list tasks, cancel it, or re-attach to its live stream. DVARA governs these too, at POST /a2a/{agentId}/tasks:get, tasks:list, tasks:cancel, and tasks:resubscribe:

  • Each runs a tenant-scoped chain — authenticated caller, registry resolution (so you can only reach agents registered in your tenant), per-hop policy, and an audit record (A2A_TASK_GET / _LIST / _CANCEL / _RESUBSCRIBE).
  • Task read responses are PII-scanned — a returned task can carry message content — and tasks:resubscribe reuses the same in-flight streaming PII guard.

Push notifications

Instead of polling or holding a stream open, a caller can register a webhook the peer will call with task updates. DVARA governs that config CRUD at POST /a2a/{agentId}/pushConfig:set, pushConfig:get, pushConfig:list, and pushConfig:delete — same tenant-scoped auth + policy + audit. On set, the tenant-supplied webhook URL is SSRF-validated before the config ever reaches the peer: a URL pointing at loopback, private, link-local, or cloud-metadata space is rejected and never forwarded — the same egress guard DVARA applies to webhook and MCP-server URLs elsewhere.

The agent registry

DVARA only forwards hops to agents you have registered — an "address book" of peer agents, scoped per tenant. Each entry carries the peer's endpoint, its authentication scheme, a credential reference, and an optional skills allow-list.

Register agents through the Automation API or the Flightdeck:

  • Automation APIPOST /v1/admin/a2a/agents (and the usual GET/PUT/DELETE).
  • DVARA ConsoleAgents → A2A Agents (/a2a/agents), for platform operators.
  • Tenant PortalAgents → A2A Agents (/portal/a2a/agents), for tenant self-service. Tenant-supplied endpoint URLs are SSRF-validated (private, loopback, and cloud-metadata addresses are rejected).
{
"agent_id": "researcher",
"endpoint_url": "https://researcher.example.com/a2a",
"auth_scheme": "OAUTH2",
"auth_mode": "STORED",
"credential_ref": "vault://secret/a2a/researcher",
"skills_allow_list": ["summarize", "translate"]
}

Authenticating to the peer

DVARA speaks the five A2A security schemes to each peer — API_KEY, HTTP (bearer), OAuth2, OpenID Connect, and mutual TLS — and injects the right one per agent. Two auth modes decide whose credential is used:

  • STORED — DVARA resolves the agent's own credential (from a vault reference or environment) and presents it. The peer sees "DVARA, acting for this tenant."
  • DELEGATED — DVARA forwards the caller's on-behalf-of token (X-Dvara-Delegated-Authorization) to the peer, so the peer enforces the end user's permissions. A delegated agent called without the token is rejected.

Agent Card discovery

An A2A client can fetch a peer's capabilities through DVARA:

GET /a2a/{agentId}/.well-known/agent-card.json

DVARA authenticates the caller, fetches and caches the peer's public Agent Card, and filters the advertised skills by the tenant's allow-list — so a tenant only sees the skills it's allowed to invoke. The card is refreshed on a TTL, or immediately with ?refresh=true.

Per-hop policy

An A2A policy answers a single question per hop: may this source agent invoke this skill on this target? Policies are authored as YAML rules, versioned, and tenant-scoped (a global policy applies to every tenant):

rules:
- match: { source: "billing-*", target: "researcher", skill: "summarize" }
effect: ALLOW
- match: { target: "payments", skill: "transfer" }
effect: DENY
reason: "Money movement requires human approval, not agent delegation"

Policies are default-allow with first-match-wins: put narrow denials before broad allows. Manage them via POST /v1/admin/a2a/policies (with status lifecycle, versioning, rollback, and a dry-run validator) or the Console. Changes propagate to the fleet within a poll interval — no restart.

Tamper-evident audit

Every hop is recorded on a tamper-evident, HMAC hash-chained audit trail — the same forensic-grade chaining the LLM and MCP planes use, but in its own store so the two streams stay independently auditable. Each record links to the previous one by hash, so any insertion, deletion, or edit breaks the chain and is detectable.

The A2A plane emits:

EventWhen
A2A_HOP_INTENTBefore a hop is forwarded to the peer
A2A_HOP_RESULTOn a successful reply
A2A_HOP_DENIEDWhen policy, PII, a loop, or a killed session rejects the hop
A2A_DELEGATION_RECORDEDOn a delegated (on-behalf-of) hop — the token is recorded by fingerprint, never in the clear
A2A_PII_DETECTEDWhen PII is redacted or blocked on a message
A2A_LOOP_DETECTEDWhen a runaway delegation loop is detected and the session killed
A2A_CARD_DISCOVEREDWhen a caller fetches a peer's Agent Card

Because A2A audit is a separate store, these events do not appear in the shared audit viewer. Browse them at Console → Agents → A2A Audit (/a2a/audit, cross-tenant, with the HMAC tamper-evidence surfaced per event) or Portal → Agents → A2A Audit (/portal/a2a/audit, scoped to the tenant's own hops).

Even though it lives in its own store, the A2A chain is not invisible to compliance. Every generated compliance report — SOC 2, HIPAA, GDPR, and the India RBI / SEBI reports — carries an A2A governance section: agent-to-agent hop volume, per-hop policy denials, PII actions, and loop detections for the reporting window (tenant-scoped like the rest of the report), plus an A2A chain-integrity check that verifies the A2A trail's tamper-evidence the same way the report verifies the shared chain. So an auditor sees agent-to-agent activity — and its forensic integrity — alongside the LLM and MCP evidence in one document, without leaving the report.

Configuration reference

The A2A Proxy runs like the other planes — it requires a license and a database, and shares the standard actuator/observability configuration. A2A-specific settings:

PropertyDefaultPurpose
a2a.discovery.card-ttl-seconds3600How long a cached Agent Card is served before the next discovery re-fetches it
a2a.discovery.fetch-timeout-seconds10HTTP timeout for the peer Agent Card fetch

The per-tenant skills allow-list is not a property — it lives on each agent registry entry (skills_allow_list). Per-hop policy lives in the A2A policy store, managed via the Automation API or Console.

Current limitations

  • Every A2A operation is governed in 1.3.0 — messages (sync + streaming), Agent Card discovery, the task lifecycle, and push-notification config — with PII enforced in flight and webhook URLs SSRF-validated. Content-safety guardrails on the A2A stream (beyond PII) are on the roadmap.
  • Public Agent Card discovery. DVARA fetches the peer's public card; the authenticated "extended card" is a follow-up.
  • OAuth2 / OIDC use a stored token. For STORED agents on these schemes, DVARA presents the configured credential as a bearer token; full grant-flow acquisition is a follow-up. Per-agent mTLS client certificates are a follow-up (the connector's default TLS context is used today).
  • Delegation provenance, not cryptographic delegation. A2A_DELEGATION_RECORDED gives you a tamper-evident record of who delegated to whom. Cryptographic authority-to-delegate (signed, scope-attenuated delegation) is planned for a later release.

Where to go next