An agent CLI assumes it is the only one, on the only machine. Claude Code, Codex, opencode, qwen — each owns a terminal, a working tree and a model, and as far as it is concerned that is the whole world.
My world is not shaped like that. The GPU is in one tower. The mail and web servers are somewhere else entirely. The router is a fanless Celeron that never sleeps and has no AVX instructions at all. The laptop is wherever I am, which is frequently behind someone else's NAT. The work does not sit in one place, so the team doing the work should not have to either.
nexmesh is what I built for that: a hub, a node daemon on every machine, and a team of agent CLIs that spans the lot. Rust, GPLv3, public.
The design is not mine, and that matters
nexmesh is a reimplementation of multiagents by Arman Andreasyan (@zetbrush). That project worked out what a group of agent CLIs actually needs in order to behave like a team, and this one would not exist without it. If you want this on a single machine, use the original: it is the more mature tool, and it is where the ideas come from.
Four of those ideas are inherited essentially unchanged, and each one would
have taken weeks to rediscover. A slot is a seat on the team with a
stable identity and an agent is whatever process is currently filling
it — that split is what makes crash recovery coherent instead of
improvised. A task moves through an explicit state machine, and an agent cannot
disconnect in the middle of it. A message counts as delivered only once it has
actually been surfaced to the model, so a dropped hand-off is retried rather
than lost. And unread messages ride in on the agent's next MCP tool result,
under a [PENDING MESSAGES] header — the trick that makes an
arbitrary MCP-speaking CLI usable with no bespoke code at all.
What follows is only about what changes when the team stops sharing a kernel.
Every machine has a pid 4242
This is the whole problem in one line. A single-host orchestrator can ask
whether an agent is still alive with kill(pid, 0), and the answer
is true. Send that same probe from a hub to an agent on another machine and it
lands on an unrelated local process that happens to hold that number.
The orchestrator is told the agent is healthy. It is not even asking about the
agent.
So identity here is the triple (node, pid, start_ticks), and
the hub cannot signal anything at all. Only the node that owns
a process may signal it, and only after verifying that triple. That is a
deliberate amputation: the component with all the state is the component with
no ability to act on a process directly.
Three more assumptions break the same way. The original's broker HTTP API
has no authentication, which is entirely reasonable on loopback and is remote
code execution the moment it is not — the API's job, after all, is to
spawn processes. Stdio pipes to a child process do not cross a machine
boundary, so the thing that drives a CLI has to live where the CLI lives;
the node daemon has no equivalent upstream. And file locks keyed by path are
meaningless across hosts, because src/** on one machine is not
src/** on another. Locks here are keyed by scope: local to a node,
or shared across a tree that several machines hold.
Nodes dial out, always
A node opens one WebSocket to the hub and the hub never connects back. Spawns, kills, message injection and interactive terminal attach are all multiplexed over that one socket.
That single choice is why the laptop works unchanged from a hotel network, why a node behind NAT needs no port forwarding, and why a machine with a public address does not have to open anything inbound to join. Starting with two machines does not paint you into a corner at the fifth.
Adding a CLI is a TOML file
The original has three hand-written drivers. Here a CLI is a declarative
descriptor, and delivery has three tiers. The floor is mcp_only:
any CLI that speaks MCP works with no bespoke code, because messages arrive on
its next tool result. Above that, a CLI with a structured stdin protocol can be
steered mid-turn, and a genuinely odd one can have a plugin.
An agent does not have to be a CLI at all. The hub is itself an MCP server:
point any MCP client at its /v1/mcp endpoint and it takes a seat on
the team. The mail server's assistant joined the fleet that way — one
command, thirty tools, one seat, no code written for it.
Reading the codebase once, not once per agent
Four agents on four machines pointed at one repository will each read the same two hundred files to answer the same question. That is four lots of tokens for one answer, and four slightly different understandings of the codebase afterwards.
So nexmesh parses the tree with tree-sitter — Rust, Python, JavaScript, TypeScript, Go, PHP, shell — and answers structural questions from a graph instead: who calls this, what does it import, which subsystem does it belong to, is there a path from here to there. No embeddings, no vector store, nothing leaving the machine. Subsystems are not labelled by hand, they are found with a Leiden community detection pass over the call graph.
That idea comes from graphify by Graphify Labs, which was evaluated as a dependency and reimplemented instead — it is Python, this is Rust, and no code was copied. What it contributed is the shape of the answer, including the part I would have got wrong: distinguishing edges that are syntax facts from edges that are guesses. A call resolved by name is an inference, and it must not be handed to an agent looking like a certainty. If you want a code knowledge graph on its own, use graphify; it is the better tool at that one job.
Memory that knows how far it reaches
A shared knowledge store is upstream's diagnosis of why these systems fail: context drift between agents. Across machines it needs a scope. A note can belong to one agent, to a group, to a session, or to the whole fleet, and a lookup resolves nearest-first so a local convention shadows a global one. Writing to the global scope takes a capability, because a fleet-wide fact asserted by one confused agent is worse than no fact at all.
Everything is searchable, every revision keeps its author, and entries can be given a lifetime. An agent that comes back after a crash — or after a week — finds its own thread where it left it.
A team of unequal agents
Agents on a fleet are not interchangeable. One sits next to a GPU, one has a different model, one is the only process that can reach a particular service. So a slot can request a model and an agent reports the one it actually got, which are not always the same thing. Agents carry capability labels and can be addressed by capability rather than by name. A session can carry a token budget that pauses it on the way past, instead of letting you discover the spend afterwards.
When an agent needs something it cannot do itself, it asks, and the ask
blocks until someone answers. Sometimes the answerer is me. Sometimes it is a
bridge: a small participant that sits beside a media and OCR service and offers
transcription, speech, embeddings and image work to the team. It discovers what
that service can actually do by probing it rather than trusting its
documentation — including routes that only accept POST, where a GET
answering 405 proves the route exists and a 404 proves
it does not. Sixteen capabilities found that way, several of which the service's
own capability document had never mentioned.
The bridge holds that service's token. nexmesh itself holds no model credentials anywhere, by design.
Security, because this thing spawns processes
An orchestrator that can start a process on any machine in a fleet is a remote code execution service with good manners. It is worth being blunt about that, because it decides the defaults.
The hub runs its own certificate authority and issues a certificate per node; nodes and operators connect over mutual TLS; enrolment goes through short-lived pairing codes where the private key is generated at the far end and never travels; certificates are bound to the credential that claimed them and can be revoked. The hub refuses to serve an unauthenticated operator API on anything but loopback, and says so rather than quietly doing it. The dashboard has real accounts with Argon2id password hashing, and the first one can only be created with a code printed on the hub itself — a hub reachable from the internet must not be claimable by whoever arrives first.
Where it is now
Running, on my own fleet: a hub on the fanless router, nodes on the GPU tower, the server and the laptop, agents spawning across all of them, behind nginx with a certificate and a dashboard. 326 unit tests and 23 end-to-end suites, none of which spend a penny of API credit — every agent CLI the tests drive is a fake that speaks the real protocol.
It is GPLv3 and the source is at github.com/nextime/nexmesh, with static binaries built for machines older than the one that compiled them. If you run more than one machine and you have ever wanted the agent on your laptop to hand work to the one sitting next to your GPU, it is for you.
And if you only run one machine: use multiagents. That is not modesty, it is the correct recommendation.