Swarmly
Not the Agent2Agent protocol. Swarmly is an independent project. It does not implement, and is not affiliated with, the Agent2Agent (A2A) Protocol that started at Google and is now hosted by the Linux Foundation. Its message formats are its own; their identifiers (
a2a/v1,a2a/2) and theA2A_*variables only abbreviate "agent-to-agent". It coordinates coding-agent command-line tools through a git repository, a synced folder and Telegram, rather than through A2A's HTTP/JSON-RPC interface and Agent Cards. To interoperate with A2A-compliant agents, use the official specification and SDKs at a2a-protocol.org.
Coordinate coding-agent CLIs from different vendors, on different machines, on one project, with a human in charge.
Swarmly connects agent runtimes such as the Antigravity CLI, OpenAI Codex CLI, Claude Code and Nous Research Hermes Agent through two planes that need no public IP and no open ports:
- The durable plane holds the record. Signed message envelopes are stored in one of three
places: a git repository (gitbus,
.agents/inbox/), a synced folder (Google Drive for desktop, Dropbox, OneDrive, Syncthing, a network share), or Google Drive through its API. The record survives restarts, works offline and catches up on its own. - Telegram is the instant plane. Every agent has a bot in one group, and the human can watch and override everything there. Messages are short and point at the durable record.
An optional TCP gateway (the "fast plane") is included for agents on the same network.
Swarm kernel (a2a/2, preview): signed per-agent lanes off
main, one validated write path (swarmlyCLI and MCP server) and a safety guard. See docs/SWARM.md.
Topic-to-project routing (multi-project): Telegram forum topics bound to project repositories, with GitHub issues and pull requests as the default durable plane, git worktree isolation, and a missed-ping notification backstop. See docs/PROJECTS.md.
Roles: give each agent a role (coordinator, developer, reviewer, QA tester, researcher, or your own) and its turns follow that role's workflow. See docs/ROLES.md.
New here? Getting started brings a node up in about fifteen minutes. Then see the envelope cheat-sheet, adopting Swarmly in your project and the runnable demo.
Supported agents
| Runtime | adapter.type |
How it is driven | Setup |
|---|---|---|---|
| Antigravity CLI | antigravity, antigravity_thread |
agy -p, or queued into an open Antigravity conversation |
SETUP-ANTIGRAVITY |
| OpenAI Codex CLI | codex, codex_thread |
codex exec, or queued into an open Codex task |
SETUP-CODEX |
| Claude Code | claude, claude_thread |
claude -p, or one persistent session per topic (recommended) |
SETUP-CLAUDE |
| Hermes Agent | hermes |
hermes -z |
SETUP-HERMES |
| Reasonix (DeepSeek) | reasonix |
reasonix run -p |
SETUP-REASONIX |
| Anything with a CLI | custom |
any command line with a {prompt} placeholder |
telegram-channel.md §4 |
Coordination planes
| Plane | Transport | Role | Status |
|---|---|---|---|
| Durable | GitHub issues & PRs (github: backstop) |
default durable coordination plane across projects | stable |
| Durable | gitbus (.agents/inbox/ of the repository named in bus.repo) |
the record: tasks, handoffs, reviews, blockers | stable |
| Durable | synced folder (bus.transport: folder) |
the same record without git | new |
| Durable | Google Drive API (bus.transport: gdrive) |
the same record for headless nodes | new |
| Instant | Telegram group, one bot per agent | low-latency pings, ACKs, human override | stable |
| Fast | TCP gateway with challenge-response auth (swarmly/server.py) |
same-network broadcast, archived to gitbus | experimental |
Each project picks a durable coordination plane — GitHub issues and PRs (see docs/PROJECTS.md) or an envelope bus transport. Telegram runs beside it:
swarmly-telegram post writes the envelope durably and then pings the peer on Telegram with a
pointer to it. docs/TRANSPORTS.md covers the setup for each transport
and how to choose between them. The channel rules (delivery semantics, the escalation ladder)
are in docs/telegram/PROTOCOL.md; the bus poller's gates and
dedupe keys are in docs/telegram-channel.md §6.
Install
pipx install swarmly # or: uv tool install swarmly, or pip install swarmly in a venv
swarmly-telegram --help
Python 3.10 or newer and git are the only prerequisites. One install gives you the three
commands: swarmly-telegram (the Telegram transport and bus poller), swarmly-keygen (signing
keys) and swarmly (the swarm kernel). Google Drive as a bus needs pipx install "swarmly[gdrive]". To work on Swarmly itself, install from a clone instead:
pip install -e ".[test]".
Quickstart
1. Try the bus with no tokens at all. From a clone, two agents exchange a signed message through a throwaway git repository, or through a plain shared folder:
git clone https://github.com/eeftychiou/swarmly.git && cd swarmly
examples/minimal-bus-demo.sh # gitbus
examples/folder-bus-demo.sh # folder bus (what Drive or Dropbox would sync)
2. Set up an agent. Each agent gets its own Telegram bot (from @BotFather) and its own
config.yaml. Run this in the project repository your agents will share; its
.agents/inbox/ becomes the durable bus:
cd ~/projects/your-project
swarmly-telegram init --alias Hermes --adapter hermes --bus-repo .
echo 'A2A_TELEGRAM_BOT_TOKEN_HERMES=123456:ABC...' >> ~/.a2a-telegram.env
swarmly-keygen --agent hermes --env-file ~/.a2a-telegram.env --registry .agents/bus/config/a2a_config.json
git -C .agents/bus add .gitignore config/a2a_config.json
git -C .agents/bus commit -m "Register hermes" && git -C .agents/bus push -u origin agents-bus
swarmly-telegram whoami # proves the bot token
swarmly-telegram discover # lists the chats the bot sees: your group's id is home_channel_id
# edit config.yaml: agent.name (the bot's username), home_channel_id, admin_user_id, peers
init writes config.yaml, an empty signing-key registry and the .gitignore lines that keep
the config and the transport's state out of git. The agent commits its bus messages in this
checkout, so the host needs a git identity (git config user.name and user.email). --adapter is hermes, codex, claude,
antigravity (or a *_thread variant), echo to test the channel without an agent CLI, or
custom. swarmly-keygen writes the private key to the env file (owner-only) and only the
public key to the registry; every peer verifies against that committed registry.
3. Check it, then run it.
swarmly-telegram check # validates the config and says what is still missing
swarmly-telegram run # serve the Telegram channel and poll the bus
swarmly-telegram post --to Codex "PR #7 is ready" # durable write + Telegram ping
Every command reads ./config.yaml unless you pass --config path/to/config.yaml. Getting started walks through the Telegram side (the group,
bot-to-bot mode, admin rights) and running the transport as a service.
A minimal agent config:
agent:
name: HermesBot
alias: Hermes
bot_token_env: A2A_TELEGRAM_BOT_TOKEN_HERMES
home_channel_id: -1001234567890
admin_user_id: 1234567890 # your numeric Telegram user id (from @userinfobot)
adapter:
type: hermes
cwd: /home/user/your-project
# Optional: route Telegram forum topics to separate project repos & checkouts
# projects:
# webapp:
# repo: example-org/webapp
# cwd: /home/user/webapp
# topics: [12, 15]
# worktrees: true
# github:
# login: hermes-bot
# poll_minutes: 5
Telegram needs Bot-to-Bot Communication Mode enabled for each bot in @BotFather, and each bot
promoted to group admin. docs/telegram/SETUP-ADMIN.md walks
through it.
Messages
Every message is an envelope that conforms to
schemas/a2a_envelope.schema.json, stored as YAML
front matter followed by a readable body:
---
protocol: a2a/v1.0
message_id: msg-20260913T124500Z-codex
correlation_id: pr-47
timestamp_utc: '2026-09-13T12:45:00.123456+00:00'
sender:
agent_id: codex
model_family: openai
framework: codex
recipient:
agent_id: antigravity
intent: PR_REVIEW_VERDICT
priority: HIGH
signature: 'hmac-sha256:7f8a9b2c3d4e5f...'
payload_json: >-
{"pr_number": 47, "verdict": "CHANGES_REQUESTED"}
---
# Review: PR #47
Two findings, both in the retry path.
Intents include TASK_PROPOSE, TASK_CLAIM, TASK_HANDOFF, PR_REVIEW_REQUEST,
PR_REVIEW_VERDICT, STATUS_UPDATE, QUERY_REQUEST and ESCALATE_BLOCKER. The full list is in
swarmly/models.py and docs/ENVELOPES.md.
Send and read from the command line:
# codex's key, from `swarmly-keygen --agent codex --env-file .env --registry config/a2a_config.json`
set -a; . ./.env; set +a # sending refuses without A2A_KEY_CODEX
python scripts/a2a_bus.py send --sender codex --recipient antigravity \
--intent PR_REVIEW_REQUEST --priority HIGH --payload '{"pr_number": 47}'
python scripts/a2a_bus.py inbox # lists messages and verifies signatures
Security model
Read this before you run an agent. Every agent this project drives can run code on its host, and messages from the bus and from the Telegram group become that agent's prompts. Treat anyone who can write to your bus or post in your group as someone who can run commands as the agent's OS user. These limits remain:
- The default permission level is full: no checks and no sandbox. Every runtime starts
at
adapter.permissions: full, so an agent can pull, commit, push and runghout of the box. Keep the group and the bus repository private (the warning above).standardstill lets the agent run interpreters, package managers and git, which amounts to arbitrary code execution;strictonly edits files. The default Antigravity command passes--dangerously-skip-permissions(an explicitadapter.commandwithout it keeps approvals). - Agents run as the transport's OS user. Runtimes do not inherit variables whose names look
like secrets (tokens,
A2A_KEY_*, passwords), but that is a denylist: the CLI's own API key passes, and so does a secret with an unusual name. As the same user an agent can also read the transport's.envfiles and, on Linux,/proc/<pid>/environ. The real boundary is the OS: run each agent as a separate low-privilege user, or in a container or VM, with only that agent's secrets. - With HMAC, every receiver can sign as every sender. Each node holds each HMAC sender's secret to verify it. Prefer Ed25519 for bus senders: the registry then holds only public keys.
- Write access to the bus repository is the trust anchor. Each node trusts the keys and
revocations in its bus checkout's
config/a2a_config.json, so anyone who can push there can add a trusted sender, replace a key or drop a revocation. Restrict who can write to the bus repository and protect that file (branch protection, CODEOWNERS or required review). - Revocation needs every node's copy of the registry.
revocation_listis read fromconfig/a2a_config.jsonin each node's bus checkout, so a revocation takes effect on a node only once that checkout carries it. - The fast-plane gateway (
swarmly/server.py) is experimental: raw TCP with newline-delimited JSON, plaintext, and the client does not authenticate the server. It binds127.0.0.1by default; reach it from another machine only through an SSH tunnel or a TLS proxy. - The swarm safety guard is advisory for every CLI except Claude Code, which has an enforcing hook.
What does hold today:
- No bus by default. Without
bus.reponothing is polled, and the first cycle on a bus records what is already there without delivering any of it. - Bus messages are verified on delivery. A message is delivered only when its signature
verifies against a registered, non-revoked sender. The signature covers the whole envelope
(payload, sender, recipient, message id, intent, timestamps), so a copied message cannot be
re-addressed or given a new id, and it is accepted only within 7 days of its signed
timestamp. Old payload-only signatures are refused unless the registry sets
"accept_v1_signatures": truefor a rollout;bus.allow_unsigned: trueturns the checks off for a local demo bus. See docs/ENVELOPES.md. - No keys ship, and signing fails closed. The registry (
config/a2a_config.json) starts with no agents.swarmly-keygen --agent <id> --env-file <file> --registry <registry>creates an Ed25519 key per agent: the private key stays in that agent's env file, and only the public key is registered, withkey_type: ed25519, so onlyed25519:signatures verify against it. Sending refuses withoutA2A_KEY_<SENDER>. (An HMAC agent is registered by the name of an environment variable, and has no key while that variable is unset.) - Telegram authorization. People are authorised by numeric user id only (
admin_user_id,allowed_users); strangers are ignored by default; forwards and anonymous-admin posts are not treated as bots. - No shell. Agent CLIs are started from argument lists, never through a shell. On Windows an
npm
.cmdshim is replaced by the script it wraps and any other.cmd/.batis refused, so prompt text never reachescmd.exe. Timeouts kill the whole process tree. - The gateway checks what it archives. Message ids and header fields are validated, so a client cannot write outside the inbox; idle connections are closed and their number capped.
SECURITY.md lists the same limits and says how to report a vulnerability.
Python SDK
import asyncio
from swarmly import A2AClient, MessageIntent, MessagePriority
async def main():
client = A2AClient(agent_id="codex", private_key="...", host="127.0.0.1", port=8765)
await client.connect() # answers the challenge automatically
client.on_message(lambda msg: print(msg.sender.agent_id, msg.payload))
await client.send_message(
intent=MessageIntent.PR_REVIEW_VERDICT,
payload={"pr_number": 47, "verdict": "APPROVED"},
recipient_id="antigravity",
priority=MessagePriority.HIGH,
)
asyncio.run(main())
Documentation
| Topic | Where |
|---|---|
| First node in fifteen minutes | docs/GETTING-STARTED.md |
| Durable transports (git, folder, Google Drive) and Telegram | docs/TRANSPORTS.md |
| Using Swarmly in another project | docs/ADOPT.md |
| Telegram channel, adapters, rules | docs/telegram-channel.md |
| Channel protocol v1.1 | docs/telegram/PROTOCOL.md |
| Safe upgrade and rollback | docs/telegram/UPGRADE.md |
| Every document, by audience | docs/README.md |
| How this repository is maintained | AGENTS.md |
Status and roadmap
This repository is developed by the agents it connects. Claude Code, Antigravity, Codex and Hermes coordinate here over their own gitbus and Telegram, under a human maintainer.
Planned:
- more cloud transports beside git, folders and Google Drive (S3-compatible storage, OneDrive's API);
- carrying the GitHub event watcher used in production gitbus deployments back into this library.
Running the tests
pip install -e ".[test]"
python -m unittest discover tests
How this was built
I designed Swarmly and wrote its protocol and the process rules in
AGENTS.md: every pull request gets an independent review with a merge or fix-first
verdict before whoever acts as maintainer, usually an agent, merges it; I make the calls the
agents escalate to me.
Most of the implementation was written by the coding agents it connects. This public repository
starts from a single commit; the development history stays in a private repository, because it
carries the identifiers of the deployment it was built on. Of its roughly 320 non-merge commits
at the time of writing, about 280 were made by Claude Code, Hermes, OpenAI Codex CLI and
Antigravity agents under their own git identities (for example Codex <codex@a2a.local>). They
claim tasks, review each other's pull requests and report status over this project's own bus and
Telegram channel. In the first week, before per-agent identities were introduced, some agent
work was committed under my name.
— Eftychios Eftychiou
Acknowledgements
- ductor (MIT) by PleasePrompto: the Telegram
transport's forum topics, typing indicator, busy queue,
/stop, incoming files and long-reply splitting were reimplemented from its Telegram transport. See THIRD_PARTY_NOTICES.md. - superpowers (MIT) by Jesse Vincent: the format of the
repository's agent skill (
.agents/skills/vacuous-green/) and its "verify red" step follow superpowers' skills. - aiogram (MIT), the Telegram Bot API framework the transport is built on.
Contributing and license
Contributions are welcome. See CONTRIBUTING.md. Released under the MIT License.
Metadata
Release files for swarmly 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| swarmly-0.9.0.tar.gz | 347.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| swarmly-0.9.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 727.2 kB
Release files / swarmly-0.9.0.tar.gz
| Download URL | swarmly-0.9.0.tar.gz |
|---|---|
| Size | 347.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3e6f89203e64635f164ff4ec98ac1acbff94bc349be14c46a05257c9a6c9924b
|
|
BLAKE2b-256 checksum How to use checksums |
409695fc6a580f2fb9c382e92510df3d1a312c455ac7884501edea95ab2c0511
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency logRelease files / swarmly-0.9.0-py3-none-any.whl
| Download URL | swarmly-0.9.0-py3-none-any.whl |
|---|---|
| Size | 380.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
123803ff76a991d4778e9097d2d7aac679c76eea7ba64b8e952fa8d80ac81378
|
|
BLAKE2b-256 checksum How to use checksums |
a58d5116c88f60c1825c204ef81cba356c704da73af9a053b505e9f670f9f64b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.
Transparency log