Skip to content

A2A bridge validation receipts

The local A2A bridge is a local-first HTTP+JSON edge for Synapse. Whether it is "production-grade" in the broad sense is not a single conformance pass/fail — it is a set of receipts that survive across the bridge boundary. This page turns the community A2A validation track into that set: the structured artifacts an independent tester records, so anyone can reason about what was actually exercised rather than trusting a claim.

This framing was contributed by Armorer Labs in the A2A validation discussion; it is adopted here as the project's validation template. Two rules run through it:

  • Separate protocol compatibility from operational safety. A bridge can be A2A-shaped and still be unsafe to expose behind a reverse proxy without explicit auth, logging, and egress boundaries. A receipt records both, never one as the other.
  • The interesting case is not the happy-path SDK call. It is restart + bounded replay + a real webhook receiver + a client that assumes durable event history. A crisp trace of that case is what lets people decide whether the bridge is an adapter, a local task runtime, or something that needs a stronger event log behind it.

The receipts

Run synapse a2a-conformance before recording a receipt. It prints the current local matrix against the A2A 1.0.0 operation model; receipt work should update the matching row instead of replacing the matrix with prose.

The repository test suite now includes local real-receiver coverage: push delivery posts to an HTTPS receiver trusted by a test CA and follows a real 307 proxy redirect without dropping the POST body. The delivery client also rejects a public-looking webhook URL when DNS resolves it back to a local receiver before send. A validation receipt is still needed for any public receiver behind production TLS or reverse-proxy infrastructure.

The A2A deployment threat model records the local exposed-bridge review for bearer auth, TLS/proxy placement, state-file permissions, webhook egress, retention, logging, and receipt evidence. A production receipt should still prove the chosen deployment follows that model.

Each receipt is a small, self-contained record. Capture it as JSON, a gist, or a discussion reply — the shape matters more than the medium.

1. Discovery receipt

  • Agent Card returned, the capability-manifest version it was projected from, the endpoint URL, the auth mode, and the client/SDK name and version.

2. Task-lifecycle receipt

  • The submitted payload, the assigned task id, every state transition observed, cancel and timeout behaviour, and the final observed result.

3. Webhook receipt

  • The destination class (public, private, local-network), the bridge's validation decision, how auth headers were handled, the delivery attempts, the retry/backoff observed, and the operator-visible failure mode.

4. Proxy / TLS receipt

  • The public origin, the forwarded headers, where auth was terminated, and whether the protected and public routes stayed separated across the boundary.

5. Replay / subscription receipt

  • Which event window is replayable, what is lost on restart, and how a client detects the gap. This is the receipt the durable-history-assuming client depends on.

6. Threat-model receipt

  • The SSRF / local-network checks exercised, token handling, log redaction, and what a compromised client can still ask the bridge to do.

Local independent HTTP client (in-tree)

Against a running bridge (or the test harness), the repository ships an independent stdlib client that does not import the request-handler path:

synapse a2a-serve --endpoint-url http://127.0.0.1:8877 --host 127.0.0.1 --port 8877 &
synapse a2a-interop-trace --host 127.0.0.1 --port 8877 --output /tmp/a2a-interop.json

The receipt schema is synapse.a2a_interop_trace.v1 (discovery + task lifecycle). It is the deterministic second client stack for the independent matrix row. Both this stack and synapse a2a-client cap peer responses at one MiB, apply the shared 64-level JSON nesting guard and a 4,096-member cumulative shape ceiling, and keep peer values out of failure messages. --output uses an owner-only temporary file plus fsync and atomic replacement. This protects local receipt custody; it does not establish certificate pinning, client-certificate identity, public-network validation, or certification.

Official SDK and TCK receipt (2026-07-10)

The official Python SDK a2a-sdk==1.1.0 discovered the live Agent Card, selected RestTransport, and completed send, get, list, and cancel. The official A2A TCK at commit 5996b79, pinned to specification commit 173695755607e884aa9acf8ce4feed90e32727a1, then ran the HTTP+JSON MUST profile:

Result Count
Passed 55
Failed 5
Skipped 175
Deselected 30

The run closed wire timestamp, explicit version negotiation, AIP-193 error shape, HTTP media-type, unknown-task, continuation, and direct A2A 1.0 inline push-config defects found by the initial diagnostic. The five remaining failures on that date were response-content scenarios: four require exact structured artifacts and one requires a direct Message response.

Update (2026-07-24): the shipped bridge now implements those residual profiles on production message:send / JSON-RPC dispatch. Official TCK messageId prefixes (tck-artifact-text-*, tck-artifact-file-*, tck-artifact-file-url-*, tck-artifact-data-*, tck-message-response-*) and explicit a2aScenario metadata/configuration select immediate completed-Task-with-Artifact or direct Message responses. Ordinary chat sends still return an asynchronous working Task. In-repo residual tests exercise the real handlers; a re-run of the external TCK suite is optional evidence and is not claimed as full certification.

The TCK loopback webhook is rejected by the production SSRF guard. Separate repository tests exercise authenticated HTTPS delivery, a real 307 redirect, and DNS-rebinding refusal under an explicit local-test policy. Native TLS a2a-serve plus dual a2a-interop-trace HTTPS runs are covered locally. Outbound HTTP(S) discovery/send/get is available via synapse a2a-client and synapse_channel.a2a_client.A2AOutboundClient against a second peer. Production public-webhook operator sign-off and full external TCK certification remain open.

This upgrades the matrix with real official-SDK and official-TCK evidence plus in-repo residual scenario handlers, but it is still partial validation, not A2A certification or full conformance.

How to contribute one

Run the bridge (synapse a2a-card, synapse a2a-serve — see the README's A2A section), exercise a dimension above against your own client/SDK and, where relevant, a real webhook receiver and a reverse-proxy/TLS boundary, and post the receipt to the validation discussion. The restart-plus-replay-plus-real-webhook case is the most valuable one to trace.

Validated dimensions land back in the deployment guide and the A2A section of the README; gaps a receipt exposes become tracked work. Synapse claims no external A2A conformance from screenshots — only from receipts that others can reproduce.