A2A External Gateway¶
The A2A gateway is an optional external interface that enables SynthOrg agents to
federate with agents in other A2A-compatible systems. It is disabled by default
(a2a.enabled: false). Internal communication is unchanged; the MessageBus remains the
sole transport for intra-organisation messages.
See also: Communication (transport), Coordination (delegation depth and cycle guard), Event Stream (SSE hub).
Architecture¶
The gateway sits at the organisation boundary and handles two directions:
- Inbound (external -> internal)
- External A2A clients discover SynthOrg agents via Agent Cards, then call one of the
gateway's five JSON-RPC methods:
message/send(create a task),tasks/get,tasks/cancel,skills/query(find learned peers advertising a skill), andskills/negotiate(confirm a named peer still serves a skill).tasks/get/tasks/cancelenforce per-peer ownership (a task is stamped with its originating peer; a cross-peer access 404s). The gateway translates A2A requests into internal MessageBus messages and applies A2A-specific security checks before admission;message/sendcreates a root task (see Delegation Depth and Cycle Guard for the ancestry check that applies once that task delegates onward). - Outbound (internal -> external)
- SynthOrg agents can delegate tasks to external A2A agents. The A2A client discovers
external agents via their Agent Card URLs (pinning the SSRF-validated IP to close the
DNS-rebind window), creates tasks, and maps external task states back to internal
states.
PeerDiscoveryClientfetches and registers remote Agent Cards (streamed and byte-bounded) into thePeerRegistrythat the inbound skill methods resolve against.
Agent Card Projection¶
When the gateway is enabled, SynthOrg serves an Agent Card at
/.well-known/agent-card.json per the A2A specification. The card is a safe projection
of AgentIdentity; only fields relevant to external capability discovery are exposed:
| AgentIdentity Field | Agent Card Field | Included | Rationale |
|---|---|---|---|
name |
name |
Yes | Public identity |
role |
description (partial) |
Yes | Capability context |
skills (SkillSet) |
skills (AgentSkill[]) |
Yes | Lossless mapping via Skill model |
department |
metadata | Optional | Organisational context |
authority (reports_to) |
- | No | Internal reporting-graph hierarchy |
model (ModelConfig) |
- | No | Internal infrastructure |
tools |
- | No | Security-sensitive capability list |
budget_limit |
- | No | Internal financial data |
The Skill model is A2A AgentSkill-aligned on the shared
capability fields (id, name, description, tags, input_modes,
output_modes). Those fields project losslessly in both directions. The
SynthOrg-only proficiency field has no A2A counterpart, so:
- SynthOrg -> A2A:
proficiencyis dropped from the projectedAgentSkill. - A2A -> SynthOrg: imported
AgentSkillobjects populateproficiencyfrom the internal default (1.0) since the wire format does not carry it.
Concept Mapping¶
SynthOrg and A2A use different terminology for overlapping concepts. This table provides a bidirectional reference for the gateway translation layer.
Task State Mapping¶
| SynthOrg State | A2A State | Direction | Notes |
|---|---|---|---|
CREATED |
submitted |
Bidirectional | Initial task creation |
ASSIGNED |
working |
SynthOrg -> A2A | Agent has accepted the task |
IN_PROGRESS |
working |
Bidirectional | Active execution |
IN_REVIEW |
working |
SynthOrg -> A2A | Internal review stage, opaque externally |
BLOCKED |
input-required |
Bidirectional | Waiting for external input |
AUTH_REQUIRED |
auth-required |
SynthOrg -> A2A | Mid-execution approval park; returns to ASSIGNED on approval, CANCELLED on timeout |
SUSPENDED |
input-required |
SynthOrg -> A2A | Checkpointed (graceful-shutdown) tasks; resume from checkpoint returns to ASSIGNED |
INTERRUPTED |
failed |
SynthOrg -> A2A | Interrupted execution; externally indistinguishable from failure |
COMPLETED |
completed |
Bidirectional | Successful completion |
FAILED |
failed |
Bidirectional | Unrecoverable failure |
CANCELLED |
canceled |
Bidirectional | Client-initiated cancellation |
REJECTED |
rejected |
Bidirectional | Task refused by agent or guard |
Gate verdict mapping (not a task state):
| SynthOrg Verdict | A2A State | Direction | Notes |
|---|---|---|---|
Verdict ESCALATED |
auth-required |
SynthOrg -> A2A | The ESCALATED verdict is what parks the task in the AUTH_REQUIRED state above; both surface to A2A as auth-required (one is the task state, this row is the verdict that produces it). External client must provide additional credentials |
Identity Mapping¶
| SynthOrg | A2A | Direction | Notes |
|---|---|---|---|
AgentIdentity |
AgentCard |
SynthOrg -> A2A | One-way projection (safe subset) |
Skill |
AgentSkill |
Bidirectional | Lossless on shared capability fields; internal-only proficiency is dropped outbound and defaulted to 1.0 on inbound |
SkillSet.primary |
AgentCard.skills (tagged primary) |
SynthOrg -> A2A | Primary/secondary distinction preserved via tags |
SkillSet.secondary |
AgentCard.skills (tagged secondary) |
SynthOrg -> A2A | Primary/secondary distinction preserved via tags |
Message and Task Lifecycle Mapping¶
| SynthOrg | A2A | Notes |
|---|---|---|
Message (internal) |
Message + Part[] (A2A) |
Internal message content maps to A2A text parts |
DelegationRequest |
message/send (JSON-RPC) |
Task creation |
DelegationResult |
tasks/get response |
Task completion/status |
TaskExecution state |
Task object + status |
Ongoing task tracking |
| MessageBus channels | - | No A2A equivalent; internal routing only |
| Group conversations | - | No A2A equivalent; internal coordination only |
SSE Streaming¶
External task update delivery uses Server-Sent Events per the A2A specification. The dashboard also uses SSE for observability and the HITL interrupt/resume protocol (see Event Stream and HITL Surface).
| Consumer | Transport | Protocol | Use Case |
|---|---|---|---|
| Web dashboard | SSE | AG-UI projected events | Observability + HITL interrupt/resume |
| Web dashboard | WebSocket | Custom events | Bidirectional UI actions (chat, settings) |
| External A2A client | SSE | message/stream |
Task progress streaming |
The EventStreamHub is the single event source for all SSE consumers (hub-driven
architecture). Both the AG-UI dashboard and the A2A gateway subscribe
to the hub and apply per-consumer projection layers. The gateway applies an A2A
projection that filters to task-related events for explicitly subscribed tasks and
formats payloads per the A2A specification; no internal channel traffic leaks to
external consumers.
A2A Client (Outbound)¶
SynthOrg agents can delegate tasks to external A2A agents through the outbound client:
- Discovery: Fetch the external agent's Agent Card from its well-known URL
- Skill import: Deserialise
AgentSkill[]into internalSkillmodel (lossless) - Peer skill discovery (optional):
A2AClient.query_skillsissuesskills/queryto find federation peers a node knows that advertise a skill, thennegotiate_skillsissuesskills/negotiateto confirm a chosen candidate still serves it before routing. The negotiated routing URL must be SSRF-validated by the caller before use. - Task creation: Send
message/sendJSON-RPC request with auth credentials - Monitoring: Subscribe to task updates via SSE or poll via
tasks/get - State mapping: Map external A2A task states back to internal states (see table above)
The outbound client authenticates using the a2a.auth.outbound configuration (see
A2A Security). A2AClient.send_message has no delegation
depth or cycle check of its own (the
delegation depth and cycle guard
applies to sub-agent delegation via delegate_and_await, a separate code path); no
agent tool calls A2AClient.send_message, so outbound dispatch is unwired.