The Developer Platform
Build apps on top of MeshHold.
Build apps on top of MeshHold. REST API, Server-Sent Events stream, the mesh protocols, and a Claude Code / OpenCode / Gemini CLI / Codex agent surface.
REST API
All endpoints live under /api/v1/. Bearer-token auth (the same token
the web UI uses). Configurable bind on api.listen_addr.
Key endpoints:
GET /api/v1/vaults— list vaultsPOST /api/v1/vaults— createGET /api/v1/vaults/{id}/files/{path}/DELETE …— read / delete a filePOST /api/v1/vaults/{id}/upload/{path}— upload oneGET /api/v1/events/stream— global SSE for chat and agent-activity events
The full reference is the REST API reference.
SSE event stream
const es = new EventSource('/api/v1/events/stream?token=...');
es.addEventListener('chat_message', (e) => {
const m = JSON.parse(e.data);
console.log(m.room_id, m.sender_node_id, m.message_type);
});
The event says that a message arrived, not what it says: fetch the text
from GET /api/v1/rooms/{room_id}/messages.
Mesh protocol IDs
Nodes talk to each other over MeshHold's own mesh layer. Every stream opens with a protocol ID, and the version is part of the ID — a node that does not serve one answers "unsupported" rather than guessing. What a node serves at this release:
| Protocol | Purpose |
|---|---|
/meshhold/hello/1.0 |
First exchange on a connection: name, capabilities, the addresses each side listens on, and the peers the answering side knows |
/meshhold/topo/2.0 |
Topology: each node's signed presence and record, flooded |
/meshhold/beat/1.0 |
The idle beat: one frame per peer carrying every subsystem's digest |
/meshhold/topic/1.0 |
The broadcast flood that carries the topics — /meshhold/chat/<room> and /meshhold/replication/<vault> — each message signed by its publisher |
/meshhold/punch/1.0 |
Coordinates a hole punch between two NAT'd nodes |
/meshhold/standby/2.0 |
The session between a phone in standby and the node holding it |
/meshhold/poll/1.0 |
The latest message of each of a phone's chat rooms, in one round trip |
/meshhold/block/1.0 |
Block fetch by hash |
/meshhold/block-reserve/1.0, /meshhold/block-put/1.0, /meshhold/replica-commit/1.0 |
Pushing a new file's blocks to a peer with free disk, and registering it as their holder |
/meshhold/catalog/2.0 |
Per-vault catalog sync: compare roots, then exchange entries |
/meshhold/repl-notify/2.0 |
A replication announcement pushed straight to a member a relay separates |
/meshhold/prepare/2.0 |
Asks a node to hash a file that an access-only vault lists without hashes |
/meshhold/offers/1.0 |
What a node has opened to the network, and handing over a key on request |
/meshhold/doc/2.0 |
A document's collaboration state, so two editors converge — only between nodes that prove they hold the vault's key |
/meshhold/filesend/1.0 |
One-shot, consent-gated file push between two nodes |
/meshhold/chat/sync/1.0 |
Chat catch-up: a room's tip, then the messages missing |
/meshhold/share-publish/2.0, /meshhold/share-block/1.0, /meshhold/share-status/1.0, /meshhold/share-unpublish/1.0 |
Putting a public share link's content on a gateway node |
/meshhold/meet-create/2.0, /meshhold/meet-delete/2.0 |
Putting a meeting on a gateway node |
/meshhold/call/1.0 |
Call control: invite, ringing, accept, hang up |
/meshhold/call-media/1.0 |
Call media frames, end-to-end encrypted |
/meshhold/dgflow/1.0 |
Sets up a direct datagram flow for real-time media |
/meshhold/tunnel/1.0, /meshhold/tunnel/1.1 |
Circuit control: hop by hop, and chained |
/meshhold/tunnel-data/1.0, /meshhold/tunnel-data/2.0 |
Circuit data substreams |
/meshhold/tunnel-carrier/1.0 |
A circuit's flows carried over the delivery layer |
/meshhold/link-datagram/1.0 |
The datagram plane on a connection with no native datagrams |
/meshhold/plugin/agent/1.0 |
HTTP/1.1 over the mesh to a remote AI agent instance |
MCP / Claude Code integration
MeshHold embeds Claude Code as a vault-analog. Each instance has its own encrypted state, syncs across nodes, and proxies over the mesh.
meshhold agent create --type claude --name "Claude 1"
meshhold agent workspace add <instance-id> ~/projects/foo
meshhold agent share <instance-id>
See the Claude rollout devlog for details, and the comparison with Paseo, Happy Coder, CloudCLI and VibeTunnel for how this surface lines up against the dedicated mobile agent clients.
OpenCode driver
The same agent surface drives OpenCode through its native HTTP/SSE serve mode. Pick the driver in the Add-Instance UI; provider OAuth flows are in Settings → Providers.
Gemini CLI driver
Google's Gemini CLI is driven over ACP (the Agent Client Protocol —
newline-delimited JSON-RPC 2.0 over stdio), spawned as gemini --acp. Every
instance is fully isolated: its own ~/.gemini (a per-instance HOME
override), its own long-running process, and its own API key stored in that
instance's ~/.gemini/.env — so several accounts coexist on one node.
Streaming updates and tool-call approvals (session/request_permission maps
to the same Allow / Deny cards as the other drivers) flow through the one
agent surface. Add a Gemini API key under Settings → Providers.
MCP for gemini is not yet wired. Gemini's ACP mode blocks session creation while an MCP server is configured, so the MCP tab is hidden for gemini pending an ACP-native fix. Claude, OpenCode and Codex support MCP normally.
Codex driver
OpenAI's Codex is driven over its app-server protocol — JSON-RPC 2.0 over
stdio, one long-lived process per instance. Each instance gets its own
CODEX_HOME, so its sign-in, its history and its configuration are its own
and several accounts coexist on one node. MeshHold never reads or writes the
~/.codex a person uses from their own terminal.
Sign-in happens in MeshHold's own UI: a ChatGPT account by device code (a link and a code, which can be finished from another device), or an API key. The Account panel says which methods this build offers and what the account can do.
Codex draws. Ask for a picture and the answer arrives as a card in the chat with the image in it, saved into the session rather than carried in the event stream, so it survives a reload, a daemon restart, and a second node reading the same session through the proxy. The picture tool needs a ChatGPT sign-in on a plan above Free — an API-key instance says so in the Account panel instead of offering a button that would fail.
MCP servers are configured from the MCP tab like the other drivers,
including remote servers over HTTP with an OAuth login. MeshHold's own tools —
vault search, chat_send, attach_file, the document tools —
are offered to a Codex thread on the same per-instance grants as everywhere
else.
Approvals map to the same Allow / Deny cards as the other drivers, and a Windows instance can say whether its sandbox is ready and offer to set it up.
All four drivers sit behind a single SessionBackend interface, so the
REST surface and the Web UI are identical regardless of which CLI backs an
instance.