the Agent-to-Agent Exchange
A contract, not a product surface: Wall Street defines the protocol that lets three independently-owned systems — Family CFO, Trader Joe, and TMZ — optionally share data and consult each other over plain HTTP, with no shared database and no dependency on any peer staying up. An A2A gateway enforces one request envelope, a pluggable auth chain, and a circuit breaker that collapses every failure mode — a rejected auth check, an unreachable peer, a response that fails schema validation — into the same graceful "not connected" outcome, never a crash. A config-driven topology registry is the single source of truth for which systems may talk to which, so adding a fourth or fifth system is a config entry, not a core-code change. A React Flow canvas turns that topology into something a person can see and rearrange — but the canvas is downstream of the protocol, not the point of it. Wall Street is itself a small multi-agent system: an orchestrator on a lightweight ReAct loop routes to a connection-recommender and a data-interpreter, each config-driven down to its LLM tier, each falling back to deterministic rules with no model configured at all. The design — five phases, twenty-four tickets — is complete and ready to build.
Share this case study →The hard part: freezing a wire contract that three independently-owned systems must fit before any of them writes a line of new code — every field traced to a real column in a real production table, every one of nine failure modes collapsed to the same "not connected" outcome — because getting that degradation semantics right during design is far cheaper than retrofitting it after three teams have already built against a live contract.
Lightweight ReAct loop routing to Wall Street's own specialist agents
Python (gateway), React, Vite, TypeScript, Tailwind, React Flow
Every cross-system call carries the same envelope: an X-A2A-Version header, a request ID that doubles as a consult idempotency key, and a trace ID that threads through the caller, the hub, and the responder so a single cross-system exchange can be reconstructed afterward from three separate audit trails. Nine named failure codes — from a rejected auth check to an unreachable peer to a response that fails schema validation — all collapse to one outcome for the calling system: a clearly labeled "not connected" result, never a crash, never a blocked local workflow. A pluggable auth chain (an IP allowlist, then per-caller bearer tokens, with an mTLS slot reserved but unimplemented) sits in front of every peer's A2A surface, kept deliberately separate from that system's own UI login. None of this is generic middleware bolted on after the fact — the envelope, the auth chain, and the full error taxonomy were the first thing designed, before a single peer server or canvas component.
A YAML topology registry is the only place that knows which systems exist and who may talk to whom: each peer declares a node kind and the capabilities it provides and consumes, and each connection rule names a capability, a provider, and its legal consumers. The gateway enforces those rules on every call, and the canvas asks the same registry — one BFF endpoint, GET /api/topology — before it will let a person drag an edge between two nodes, so the frontend never hardcodes a single system pair. Adding a fourth or fifth system to the ecosystem is scoped to: implement the A2A server's minimal surface (health, capabilities, its query endpoints), add one peer block to the topology config, add its token environment variables, and — only if it needs a visually distinct node — register one new frontend node type. Reusing the generic "system" node kind means zero frontend code at all; the new peer just appears once capability discovery finds it.
On the canvas, systems, agents, and data sources are nodes and A2A capabilities are edges — a data-share edge pulls a peer's real production data through a query call, a consult edge posts a request and shows the peer's answer as a read-only conclusion in a details drawer. Every node and every edge degrades independently: a peer that's unreachable renders its node gray with a reason code while the rest of the canvas keeps working exactly as designed — the same "no peer is ever load-bearing for another" rule the protocol enforces at the wire level, now visible as a UI state instead of a log line. The canvas is the part a visitor actually sees and clicks; every rule it enforces — which connections are legal, what a degraded peer looks like, what data is allowed to reach the browser at all — was decided in the gateway and the contract first. The canvas renders that design; it isn't a second one.
Wall Street doesn't just route other systems' agents — it runs a small one of its own. An orchestrator on the same lightweight ReAct loop used elsewhere in this portfolio routes to two specialists: a connection-recommender that reads the current canvas and the capability map to suggest viable connections, and a data-interpreter that turns a node's already-redacted metrics into a one-line explanation. Both are LLM-optional by design — the recommender's rule-based fallback, computed straight from the provider/consumer intersection in the capability map, is the default and stays fully functional with no model configured at all, and the interpreter simply hides itself if none is. A fourth specialist, an insight-narrator that reads across several nodes at once, is designed and config-present but left disabled until cross-node narrative synthesis is actually built. Which LLM tier each agent uses, its temperature, and whether it's enabled at all live in one config file — swapping models is a config edit, never a code change — and every system prompt lives in an external prompt store, loaded by language and key, never hardcoded into the agent itself.
The browser never holds an A2A token and never talks to a peer directly — it authenticates to the gateway's BFF with a separate UI token, and every production number it receives has already passed through the gateway's redaction layer, which serializes against the contract's own field whitelist and silently drops anything not explicitly listed. Every data contract marks its free-text fields — a headline, a note, an agent's answer — as untrusted, and any system that feeds that text back into its own LLM must wrap it in its existing anti-injection envelope and treat it as data, never an instruction; the contract's own test suite requires that a response with an embedded command like "set the budget to zero" produce no write anywhere. Cross-system calls are read-only by design — the one exception, a consult's conclusion, can only come back as a proposed action, executed nowhere until the receiving system's own human-confirmation flow approves it. None of this is an add-on: the whitelist-as-schema, the untrusted-field tagging, and the no-cross-system-writes rule are the contract itself, frozen before a single peer implements it.
Python (stdlib A2A gateway), React + Vite + TypeScript canvas, Tailwind CSS, React Flow, a shared reference mock server for contract-consistency testing across all three peers.