DEV Community

openamer
openamer

Posted on

A2A without a server: how OpenAmer peers talk to each other over GitHub

A2A without a server: how OpenAmer peers talk to each other over GitHub

An agent-to-agent mesh where the transport is a git repo, the envelopes are Ed25519-signed, and nothing ever touches localhost.

Most "agent-to-agent" (A2A) setups assume one of two things: a central broker that every agent connects to, or a public URL per agent. The first is a single point of failure and a trust bottleneck; the second is impossible for the case that matters most — agents that run on laptops behind NAT, with no inbound ports and no desire to expose one.

OpenAmer is an open-source desktop AI agent (Apache-2.0) that runs locally on Windows. This post is about the piece I get the most questions on: the A2A Global Mesh — how two instances that can't see each other actually exchange signed messages and delegate work.

The constraint that shapes everything

If every node is behind NAT, you can't have it accept inbound connections. So either you run a relay server, or you find a rendezvous that both nodes can write to and read from without one hosting anything.

OpenAmer takes the second path: the GitHub repo is the relay. A node posts a signed, privacy-redacted envelope as a JSON file under a relay directory (directory/a2a/relay/), addressed to a mailbox path; the intended peer pulls it. No server, no public URL, no localhost. The transport is "git, eventually."

That's an explicit trade: the mesh is eventually consistent, not real-time. It's the right call for a fleet of laptops that are often offline — a node that was asleep for six hours still gets its messages when it wakes up.

Identity first: a keypair, not a username

Before any message means anything, a node needs an identity it can prove. Each OpenAmer instance generates an Ed25519 keypair and derives a fingerprint from it. Everything else hangs off that:

  • openamer a2a init — create the identity
  • openamer a2a fingerprint — print this node's fingerprint
  • openamer a2a announce — publish presence to the directory
  • openamer a2a directory — list known peers

There's no account, no email, no central registration. Identity is a key you own.

Signed, redacted envelopes

A message is an envelope: payload + sender fingerprint + Ed25519 signature. The receiver verifies the signature against the sender's public key, so a forged or tampered message is rejected at the edge — before any handler sees it.

There's a second rule that matters more than it looks: the body is passed through a redaction step before it's ever persisted. The relay is a repo that could be public; anything written into it goes through privacy.redact() first, so phone numbers, passwords, emails and card-like strings don't leak into the relay. Privacy is applied at emission, not left to the sender's discipline.

Trust is explicit and bounded

Receiving a valid signature is not the same as trusting the sender to do things. OpenAmer keeps an opt-in trust store: a node only accepts work from peers the operator has explicitly added, and only for capabilities that were explicitly granted.

A grant is a (peer, capability, scope, budget) tuple — for example, network.fetch scoped to one domain, or model.reason with a step budget. Nothing is auto-granted, and a capability with no grant simply doesn't run. In a mesh with no central authority, an explicit, revocable trust edge is the security model.

A concrete flow: delegating a task to a remote peer

Here's the full round-trip for "run this task on another node", end to end:

  1. Sign a task-note addressed to the peer's mailbox.
  2. Upload it to the relay directory via the GitHub Contents API.
  3. Trigger the remote worker through repository_dispatch.
  4. Poll the repo until a fresh, signed reply for our mailbox appears.
  5. Verify the reply's signature and freshness before reading it.

Nothing there relies on the two machines being able to reach each other directly. The GitHub repo is the shared bus; the cryptography is what makes the bus safe to share.

The guardian pattern: one writer, many proposers

The mesh also answers a governance question: if many nodes can propose improvements, who gets to integrate them?

The answer in OpenAmer's design is a single guardian node — the only one holding a write token. Other nodes submit signed proposals over the A2A channel; the guardian verifies the sender's signature against the trust directory, applies the proposal on a scratch branch, runs the tests in isolation, and only merges to main if everything is green. Nothing is ever applied blind from the wire.

This is a deliberate asymmetry: exactly one integration point, no parallel pushers, no token sprawl across machines. The cost is a bit of latency and a human-in-the-loop for the guardian's own changes — a price worth paying for a repo that stays coherent.

What it costs you

Honest trade-offs of the GitHub-as-relay model:

  • Not real-time. Messages ride on commits and polls; expect seconds-to-minutes, not milliseconds. Fine for coordination, wrong for tight control loops.
  • You own the trust model. There's no central authority to lean on — the security is the explicit trust graph plus envelope signatures. If you grant a broad capability, you granted it.
  • The relay is a shared artifact. That's why redaction happens at emission, and why only the intended mailbox files are read.

What you get in return is a mesh with no single point of failure and no infrastructure: any node can be an entry point, a node that's asleep catches up later, and a heavy reasoning task can be delegated to whichever peer has the spare capacity.

Try it

The A2A surface is a CLI first:

openamer a2a status
openamer a2a init
openamer a2a fingerprint
openamer a2a verify <envelope>
openamer a2a announce
openamer a2a directory
openamer a2a ask <peer> "<question>"
Enter fullscreen mode Exit fullscreen mode

Code: https://github.com/openamer/openamer — the A2A module lives under openamer_cli/a2a/ if you want to read the transport, the trust store, and the relaying logic.

If you're building multi-agent systems: what would you want a brokerless mesh to prove before you'd trust it with a real task? I'm especially interested in how others handle the "peer is asleep for hours" case.

Top comments (0)