stonemux Documentation
Multi-agent coordination without tmux. Inboxes, doorbell webhooks, task routing, broadcast, shared state, and real-time event streaming. Your agents talk to each other — asynchronously, reliably, auditably.
1. Overview
stonemux is a compiled Rust binary that runs as a local MCP server providing multi-agent coordination primitives. It replaces ad-hoc tmux-based agent communication with structured messaging, task lifecycle management, shared state with optimistic concurrency, and real-time event streaming.
Key Capabilities
- Per-agent inboxes — Every registered agent gets a persistent inbox. Messages are stored, ordered, and retrievable via polling or Server-Sent Events.
- Doorbell webhooks — Instant notification when a message arrives. No polling overhead. Configurable per-agent webhook URL.
- Long-poll fallback — For environments where webhooks aren't possible, agents can long-poll their inbox with configurable timeout.
- Task lifecycle — Post tasks with descriptions, priorities, and capability requirements. Agents claim, execute, and complete tasks atomically.
- Broadcast channel — Fleet-wide announcements that reach all registered agents simultaneously.
- Shared state — Key-value store with versioned updates and optimistic concurrency control. Watch for changes in real time.
- Agent directory — Discover agents by role, group, capability, or status. Heartbeat monitoring detects stale agents.
- Server-Sent Events (SSE) — Real-time event stream for messages, task updates, state changes, and agent registrations.
- Channel isolation — Organize agents into channels for namespace isolation. Messages, tasks, and state are scoped per channel.
Architecture
stonemux runs as a single-process HTTP server on port 3392 (configurable). All coordination state is stored in a local SQLite database with WAL mode. Agents connect via HTTP REST API or MCP transport. No external dependencies, no cloud services, no data leaving your machine.
Anti-proxy protection: stonemux Pro/Enterprise includes agent token authentication. Each agent must prove ownership of a valid license key to obtain a 24-hour TTL token, required on every API call. This prevents a single license from being shared across unauthorized proxies.
2. Installation
macOS (Homebrew)
brew install keystoneproject/tap/stonemux
macOS (Direct Download)
# Apple Silicon (M1/M2/M3/M4)
curl -L https://keystoneproject.dev/releases/stonemux/darwin-aarch64/stonemux-v1.1.0-darwin-aarch64.tar.gz | tar xz
sudo mv stonemux /usr/local/bin/
# Intel
curl -L https://keystoneproject.dev/releases/stonemux/darwin-x86_64/stonemux-v1.1.0-darwin-x86_64.tar.gz | tar xz
sudo mv stonemux /usr/local/bin/
Linux
# x86_64
curl -L https://keystoneproject.dev/releases/stonemux/linux-x86_64/stonemux-v1.1.0-linux-x86_64.tar.gz | tar xz
sudo mv stonemux /usr/local/bin/
# ARM64 (aarch64)
curl -L https://keystoneproject.dev/releases/stonemux/linux-aarch64/stonemux-v1.1.0-linux-aarch64.tar.gz | tar xz
sudo mv stonemux /usr/local/bin/
3. Quick Start
Step 1: Activate
stonemux activate --key SX-XXXX-XXXX-XXXX-XXXX
stonemux status
Step 2: Start
stonemux serve --port 3392
Step 3: Register agents and send messages
# Register two agents
curl -X POST http://localhost:3392/agent/register \
-H "Content-Type: application/json" \
-d '{"agent_id": "researcher", "role": "research", "capabilities": ["web-search", "analysis"], "channel": "default"}'
curl -X POST http://localhost:3392/agent/register \
-H "Content-Type: application/json" \
-d '{"agent_id": "writer", "role": "writing", "capabilities": ["content-creation"], "channel": "default"}'
# Send a message from researcher to writer
curl -X POST http://localhost:3392/msg/send \
-H "Content-Type: application/json" \
-d '{"from": "researcher", "to": "writer", "content": "Analysis complete. Top 3 competitors identified. Ready for report draft."}'
# Writer polls for messages
curl "http://localhost:3392/msg/poll?agent_id=writer&timeout=5"
Step 4: Post and claim a task
# Post a task
curl -X POST http://localhost:3392/task/post \
-H "Content-Type: application/json" \
-d '{"description": "Draft competitive analysis report", "posted_by": "researcher", "required_capability": "content-creation", "priority": 5}'
# Writer claims the next available task matching their capabilities
curl -X POST http://localhost:3392/task/claim \
-H "Content-Type: application/json" \
-d '{"agent_id": "writer"}'
# Writer completes the task
curl -X POST http://localhost:3392/task/complete \
-H "Content-Type: application/json" \
-d '{"task_id": "task-uuid-here", "agent_id": "writer", "result": "Report drafted. 3 pages, executive summary included."}'
4. Configuration
[server]
host = "127.0.0.1"
port = 3392
data_dir = "~/.stonemux"
[license]
key_file = "~/.stonemux/license.key"
license_server = "https://license.keystoneproject.dev"
[coordination]
max_message_size = 65536 # Maximum message content size (bytes)
poll_timeout_max = 120 # Maximum long-poll timeout (seconds)
task_claim_timeout = 300 # Seconds before unclaimed task is re-available
heartbeat_interval = 60 # Agent heartbeat interval (seconds)
stale_threshold = 300 # Seconds without heartbeat before agent marked stale
[events]
sse_keepalive = 15 # SSE keepalive interval (seconds)
max_event_buffer = 1000 # Maximum buffered events per stream
[logging]
level = "info"
format = "json"
5. API Reference
Agent Management
Register an agent with the coordination server. Required before sending/receiving messages or claiming tasks.
Request Body
{
"agent_id": "string", // Required. Unique identifier.
"role": "string", // Required. Agent's role (e.g., "researcher", "writer").
"capabilities": ["string"], // Optional. Capabilities for task matching.
"group": "string", // Optional. Agent group for broadcast filtering.
"channel": "string" // Optional. Channel. Default: "default".
}
Tier Limits
- Free: 2 agents maximum
- Pro: Unlimited agents
- Enterprise: Unlimited agents + audit logging
Remove an agent from the directory. Pending messages are preserved.
{ "agent_id": "string" }
Send a heartbeat to keep the agent marked as active.
{ "agent_id": "string" }
Discover agents by role, group, status, or channel. Supports filtering stale agents.
Query Parameters
?role=researcher // Filter by role
&group=analysis-team // Filter by group
&status=active // Filter by status (active/stale)
&channel=default // Filter by channel
&include_stale=false // Include stale agents (default: false)
Response
{
"agents": [
{
"id": "researcher",
"role": "research",
"capabilities": ["web-search", "analysis"],
"group": "analysis-team",
"channel": "default",
"status": "active",
"registered_at": "2026-06-10T02:00:00Z",
"last_heartbeat": "2026-06-10T04:58:00Z"
}
]
}
Messaging
Send a message to a specific agent's inbox. Triggers doorbell webhook if configured.
Request Body
{
"from": "string", // Required. Sender agent ID.
"to": "string", // Required. Recipient agent ID.
"content": "string", // Required. Message content.
"channel": "string" // Optional. Default: "default".
}
Broadcast a message to all agents, optionally filtered by group.
Request Body
{
"from": "string", // Required. Sender agent ID.
"content": "string", // Required. Broadcast content.
"group": "string", // Optional. Target group only.
"channel": "string" // Optional. Default: "default".
}
Pro/Enterprise only. Broadcast requires a Pro or Enterprise license.
Long-poll for new messages. Blocks until a message arrives or timeout expires.
Query Parameters
?agent_id=writer // Required. Agent to poll for.
&timeout=30 // Optional. Timeout in seconds (default: 30, max: 120).
Response
{
"messages": [
{
"id": 42,
"from": "researcher",
"to": "writer",
"content": "Analysis complete...",
"channel": "default",
"sent_at": "2026-06-10T04:30:00Z"
}
]
}
Task Coordination
Post a task to the coordination queue. Tasks can require specific capabilities and carry priority.
Request Body
{
"description": "string", // Required. Task description.
"posted_by": "string", // Required. Agent posting the task.
"required_capability": "string",// Optional. Capability required to claim.
"priority": 5, // Optional. Higher = more urgent (default: 0).
"channel": "string", // Optional. Default: "default".
"metadata": {} // Optional. Arbitrary JSON metadata.
}
Claim the highest-priority unclaimed task matching the agent's capabilities. Atomic — only one agent can claim each task.
{
"agent_id": "string", // Required. Claiming agent.
"channel": "string" // Optional. Default: "default".
}
Mark a claimed task as complete with a result.
{
"task_id": "string", // Required. Task UUID.
"agent_id": "string", // Required. Must be the agent that claimed it.
"result": "string", // Optional. Completion result/summary.
"status": "string" // Optional. Default: "complete". Can be "failed".
}
List tasks, optionally filtered by status and channel.
?status=pending // Filter: pending, claimed, complete, failed
&channel=default
Shared State
Get a value from shared state.
?key=deployment-status&channel=ops
Response
{
"key": "deployment-status",
"value": "in-progress",
"version": 3,
"updated_by": "deploy-agent",
"updated_at": "2026-06-10T04:45:00Z"
}
Set a value in shared state. Supports optimistic concurrency via expected_version.
{
"key": "string", // Required.
"value": "string", // Required.
"channel": "string", // Optional. Default: "default".
"expected_version": 3, // Optional. Fails if current version doesn't match (CAS).
"updated_by": "string" // Optional. Agent making the update.
}
Optimistic concurrency: Set expected_version to the version you read. If another agent updated the key since then, the write fails with a 409 Conflict — read the new value and retry.
Long-poll for a state change. Returns when the key is updated past a given version, or on timeout.
?key=deployment-status&channel=ops&after_version=3&timeout=30
Events & Monitoring
Server-Sent Events (SSE) stream. Emits events for messages, task updates, state changes, and agent registrations.
Headers
Accept: text/event-stream
Last-Event-ID: 42 // Optional. Resume from event ID.
Event Types
event: message
data: {"from": "researcher", "to": "writer", "content": "..."}
event: task_posted
data: {"task_id": "uuid", "description": "...", "posted_by": "..."}
event: task_claimed
data: {"task_id": "uuid", "agent_id": "writer"}
event: state_changed
data: {"key": "deployment-status", "value": "complete", "version": 4}
event: agent_registered
data: {"agent_id": "new-agent", "role": "analysis"}
Pro/Enterprise only. SSE event streaming requires a Pro or Enterprise license. Free tier agents must use long-poll.
Create a named channel for namespace isolation.
{ "name": "string", "created_by": "string" }
List all channels.
Server statistics — agent counts, message counts, task counts.
Health check. Returns status, version, tier, uptime.
6. MCP Tool Reference
When connected as an MCP server, stonemux exposes coordination tools to your agent:
mux_send
Tool: mux_send
Parameters:
- from (string, required): Sender agent ID
- to (string, required): Recipient agent ID
- content (string, required): Message content
- channel (string, optional): Channel. Default: "default"
mux_broadcast
Tool: mux_broadcast (Pro/Enterprise)
Parameters:
- from (string, required): Sender
- content (string, required): Broadcast content
- group (string, optional): Target group filter
mux_poll
Tool: mux_poll
Parameters:
- agent_id (string, required): Agent to check inbox
- timeout (integer, optional): Seconds to wait (default 30)
mux_task_post
Tool: mux_task_post (Pro/Enterprise)
Parameters:
- description (string, required): Task description
- posted_by (string, required): Poster
- required_capability (string, optional): Skill match
- priority (integer, optional): Default 0
- metadata (object, optional): JSON payload
mux_task_claim
Tool: mux_task_claim (Pro/Enterprise)
Parameters:
- agent_id (string, required): Claiming agent
mux_task_complete
Tool: mux_task_complete (Pro/Enterprise)
Parameters:
- task_id (string, required): Task UUID
- agent_id (string, required): Completing agent
- result (string, optional): Completion result
mux_state_get / mux_state_set
Tool: mux_state_get
Parameters:
- key (string, required)
- channel (string, optional)
Tool: mux_state_set
Parameters:
- key (string, required)
- value (string, required)
- expected_version (integer, optional): Optimistic concurrency
- updated_by (string, optional)
mux_discover
Tool: mux_discover
Parameters:
- role (string, optional): Filter by role
- group (string, optional): Filter by group
- status (string, optional): "active" or "stale"
7. Use Cases by Platform
Structured Crew Communication
Replace CrewAI's built-in delegation with stonemux for persistent, auditable inter-agent messaging with priority routing.
# Manager agent posts a research task
mux_task_post({
description: "Research Q2 earnings for top 5 tech companies",
posted_by: "manager",
required_capability: "financial-research",
priority: 8,
metadata: { deadline: "2026-06-15", format: "markdown" }
})
# Researcher agent claims and works
mux_task_claim({ agent_id: "researcher" })
# ... does research ...
mux_task_complete({
task_id: "task-uuid",
agent_id: "researcher",
result: "Report complete. Key findings: ..."
})
# Manager gets notified via SSE events or polling
mux_poll({ agent_id: "manager", timeout: 60 })
Result: Task handoffs are atomic, priority-ordered, and queryable. No lost messages, no race conditions.
Cross-Graph State Sharing
Multiple LangGraph workflows share state through stonemux's versioned key-value store with optimistic concurrency.
# Graph A updates shared state
mux_state_set({
key: "pipeline-stage",
value: "extraction-complete",
updated_by: "graph-a",
expected_version: 2
})
# Graph B watches for state changes
mux_state_watch({
key: "pipeline-stage",
after_version: 2,
timeout: 60
})
# Returns as soon as Graph A updates
Result: Graphs coordinate without tight coupling. State changes are versioned — no lost updates.
Multi-Agent Code Review Pipeline
Three OpenHands agents — a Coder, a Reviewer, and a Tester — coordinate through stonemux.
# Coder finishes implementation
mux_send({
from: "coder",
to: "reviewer",
content: "PR #42 ready for review. Changes: auth module refactor, 340 LOC."
})
# Reviewer approves and notifies tester
mux_send({
from: "reviewer",
to: "tester",
content: "PR #42 approved. Run integration tests on auth module."
})
# Tester broadcasts results to all
mux_broadcast({
from: "tester",
content: "PR #42: 47/47 tests pass. Ready to merge."
})
Estate Agent Coordination
Multiple Claude Code instances running as specialized agents coordinate through stonemux instead of tmux send-keys.
# Prime (orchestrator) assigns work to build agent
mux_task_post({
description: "Build stonemem v1.1.0 release binaries for all platforms",
posted_by: "prime",
required_capability: "rust-build",
priority: 10,
metadata: { platforms: ["darwin-aarch64", "linux-x86_64", "linux-aarch64"] }
})
# Build agent claims, builds, reports back
mux_task_claim({ agent_id: "build-agent" })
// ... builds ...
mux_task_complete({
task_id: "...",
agent_id: "build-agent",
result: "All 3 platforms built. Binaries at /opt/releases/"
})
# Prime monitors via events stream for real-time updates
Service Mesh Agent Communication
Google ADK agents running across microservices use stonemux channels for service-scoped coordination.
# Create per-service channels
mux_channel_create({ name: "auth-service", created_by: "orchestrator" })
mux_channel_create({ name: "payment-service", created_by: "orchestrator" })
# Auth agent posts to its channel
mux_send({
from: "auth-monitor",
to: "payment-agent",
channel: "payment-service",
content: "Token refresh rate spiking. Possible credential leak. Check payment webhooks."
})
Pipeline Stage Coordination
Haystack pipeline stages running as separate agents hand off work through stonemux tasks.
# Retriever agent posts results as a task for the Reader
mux_task_post({
description: "Read and summarize retrieved documents",
posted_by: "retriever",
required_capability: "summarization",
metadata: { documents: ["doc-1", "doc-2", "doc-3"], query: "Q2 revenue forecast" }
})
Enterprise Fleet Orchestration
Hundreds of agents across an enterprise coordinate through stonemux with group-based broadcasting and capability-matched task routing.
# HR group broadcast
mux_broadcast({
from: "hr-lead",
group: "hr-agents",
content: "New hire onboarding batch: 15 employees starting Monday. Process initiation required."
})
# IT agent discovers available HR agents
mux_discover({ role: "hr-processor", status: "active" })
// Returns 3 active HR processor agents
# Post individual onboarding tasks — agents self-select based on capabilities
mux_task_post({
description: "Process onboarding for Employee #E-2026-0891",
posted_by: "hr-lead",
required_capability: "onboarding",
priority: 5,
metadata: { employee_id: "E-2026-0891", department: "Engineering" }
})
8. Tier Comparison
| Feature | Free | Pro ($14/mo) | Enterprise ($59/mo) |
|---|---|---|---|
| Agents | 2 | Unlimited | Unlimited |
| Inbox send/receive | Yes | Yes | Yes |
| Long-poll | Yes | Yes | Yes |
| Doorbell webhooks | No | Yes | Yes |
| Broadcast channel | No | Yes | Yes |
| Task coordination | No | Yes | Yes |
| Priority routing | No | Yes | Yes |
| SSE event streaming | No | Yes | Yes |
| Shared state (KV) | No | Yes | Yes |
| REST API access | No | No | Yes |
| Webhook forwarding | No | No | Yes |
| Audit log export | No | No | Yes |
| Agent token auth | No | Yes | Yes |
9. License Management
Activation
stonemux activate --key SX-XXXX-XXXX-XXXX-XXXX
stonemux status
Agent Token Authentication (Pro/Enterprise)
To prevent license sharing, stonemux Pro/Enterprise requires agents to authenticate with a token:
# 1. Obtain a token (proves license ownership via HMAC)
curl -X POST http://localhost:3392/token/obtain \
-H "Content-Type: application/json" \
-d '{"license_key": "SX-XXXX-XXXX-XXXX-XXXX"}'
# Returns: { "token": "eyJ...", "expires_at": "2026-06-11T04:00:00Z" }
# 2. Include token in all API calls
curl -X POST http://localhost:3392/msg/send \
-H "Authorization: Bearer eyJ..." \
-H "Content-Type: application/json" \
-d '{"from": "agent-1", "to": "agent-2", "content": "Hello"}'
Tokens have a 24-hour TTL and must be refreshed. This prevents a single license from being proxied to unauthorized users.
Heartbeat & SIGIL
Same as stonemem — daily heartbeat (telemetry only, doesn't disable), SIGIL die command for key revocation.
10. Troubleshooting
Agent registration fails (402)
Free tier is limited to 2 agents. Deregister an unused agent or upgrade to Pro.
Messages not arriving
- Verify both agents are registered:
GET /agent/discover - Check the recipient's
agent_idmatches exactly (case-sensitive) - Verify agents are in the same channel
Task claim returns empty
- Check for pending tasks:
GET /task/list?status=pending - Verify the claiming agent's capabilities match the task's
required_capability - Check if another agent already claimed it
State write conflict (409)
Another agent updated the key since you read it. Read the new version and retry:
// Read current state
let state = mux_state_get({ key: "my-key" })
// Retry set with new version
mux_state_set({ key: "my-key", value: "new", expected_version: state.version })
Getting Help
- Email: [email protected]
- GitHub: github.com/keystoneproject/stonemux/issues