zahadun
Enables Claude Code and OpenCode agents to delegate tasks to peer machines over a self-hosted A2A mesh, using mTLS identity and executing via local CLIs or scripts.
README
zahadun
A self-hosted mesh of full-peer AI agents — Claude Code and OpenCode talking across your machines over A2A, with real mTLS identity and no API keys.
Every machine is an equal peer: it can ask (an MCP bridge inside Claude
Code / OpenCode) and answer (an A2A server executing tasks with the local
claude CLI, OpenCode, or plain scripts). There is no controller node, no
cloud relay, and no per-token billing — the Claude track runs on your existing
subscription via claude -p.
you, on machine A machine B
┌───────────────────────┐ ┌───────────────────────┐
│ Claude Code / OpenCode│ A2A JSON-RPC │ reverse proxy :8443 │
│ │ MCP bridge │ ──────────────────────▶│ mTLS, CN → identity │
│ ▼ │ over WireGuard mesh │ ▼ │
│ "@bob audit the SEO │ (NetBird/Tailscale) │ A2A server :9990 │
│ of example.com" │ │ ├─ script │
│ │◀── task id, then ──────│ ├─ claude -p │
│ zahadun_task(id) │ artifacts │ └─ opencode │
└───────────────────────┘ └───────────────────────┘
Why this exists
Multi-machine agent collaboration is a
frequently requested
capability. The pieces all exist — A2A↔MCP bridges, WireGuard meshes,
workload identity (SPIFFE), agent memory servers — but as separate,
mostly cloud- or Kubernetes-shaped projects. zahadun is the whole thing in
~1,500 lines of Python with two dependencies (httpx, mcp), sized for
one operator and a handful of machines.
What's different here
- Subscription, not API keys. The Claude executor is the CLI in
-pmode. If you pay for Claude Code, your mesh costs nothing extra. - One conversation, two machines. The A2A
contextIdis the Claude session UUID on every machine in a task's path.claude --resume <id>on either end shows that machine's half of the same conversation. - Deterministic
@peeraddressing. AUserPromptSubmithook parses mentions before the model sees the prompt. Routing is code, not an LLM decision: unknown peer → hard block with the roster; unreachable peer → the prompt never reaches the local model (you can't mistake a local answer for the peer's). - Conflict-free mesh memory, no database. Shared memory is a git-synced
directory of markdown files named
<peer>-<timestamp>.md. A peer only ever creates its own files, so replication can't conflict —git pull --rebasenever meets a merge. - Real identity, home-lab sized. mTLS everywhere; each machine's cert is
signed by your own root CA (an OpenBao PKI mount
works well — see docs/CA-RUNBOOK.md). CSR authenticity
is attested with an SSH signature (
ssh-keygen -Y) from the operator's personal key. Client-only machines getclientAuth-only certs: even a stolen key can't impersonate a server. - Honest cards. A peer's AgentCard lists only skills that work today.
A planned skill is a promise the caller can't distinguish from a working
one — they just get
FAILED.
Architecture in five decisions
- The router is a human. Pure A2A: addressing means choosing a peer.
No broadcasts, no capability matching. You say
@bob, code delivers to bob. - Tracks are pure end-to-end. A task submitted from Claude Code executes
in Claude on the target; OpenCode-to-OpenCode likewise. Mixing tracks would
orphan the session history that makes
--resumework. - The bridge has no model. The MCP server is an HTTP client plus a file
layer. All intelligence lives in the tool that loaded it or on the target
peer. It detects its own track from the MCP handshake's
clientInfo. - The risky part is code; the convenient part is the model. Delivery and addressing: hook, deterministic. Result pickup, catalogs, memory: MCP tools, model-driven.
- Peer input is untrusted. The Claude executor runs headless with no
tools by default (permission prompts auto-deny). You grant tools per
skill, explicitly, in
skills.json. Loops are cut by anX-Zahadun-Traceheader; caller identity comes from the client cert's CN via the proxy — never from the request body.
Quick start
See INSTALL.md. The short version, per machine:
# module
python3 -m venv /opt/zahadun-a2a/venv
/opt/zahadun-a2a/venv/bin/pip install zahadun-a2a # or from a checkout
# client side (every machine): MCP bridge + @peer hook
claude mcp add zahadun --scope user -- /opt/zahadun-a2a/venv/bin/python -m zahadun_a2a.mcp
# + hook in ~/.claude/settings.json, + block in opencode.json → INSTALL.md
# server side (machines that answer): systemd unit + reverse proxy with mTLS
# examples/ has units and nginx/Apache/Caddy configs
Everything runs as a regular user. No dedicated system account, no root
services — the Claude executor needs the user's ~/.claude anyway.
MCP tools exposed to your agent
| tool | purpose |
|---|---|
zahadun_peers() |
who is in the mesh, what they can do (live AgentCards) |
zahadun_ask(peer, task, …) |
delegate; returns a task id |
zahadun_task(id) |
poll result / status |
zahadun_models() |
live model catalog of the local OpenCode engine |
zahadun_memory_search/read/write/topics |
shared mesh memory |
Status
Working: the full client+server loop, three executors (script /
claude -p --session-id / OpenCode prompt_async with a model-fallback
ladder), task persistence across restarts, audit log, loop detection.
Not yet: SSE streaming (cards honestly say streaming: false),
input-required pauses (skills marked as needing human confirmation are
refused, not hung), per-caller rate limits.
Security model
Read SECURITY.md before exposing anything. Summary: designed for a single operator's machines on a private WireGuard mesh; peers are semi-trusted (authenticated, but their task content is not); it is not a multi-tenant system and was never designed as one.
A note on language
The project was built for a Polish-speaking mesh: code comments, error
messages and some config keys (drabina = model ladder, wykonawca =
executor, potwierdzenie_czlowieka = human confirmation) are Polish. The
docs you're reading, the wire protocol (A2A v1.0) and the MCP tool names are
English. Translating internals is on the table if anyone actually needs it —
open an issue.
License
Apache-2.0.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。