a2aproject / a2aproject/A2A

[Proposal]: A metadata-private custom binding for A2A (communication-graph protection)

Open
#1,964 5 comments 0 reactions 0 assignees View on GitHub
Dominant language
Shell
Stars
25.7k
Forks
2.6k
Avg merge
3d 6h
Merged PRs (30d)
16

Description

## Motivation

Currently, A2A standard bindings encrypt payloads but leave the communication graph, who talks to whom and when, exposed to network observers and relays. That graph is its own security axis, separate from the content and transport encryption that a binding already provides, and it is revealing: a metadata-only classifier recovers a workflow's task class from real A2A sample-agent traffic. In agent systems the graph is especially legible, since endpoints are labeled by capability and workflows run as fixed chains, so an observer watching only the graph can recognize a deployment's recurring workflows and, in some settings, act on the next one before it completes.

**Links:**
* [arXiv preprint](https://arxiv.org/abs/2606.07150)
* [binding spec](https://github.com/dangoldbj/agent-metadata-privacy/blob/main/bindings/metadata-private/v1.md)
* [reference implementation](https://github.com/dangoldbj/agent-metadata-privacy)
* [dataset (Hugging Face)](https://huggingface.co/datasets/dangoldbj/a2a-metatrace)

## Proposal

This proposal introduces a metadata-private custom binding for A2A, to be developed as an `experimental-cpb-metadata-private` repository through the existing experimental-binding process. The binding exposes exactly the same A2A surface agents use today. Every operation, data type, and error maps unchanged, so it changes only the carrier, not what agents say. With cover traffic it closes the metadata channel and leaves a network observer at chance.

The binding needs no new mechanism: it declares itself through its `supportedInterfaces` URI like any other binding, so a deployment can offer graph protection as a visible, selectable property. It is opt-in and sits alongside the standard bindings (`HTTP+JSON`, `JSON-RPC`, `gRPC`) rather than replacing any of them, so agents that do not offer it are unaffected.

The binding is one instance of a transport-agnostic way to reason about this axis, developed in full in the paper: the asset (the graph), the adversaries (network observers, relays, registries), and the properties that defend it (unlinkable identifiers, no single observer, metadata minimization, private discovery).

## Authorization without identity

One piece does not carry over cleanly. A2A's identity-based schemes (`OAuth2`, `OIDC`, `mTLS`) pin a stable principal, which would re-link a client across interactions and undo the protection. The binding carries authorization as anonymous, per-interaction tokens instead, so a peer proves what it may do without presenting a persistent identity. Each token is single-use, presented per interaction rather than per connection, and carries a scope rather than a stable subject, so two calls from the same client cannot be linked through it. It rides on top of the agent's own authorization checks rather than replacing them, so the agent's security model is unchanged. The issuance and revocation model is part of the experimental work.

## Measurements

- On a corpus of real traffic from the official A2A sample agents (released at https://github.com/dangoldbj/a2a-metatrace), a classifier using only metadata, with no payloads, recovers a workflow's task class at roughly 6x chance.
- The exposure is prospective and actionable. In a testbed, a metadata-only observer reads a workflow's opening and gains enough predictive leverage to act before the task completes. With metadata-minimization shaping enabled, that leverage collapses to the blind baseline, so the same property that closes the channel also removes the advantage.
- Vantage is concentrated. A few providers front most deployed endpoints, with about three-quarters of them sitting behind the top three, so a broad observer is closer to the common case than to a worst case.

Full threat model, proofs, and scope are in the paper linked above.

## Reference binding

I built a reference binding from standards-track parts: an `Oblivious HTTP (RFC 9458)` relay and gateway over `HPKE` for unary calls, a metadata-minimization shaping profile, and constant-rate polling for streamed updates, over `TLS` or `MLS`. That unary and polling core is the working code. `Chunked OHTTP` streaming and Privacy-Pass-style authorization are specified but not yet built, and they sit in the same normative spec, which covers the full operation and error mapping plus the conformance levels. Both the code and the spec are linked above.

It declares in the Agent Card like any other binding, with no new fields:

```json
{
"supportedInterfaces": [{
"url": "https://relay.example/ohttp",
"protocolBinding": "https://github.com/dangoldbj/agent-metadata-privacy/bindings/metadata-private/v1",
"protocolVersion": "1.0"
}]
}
```

The `url` points at the relay, not the agent, so the agent's address travels encapsulated to the gateway and stays hidden from the relay and from a network observer. The gateway sits in the agent operator's trust domain and the relay is an independent party. The metadata protection holds under their non-collusion, the standard `OHTTP` deployment assumption. The binding spec defines how a client obtains the gateway and its `OHTTP` key configuration: key configs rotate, so a client fetches them from the gateway per RFC 9458 rather than reading them from a cached card. Operations map unchanged:

- Unary calls (`SendMessage`, `GetTask`, `ListTasks`, `CancelTask`, push-config, `GetExtendedAgentCard`) run as `OHTTP` requests.
- Streaming (`SendStreamingMessage`, `SubscribeToTask`) is specified over `chunked OHTTP`, with a constant-rate polling fallback that the current implementation uses.
- Server push moves from a webhook to a client-pull queue, or an optional client-established tunnel for low-latency delivery, since an unlinkable client has no address. The polling fallback covers it today.
- Discovery runs over the binding too, so resolving an agent's card does not reveal which agent to a registry or observer.

The shaping profile completes the metadata protection that `OHTTP` begins. It pads every message to constant-size cells on a fixed cadence and gives each interaction fresh identifiers, and it comes in two profiles that trade bandwidth for protection. The lighter `interactive` profile closes the size channel but still exposes message count, which tracks how long a workflow runs. The heavier `cover` profile adds constant-rate cover traffic to close the count channel as well, at roughly an order of magnitude more bytes and latency.

The measurements show that bare `OHTTP` leaves a workflow recoverable at about 4x chance, since hiding the agent's address does nothing about message counts or timing. Only the cover profile drives a network observer all the way to chance. No single defense closes the channel on its own, so metadata minimization has to live in the binding and apply as a set.

## Next steps

The spec, the reference implementation, the dataset, and the measurements above are already in place for an `experimental-cpb-metadata-private` repository. Streaming over `chunked OHTTP` and anonymous-credential authorization are specified but not yet built, and I'd be happy to complete them as part of the experimental process.

## Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.