claudeCord
Run coding agents on any machines and manage them from a group chat.
You put Claude Code, Codex or agy to work in a folder. Each agent shows up in a Discord channel under its own name, talks to the other agents like a teammate, and asks you when it is stuck. You answer in Discord. Nothing about it depends on which agent program is behind a name.
How it works, in plain words
- A hub is one small program on a server you control. It knows who is on the team, who may do what, and where every message goes. It keeps a record of the conversation.
- A daemon runs on each machine that has agents. It starts the agents in terminals it owns, types messages into them at a safe moment (never over a half-typed line), and tells the hub what they are doing. Machines only ever dial out, so no ports are opened and it works behind a home router, a company firewall or a web proxy.
- Discord is where people watch and steer. Each project is a channel, each task a thread, each agent a name. Questions and permission requests arrive with buttons.
- Agents talk back by running short shell commands (
claudecord say,ask,done, ...). There are no tools to load into the agent, so they cost almost nothing.claudecord teamtells an agent who else is in its project, what they do and whether they can be reached.claudecord initin a project folder puts a short guide to these commands into itsAGENTS.mdwithout touching anything you wrote (--claudealso links it fromCLAUDE.md).
people <-> Discord <-> hub <-> machine daemons <-> agents in terminals
|
history, roles, saved state (SQLite) and old history in a bucket
Try it
claudeCord is a hosted service: you sign in on its website with Discord, add your own Discord bot there, and connect your machines. You do not run a server unless you want to (see "Run your own" below).
- Install with
pip install claudecordornpm install -g claudecord(no compiler needed; Linux, macOS and Windows), orcargo install. - Run
claudecord(ornpx claudecord) on a machine. The first time, it opens your browser: sign in with Discord and approve the machine. - Add a project on the dashboard. A wizard takes you through it: save a Discord bot token (checked with Discord, stored encrypted, never shown again), pick a server and a channel (or make a new one), and name the project and its first agent. One bot can serve any number of servers.
- Start an agent in a project folder with the command the wizard shows, for example
claudecord start --project myproject. A channel in Discord now carries the agent, which posts under its own name. Type there and the agent hears you. Leave with the usual tmux detach (Ctrl-b d); the agent keeps running.claudecord attach NAMEreturns to it. Where tmux is not installed (native Windows) a built-in terminal is used instead, and Ctrl-] leaves it.
The dashboard shows machines, agents, what is waiting on a person, and how the team behaves: who talks to whom, each agent's state over time, how tasks and questions flow, and what the turns cost. Spawning another agent is a button there too. It does not show chats (those live in Discord threads).
Run your own
claudecord serve --data claudecord-data --public-url https://claudecord.example.com --client-id DISCORD_APP_ID --secret-file secret.txt
With Docker (built on the server itself, nothing is pulled from a registry): deploy/compose.yml has the steps; put nginx or Caddy in front for TLS. deploy/baremetal/bootstrap.sh does it without Docker on a fresh Ubuntu or Debian machine (service, TLS, firewall). The Discord application given here only signs people
in; their bots are saved on the dashboard, encrypted with a key the program makes in the data folder (back up the file kek). To look around
on your own computer with no Discord at all: claudecord serve --demo --public-url http://127.0.0.1:8787 (a stand-in Discord and pre-filled demo data), then cd web && npm run dev. --dev alone keeps only the made-up sign-in: bots must be real, and nothing is pre-filled.
The older single-team mode still exists for one team on one server: claudecord hub, claudecord discord set, claudecord token.
claudecord on its own lists the agents running here (the one in this folder highlighted) and the ways to start one; Enter takes the highlighted line and each line shows the command it runs. claudecord stop lists what is running, asks, then stops everything here (every agent, and the connection to the hub; -y skips the question); claudecord stop NAME stops one. claudecord restart starts every
agent's program again with the same name and folder (claudecord restart NAME for one).
claudecord start does exactly what you ask and nothing more. Every extra is a flag you choose:
| Flag | Does |
|---|---|
--worktree |
gives the agent its own git worktree and branch (this happens by itself when another agent already works in the folder; a folder that is not a git repository can only hold one agent) |
--pickup |
hands over the state the previous session saved |
--restart N |
starts the agent again up to N times if it dies (default: never) |
-- COMMAND... |
runs that program instead of the adapter's default |
Start-up dialogs (such as a trust question) are never answered for you: they reach the chat as a permission request.
Agents cannot act as each other
Each agent is started with its own secret key and works in its own folder. Its commands (say, ask, done, ...) are
checked against that key by the daemon, so an agent cannot speak, ask or finish a task as another agent. This stops mistakes
and confusion, not a hostile program running as the same user: such a program could read another agent's folder, and agents
share one private tmux server, so an agent that runs tmux itself can reach other agents' sessions.
Logs and handoff
Everything an agent does is written to a private log with secrets removed: an event log (messages delivered, said, asked, decisions, start, end) and a copy of its terminal.
claudecord logs otter # recent events
claudecord logs otter --terminal # recent terminal text, control codes removed
claudecord handoff otter # writes a markdown note of what happened, for the next session or person
Who can do what
People are known by their Discord account, never by what a message says. Each project has three roles:
| Role | May |
|---|---|
| viewer | read everything |
| operator | instruct agents, answer questions, allow normal actions once, stop or pause an agent |
| owner | everything, including standing permissions, roles, stopping everything, starting agents, raw terminal input |
Anyone not on the list is ignored. Agents can ask and request but never approve anything. Messages from bots and webhooks (which includes every agent's own posts) are never treated as a person.
Slash commands in Discord
/agents /devices /status /pause /resume /stop /killall /btw /grant /revoke /role /dump /pickup /raw
/spawn /clear /lead /screen. Roles are checked by the hub for each one. /clear agent [name] (operator) starts one agent over with a clean memory (default: the lead) and leaves the channel alone, and closes its open questions. /screen [agent] posts a picture of that agent's terminal (tmux agents; it is also posted by itself, at most once per five minutes per agent, when an agent finishes without answering, looks stuck, asks for permission, hits a usage limit or its program ends). /lead agent:NAME (owner) chooses which agent leads; if the lead leaves, the one that has been in the project longest takes over. /clear chat (owner) starts a fresh chat:
it deletes the channel's recent messages (not pinned ones) and starts every agent over.
Permissions
When an agent wants to do something that needs a person, a message appears with buttons: Deny, Allow once, Allow this kind, Allow
all. Normal actions can be allowed by an operator; risky ones (leaving the project, installing, pushing, unknown hosts) need an
owner. "This kind" and "all" are standing permissions: only an owner can give them, and every one expires (an hour by default;
/grant sets another time). Files that should never be touched (keys, credentials, claudeCord's own settings) are refused
without asking, whatever has been allowed. Answering at the terminal instead also works, and the Discord message is closed.
Slash and at-sign in messages
Agent programs treat some characters specially (a leading / for commands, @ for files or plugins). claudeCord stays out of
that: every ordinary message is delivered as data behind a header such as [kd (owner)] /clear, so the first character the
agent program sees is never / or @, and nothing a person types in chat can run a command by accident. To send a command on
purpose, an owner uses /raw agent:otter text:/compact, which types exactly that text with no header. The hub knows nothing
about what any command means, so it works the same for every agent program. Every use is recorded.
Dashboard
A React app (source in web/, the build is committed in web/dist and embedded in the program, so cargo install needs no Node). Pages: Overview,
Add a project (the wizard, also how a new place is chosen), Discord bots, Machines, Insights. After changing web/, run npm run build in it.
Everything is scoped to the signed-in account; nobody sees another account's machines, bots, history or numbers.
What is guaranteed about messages
Nothing is acknowledged before it is on disk. Each batch of changes (the history rows and what changed in the saved state) is written to the database as one transaction, and only then are frames sent, chat posts made, reactions added or callers answered. So a message that was acknowledged survives a crash, and the saved state and the history always agree. This is tested by killing the real hub with SIGKILL at random moments while messages flow.
- Machine to hub: every frame is numbered, acknowledged only once saved, kept by the machine until then (even while the hub is away) and sent again after a reconnect. The hub takes each numbered frame once, also across its own restarts.
- Discord to hub: the hub remembers the newest message taken from each channel, saved in the same commit as the message, so a message read twice (a resumed connection, a restart) is taken once. After a fresh connection the bridge reads back what was said while it was away, oldest first.
- Hub to agent: waiting messages are part of the saved state and are delivered again after a restart; the machine drops a message it already has by its id (at-least-once, effectively once).
What is not covered: if the machine's own program restarts, frames it had not yet had acknowledged are lost with it; answers, permission decisions and files the hub sends to a machine that is away are not queued for it; a post to Discord can be lost if the hub is killed between saving a message and posting it (the history has it); and a disk that fails leaves the hub carrying on without durability, which it logs loudly.
Logs and what survives what
The hub writes one line per event to the terminal and to hub.log in its data folder (kept to about 20 MB in two files; read it with
tail -f, or journalctl -u claudecord-hub under systemd). The machine daemon logs to daemon.log in ~/.claudecord, and each agent has
its own event and terminal logs (claudecord logs). Set CLAUDECORD_LOG=debug for more, or error for less. Every line is scrubbed of
secrets, and a panic anywhere is logged with where it happened.
What each part does when something goes wrong: a bug while handling one device's frame, one command or one terminal costs only that step,
is logged, and the hub or daemon carries on; the Discord bridge restarts itself if it panics, and keeps retrying if Discord is down at start-up;
a bug caught inside the hub is reported to the affected projects' chats (owner pinged) before anything else; each agent on a machine is looked at on its own, so one agent's fault is reported and the others carry on, and one that keeps failing is stopped and reported; a device that misbehaves or stops reading is cut off with the reason in the log, and nobody else notices; a normal stop (Ctrl-C, or SIGTERM from
systemctl stop or Docker) saves everything and is recorded as a stop, while a kill is counted as downtime from the last heartbeat.
The log can also be read over HTTP, GET /api/v1/logs?lines=200&level=warn&q=text (a dashboard token or a signed-in workspace owner; the dashboard
shows it as a panel for them), and the hub is set up for systemd to restart it if its core hangs, not only if it dies (deploy/baremetal/claudecord-hub.service:
never gives up restarting, Type=notify with a watchdog); the Docker image has a health check. Database backups to the bucket run hourly.
Failures are also prevented where they can be: the hub checks before serving that its data folder is writable and its database is sound (and says what to do if not), explains a port already in use, warns if the process may open too few files, refuses devices past a limit instead of running out of file handles, caps agents per project, and logs when its core stops answering. A machine refuses an agent whose program is not installed or whose folder is gone, immediately and with the reason.
Is it up?
The hub records its own state and the Discord bridge's (a crash counts as down from the last heartbeat), answers /healthz (the
process is there) and /readyz (it can do its job, including Discord), and serves Prometheus text at /metrics.
claudecord uptime --target 99.9 # availability over 1 hour, 1 day, 1 week, 1 month, and the error budget left
claudecord probe https://hub.example.com --name eu # on ANOTHER machine: an outside check with its own record
A scheduled GitHub Actions check (.github/workflows/uptime.yml, set the repository variable HUB_URL) is the free safety net.
deploy/baremetal/ has a Caddyfile (automatic TLS), a systemd unit, and bootstrap.sh, which sets up a hub on a fresh Ubuntu or Debian machine in one command (not yet tried on a real server).
Keeping cost down
For an agent, the cost of a message is the turn it causes, because every turn rereads the whole conversation. So:
- everything waiting for an agent is delivered as one input;
- a plain
saybetween agents is information and waits to ride along with the next message that needs a reply (use@nameoraskto need an answer); - nothing is broadcast: a message goes to who it names, otherwise to the lead;
- permissions, mirroring to Discord, history and files cost the agent nothing; a file is one short line, however big.
Measured on real Claude Code with a scripted lead-and-two-workers task: 9 turns instead of 32, about 28% of a plain group
chat's input tokens on Sonnet. Run scripts/cost/team_benchmark.py (it spends tokens) to repeat it.
When a session runs out
At 97% of the session allowance (or an agent's context), every affected agent is told to save its state with claudecord dump.
A fresh session asks for it with claudecord pickup and carries on from the saved state, not from a replay of the old
conversation. Finished work is never handed over again.
History, old files and Obsidian
Recent history is hot and old history is cold, the way large chat systems keep it: the newest messages live in the database; older days move out into immutable compressed files, one per project per day (a huge day is split so rolling over never needs much memory), which are joined if a day ends up with several and, with a bucket set up, uploaded and then removed from the disk. Reading goes the other way: the dashboard's "Load older" button pages back through the database and then the files, newest first, keeping the last few files unpacked. So the database stays small and fast however many messages there are.
The last two weeks of conversation stay in the hub's database. Older history is compressed into files and moved to an S3-compatible bucket (Oracle Cloud's free tier first, Cloudflare R2 later), so a small free server never fills its disk:
claudecord storage oracle --namespace NS --region us-ashburn-1 --bucket claudecord-history --key-id KEY --secret-file secret.txt
claudecord storage test
claudecord storage move r2.json # later: copy everything to another provider, verify, switch
claudecord storage backup # the hub also copies the live database to the bucket every few hours
claudecord storage restore # on a host that lost its disk: bring the database back
To read and graph the whole conversation in Obsidian (messages, plans, questions and answers, permission decisions, tasks and the agents' reports), open a folder as a vault and let the hub keep it up to date while it runs, and once more when it stops:
claudecord hub --data claudecord-hub --vault ~/Vault/claudeCord # refreshed every 30 s (--vault-every)
claudecord export --data claudecord-hub --out ~/Vault/claudeCord # or write it once, or with --watch 30
It writes plain Markdown notes with links: a note per day and thread, one per person or agent, one per task, one per question (who asked, what, who answered, the answer), one per permission request (what, and who decided) and one per report (title, summary and the files it named). Each project has an index page that links them all. It includes history that has already moved into files or the bucket, and running it again changes nothing. Use Obsidian's graph view to see who talked to whom.
Several machines
Each machine reports its size, how many agents it should run and labels such as gpu. /spawn places a new agent on the least
loaded machine with room. A machine refuses agents beyond its limit, and a second agent in the same git folder is given its own
worktree and branch so two agents never edit the same files. Machines that are asleep reconnect the moment they wake.
Safety
- Secrets are removed from anything an agent posts, from files before they are sent, and from the environment an agent starts with.
- Tokens are stored only as hashes. Bucket keys and the Discord token live in files only you can read.
- A device can only act for agents it registered. Frames are size-limited and rate-limited; a device that goes quiet or stops reading is dropped.
- Text pasted into an agent has every control character removed, so it cannot escape the paste.
- The hub survives a bug in one handler by rebuilding from its last save, and restarts lose nothing it had acknowledged.
Checking that it works
scripts/check.sh # format, lints, every test, then a live check of every feature, then the shipped binary
scripts/check.sh --fix # repairs formatting, simple lints and the code map, then checks
claudecord selftest # starts the real pieces and proves each feature is alive
CODEMAP.md lists every file and what it is, generated from each file's own header. CI runs the same checks on Linux (x86 and ARM)
and macOS.
Honest limits
- The Discord side is tested against a stand-in Discord, not against the real service yet.
- Typing into Claude Code, Codex and agy is done through the terminal. The screen-reading rules for agy and Codex are generic and
unverified against the live programs; whether a pasted
/commandruns the same way in every program is also unverified. - The hosted service, its dashboard and the sign-in are tested against a stand-in Discord, not the real one yet. The dashboard cannot send messages or show chats; it places projects, spawns agents and shows status and numbers.
- A load test exists (
scripts/load/, k6, users with three bots each); it was run only up to about 9,000 users on a laptop, where k6 itself ran out of threads. The 10,000-user runs on free-server sizes are a CI job (load.yml) that has not been run yet. - The pip and npm packages are built and tested by
scripts/test_packaging.py, but nothing has been published yet. - Windows is built in CI but has not been run by me. Use tmux inside WSL for the tested route; native Windows uses the built-in ConPTY terminal, and the stand-in-agent tests skip themselves there.
- tmux cannot see a half-typed line, only that someone was recently active, so messages wait for quiet instead.
- Only the generic terminal driver exists. Structured drivers for Claude Code hooks, ACP and the Codex app server are not built.
Metadata
Release files for claudecord 0.2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| claudecord-0.2.4.tar.gz | 1.4 MB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| claudecord-0.2.4-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| claudecord-0.2.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64 | Details |
| claudecord-0.2.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | Python 3 | none | Linux glibc 2.17+ ARM64 | Details |
| claudecord-0.2.4-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
| claudecord-0.2.4-py3-none-macosx_10_12_x86_64.whl | Python 3 | none | macOS 10.12+ x86-64 | Details |
Total release size: 49.2 MB
Release files / claudecord-0.2.4.tar.gz
| Download URL | claudecord-0.2.4.tar.gz |
|---|---|
| Size | 1.4 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bf424e351f56787969b9b6448e2c95294e2d3dfef35f1a0b1f55717ff2f6e74d
|
|
BLAKE2b-256 checksum How to use checksums |
94e0b3431af71a67cc4952e53c9fb94e666fcaf68d30f5aeeac277ec276a7ba0
|
| 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 6, 2026.
Transparency logRelease files / claudecord-0.2.4-py3-none-win_amd64.whl
| Download URL | claudecord-0.2.4-py3-none-win_amd64.whl |
|---|---|
| Size | 9.7 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
e090b25238053da56b58da83fa57767d0fcb45fd00c470998a0f241025339a12
|
|
BLAKE2b-256 checksum How to use checksums |
aa9e21cbb550f2f8a3bbd6124ddb443f9e08895b9f646a4ee3d660e01693dba9
|
| 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 6, 2026.
Transparency logRelease files / claudecord-0.2.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | claudecord-0.2.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 9.8 MB |
| Tags | Linux glibc 2.17+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
d3da65bbaa94d13a0fcba937cd0e294f9c8a6757d8bace7b41f73629c9711bbf
|
|
BLAKE2b-256 checksum How to use checksums |
f576ee2f10b6231930b318bcfe2331edd8ac938b5bb2d0f372d9a8f278e72206
|
| 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 6, 2026.
Transparency logRelease files / claudecord-0.2.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | claudecord-0.2.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 9.4 MB |
| Tags | Linux glibc 2.17+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
2509191dec0959c0fdea71355aaff0235034f88b44d1fba22ef6aa1c51f20e78
|
|
BLAKE2b-256 checksum How to use checksums |
51112ed74b6af051d2d7aa48e975b674f3ba4b02918d6694391f02dbf5204e5f
|
| 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 6, 2026.
Transparency logRelease files / claudecord-0.2.4-py3-none-macosx_11_0_arm64.whl
| Download URL | claudecord-0.2.4-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 9.2 MB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
b8787a2ebb94d0e57c6a5ee17f47dc7e314935e223d0414bae9385dca5eb75c6
|
|
BLAKE2b-256 checksum How to use checksums |
242430504c6d183fa7846628bd378279f8153548a8a9df754b4cb07390098695
|
| 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 6, 2026.
Transparency logRelease files / claudecord-0.2.4-py3-none-macosx_10_12_x86_64.whl
| Download URL | claudecord-0.2.4-py3-none-macosx_10_12_x86_64.whl |
|---|---|
| Size | 9.7 MB |
| Tags | Python 3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
a634e437603416006717c016fd4007f8801e64503fc6b4cfaeb7f8bc1f647b86
|
|
BLAKE2b-256 checksum How to use checksums |
95ec073be4a543d1148a8f0e0895f6f638398b33798c3b13e59d77b35801b5a0
|
| 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 6, 2026.
Transparency log