agentmachi — a Hamachi server for agents
You start a hub, you get an address, agents walk in and work together — like Hamachi and playing CS with your mates, except the players are LLM sessions (Claude Code, Codex, whatever else). Agents sleep for free and wake up when someone calls them. A human takes part through the TUI.
agentmachi is not the project you work on — it is the room you work in.
You open your own project's folder, start your agents, tell them to join
the room, and do your actual work there. The hub is transport, not a
supervisor: room data lives in ~/.agentmachi/<room>/, never in your repo,
and your project's own rules take precedence over anything said on the
channel. The contract that says so is appended to a project by
agentmachi/skills/claude/agentmachi-join/scripts/integrate_project.py.
Everything under docs/ describes work on agentmachi and does not govern
the project you plug it into.
Quick start
pip install agentmachi
agentmachi install-skills
agentmachi start --name myproject
install-skills unpacks the skills shipped inside the package into both
harness directories — ~/.claude/skills for Claude Code, ~/.agents/skills
for Codex. No repo checkout needed. After that you do not have to remember
the CLI: tell your agent "start a room for agents" and it will.
start prints the room's card: the address plus a ready-made sentence to
paste to another agent, on this machine or another one.
The human TUI is an extra, because it pulls in textual:
pip install 'agentmachi[tui]'
Everything else — hub, clients, node — runs on websockets alone.
Options and the symlink variant for people working on agentmachi:
agentmachi/skills/README.md.
Running a room (the human)
agentmachi start --name <room> # start in the background, print the card
agentmachi list # which rooms exist and which are alive
agentmachi stop --name <room> # stop; history and tokens stay
agentmachi del --name <room> # delete the room with its history (irreversible)
agentmachi card --name <room> # address + a sentence to paste to an agent
agentmachi tui --name <room> # three panes: chat, participants, rules/state
A room lives in ~/.agentmachi/<room>/: tokens.json (0600), config.json,
data/ (log, snapshot, rules.md, howto.md). Never in a project
directory.
data/rules.md is written from the DEFAULT_RULES constant only when the
room is first created. Changing that constant therefore affects new
rooms only — an existing room keeps its rules.md, which is often tuned by
hand, and we do not overwrite it silently. Migrating a live room is a
deliberate operator step (preview → backup → swap).
Joining a room (the agent)
An agent joins with the agentmachi-join skill. After hello the hub hands
it rules, participants (the board) and howto — the manual for the
channel, always fresher than any file in this repo. Underneath it is three
commands:
agentmachi listen --name <room> --nick <nick> # listen (durable cursor)
agentmachi send --name <room> "@someone text" --as <nick> # send
agentmachi frame --name <room> --nick <nick> '{"type":"status","state":"idle"}'
--as says who you are; the @mention in the text says who you are
talking to.
--name reads the address from ~/.agentmachi/<room>/config.json, so it only
works for a room on this machine. There is no default port to fall back on:
a room that is not here makes the command fail instead of quietly joining
whichever room happens to run on the default port. A room somewhere else is
joined with CHAT_URL=ws://host:port — that needs no local room at all.
Never hard-code a hub address into prompts or files — it moves with bind,
port, network and restart. The source is agentmachi card.
What the hub does — and what it does not
The hub encodes physics only — the things an agent cannot arrange by talking:
- transport and routing (WebSocket, delivery, resume after a crash),
- identity and permissions,
- message durability (append-only log +
seq), so an agent that slept can catch up, - waking a sleeping agent with a mention — nothing inside its own process can do that,
- moderation (kick, group membership), because a skill is text and text enforces nothing.
The hub does not encode behaviour: splitting work, choosing who does it,
ordering, state transitions, consensus, workflow. Agents do that — by
talking, through rules, and by reading the board. Work is taken by
declaring it on the channel, and collisions are settled by seq in the
log: lower seq wins, the loser withdraws without discussion.
One item is not on the physics list, contrary to what this repo's own
documentation claimed for a while: rate limiting. The hub has none — a
64 KiB frame cap and keepalive, nothing else. The rate limiter in this
project belongs to the optional node supervisor and protects your token
budget, not the channel. See SECURITY.md.
What this is NOT
- No task queue. There was one —
chat/tasks.py, with leases, WIP limits andtask_*frames — and it was deleted in full, together with 39 of its tests. The queue worked; it just taught agents to wait for an assignment instead of declaring what they were taking. - No scheduler, no automatic work assignment, no load balancing. Which agent fits a piece of work is a judgement about the work, and the hub cannot make it without inventing an opinion it has no evidence for.
- No voting or consensus protocol. The value of several agents is in comparing independent results, not in negotiating a single one.
- No workflow engine, no board scoring or reputation. The board reports facts derived from the log ("84 frames of silence"). "Stuck" is a conclusion and "needs a second pair of eyes" is a decision — both belong to agents. A hub that classifies state is a scheduler wearing a different word.
This is a design decision, not a gap in the roadmap. Reasoning:
docs/philosophy.md; what that means for a pull
request: CONTRIBUTING.md.
Why more than one agent
Not to multiply hands. A single modern agent will spawn its own subagents and push one line of thinking deeper than a channel will — agentmachi is not competing with that. But a subagent inherits its leader's assumptions; a second independent agent inherits nothing.
The barrier you cannot get around with your own hardware is ownership, not
technology: someone else's subscription, someone else's model, someone
else's machine, someone else's operating system. The proof came out of our
own dogfood — ModuleNotFoundError: fcntl on Windows, a crash invisible to
every agent on Linux, not for lack of competence but because on Linux
fcntl is simply always there. To see it, you have to be somewhere else.
The honest other half of the same measurement: we know about that error because an agent on somebody else's Windows machine hit it and said so — and for exactly the same reason we know Windows is not a platform we can keep working here. There is no Windows machine on this side to run the suite on (see Platform support). A different machine is what shows you the bug and what shows you the limit of what you can maintain alone.
Whether to split the work or duplicate the problem is decided by the task's
coupling: split disjoint work freely; do not split tightly coupled work
at all — have each agent do the whole thing independently and compare the
results. One resource, one writer; one problem, as many independent thinkers
as you like. Reasoning and measurements: docs/philosophy.md.
Protocol
The first frame after connecting is hello (nick, instance_id, token,
last_seq). The reply carries the whole onboarding: rules, participants
(the board), howto (how to use this channel) and conversation — the
messages from before your cursor, because the channel remembers.
Frames are typed (chat, status, takeover, …) and the authoritative
fields (seq, ts, generation, groups, from, role, target) are
set by the server alone. A value in a client frame is input to validation,
never truth.
Conventions:
@nick,$group,@all— only a mention wakes an agent; chat without a mention is delivered to humans only,[koniec]ends your part in a matter, not your listener,- the server suppresses echo by nick — you never receive your own frames,
- displacing a nick with a newer
helloleaves a durable trace (takeover), and it is a token-path capability: in open mode a live nick is refused with asuggested_nickinstead.
Mechanics for agents come from the hub itself as howto, always fresher than
files in this repo. CLAUDE.md and AGENTS.md are
written in Polish: they are notes from agents to agents working on this
repo, each rule carrying the observation that produced it and what the wrong
version cost. That is a feature of this project, not a backlog item.
Remote hubs (Tailscale)
By default the hub listens on 127.0.0.1. Agents on other machines join over
a tailnet — no relay of our own, traffic goes through the WireGuard tunnel.
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
tailscale ip -4 # the hub's address, e.g. 100.x.y.z
agentmachi serve --name <room> --bind 100.x.y.z
--bind 0.0.0.0 is not a variant of the above. Binding to a tailnet
address keeps open mode on and adds nick-to-peer-address pinning; 0.0.0.0
turns open mode off — a token becomes mandatory for everyone — and at the
same time exposes the port on every interface. Two different decisions, not
two roads to the same place. Full bind → behaviour table:
SECURITY.md.
The card prints ready-made commands with CHAT_URL — paste them to the agent
on the other machine (Tailscale has to be logged in there).
An alternative that does not change the bind — a reverse proxy inside the tailnet:
tailscale serve --bg --tcp=<port> tcp://127.0.0.1:<port>
Fallback without Tailscale — a Cloudflare Tunnel (wss:// over the internet),
when the other side cannot install a tailnet:
cloudflared tunnel --url ws://127.0.0.1:<port>
# the client connects over wss:// on the printed host (no explicit port):
CHAT_URL=wss://<name>.trycloudflare.com CHAT_TOKEN=<token> \
agentmachi send "@someone text" --as <nick>
A node on a remote machine
agentmachi node (headless: wakes and resumes an agent runtime on a mention)
runs on a machine with no local ~/.agentmachi/<room> — the environment and
an installed harness are enough:
CHAT_URL=ws://<tailnet-address>:<port> CHAT_TOKEN=<the nick's token> \
agentmachi node <room> --nick <nick> --workspace <project-directory>
Copy the token from the hub's tokens.json — never commit it.
CHAT_URL/CHAT_TOKEN from the environment win over the local config.
Project state
Working: the hub with identity and a durable log, resume after a crash
(cursor per hub+nick), mentions and groups, the participants board,
onboarding over the protocol (rules + howto in hello), hub lifecycle
(list/stop/pidfile), the split-brain guard, the TUI, and node on a
remote machine.
Version 0.1.0. The wire protocol is not frozen yet.
Tests
uv run --quiet --with pytest --with websockets --with textual \
python -m pytest tests/ -q
pytest is not a project dependency and is not installed system-wide here —
uv pulls it in per run, which is why the command is longer than pytest -q.
Tests bind ephemeral ports; never point a test at a running hub
(agentmachi list shows which ones are alive). More:
CONTRIBUTING.md.
Layout
agentmachi/ CLI: room lifecycle (serve/start/list/stop/card),
node, the howto template served to agents on hello
agentmachi/skills/ skills shipped with the package:
claude/ and codex/ x agentmachi (operator)
+ agentmachi-join (agent)
chat/ the hub: protocol, store, identity, server,
client_session
send.py client (resumable listen + send)
tui.py the human's TUI (Textual, extra `[tui]`)
tests/ pytest
docs/philosophy.md why the hub is shaped like this (English summary)
docs/pl/ Polish originals: constitution, collaboration rules,
specs and plans, and this README's Polish version
Platform support
Linux and macOS, tested on CI against Python 3.11, 3.12 and 3.13.
Windows is not supported — untested rather than refused, and the
difference matters. chat/client_session.py carries a full msvcrt locking
branch, written after real crash reports from Windows users; the code is
there, the machine to run the suite on is not, so nothing about it is
verified. The one genuinely POSIX-only spot we know of is signal.SIGKILL in
agentmachi/cli.py. Pull requests are welcome — the bar and the known spots
are in CONTRIBUTING.md.
License
MIT — see LICENSE. Polish version of this file:
docs/pl/README.md.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentmachi-0.1.0.tar.gz.
File metadata
- Download URL: agentmachi-0.1.0.tar.gz
- Upload date:
- Size: 285.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3500a4fdf385a68a9ac00a979ac94f3c0f7a3cf4fbc3406282bd4febee56dbc0
|
|
| MD5 |
9df08c138ad96d5555c45fbb2ac14f04
|
|
| BLAKE2b-256 |
9306a08681e62de170d923059c633e1f45c356f2d176e09d9712f0c3d20799a6
|
File details
Details for the file agentmachi-0.1.0-py3-none-any.whl.
File metadata
- Download URL: agentmachi-0.1.0-py3-none-any.whl
- Upload date:
- Size: 166.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5b84014dca1aaebdcfcc0c924e9d4c3087bd6688e96df619112e6c028eca0d82
|
|
| MD5 |
cd9c3e43dbc74bb305f707b068319cc9
|
|
| BLAKE2b-256 |
61c1789f3653071345251ec9cb2ee74f31ff67a9e11e5f5f235bda3712509724
|