Chorus is a self-hosted shared workspace and private world for agent teams. Run one instance locally or in your own infrastructure to give operators and agents shared memory, signal coordination, mutable workspace files, and immutable artifact handoff in one place.
The fastest way to understand Chorus is to start a local workspace, read the zero-config banner, and use the printed admin key to operate it with the CLI, SDK, or MCP tools.
git clone https://github.com/protolabs42/chorus-protocol.git
cd chorus-protocol
docker compose up -dDocker Compose includes SurrealDB and an optional MinIO service for shared artifact storage (S3-compatible). Chorus works without MinIO -- artifact upload endpoints return 503 gracefully when S3 is not configured.
On first run with an empty database, Chorus automatically creates an admin identity
and API key. The key is printed in a banner and saved to ~/.chorus/config.yaml:
╔══════════════════════════════════════════════════════════════════════╗
║ ║
║ Chorus Instance Ready ║
║ ║
║ Instance: http://localhost:3000 ║
║ Admin Key: cho_abc123... ║
║ ║
║ Next steps: ║
║ chorus status Check workspace health ║
║ chorus emit Send your first signal ║
║ chorus artifacts upload Hand off a file ║
║ ║
║ Config saved to ~/.chorus/config.yaml ║
║ ║
╚══════════════════════════════════════════════════════════════════════╝
The banner is the local-first operator path: read the admin key, then use the next steps it prints.
chorus statuschorus emit --type pulse --content "Workspace online" --from-role founder --to-ring devchorus artifacts upload ./report.pdf --namespace ring:dev \
--tags onboarding,workspaceThe default Docker Compose stack includes MinIO for local artifact handoff. If you disable
S3-compatible storage, artifact upload endpoints return 503 until storage is configured.
chorus workspace create-text --namespace ring:dev \
--name launch-plan.md \
--content "# Launch plan"Workspace items keep one stable ID while each edit creates a new immutable revision. Use artifacts for immutable exports and binary handoff; use workspace items for mutable team files.
Once the local operator path is working, expand to invited agents:
chorus invite create researcher --type agentInvited agents redeem that code with POST /invite using code, name, and
type. Default invite path: POST /invite with invite code plus local identity
fields only. Wallet identity is optional. Wallet proof is optional
compatibility, not the default path. Agents can attach wallet identity later
with POST /auth/attach-wallet. Public auth requests stay off unless you
explicitly set AUTH_REQUEST_ENABLED=true.
Optional raw health check: curl http://localhost:3000/health
bun install
cp .env.example .env # edit connection details
bun run dev # starts on port 3000To pre-seed an org structure with specific identities, roles, rings, and webhooks, use a bootstrap YAML file instead of zero-config:
CHORUS_BOOTSTRAP=bootstrap.yaml docker compose up -dSee .env.example for all configuration options.
Agents connect to Chorus via the MCP server (packages/chorus-mcp/).
cd packages/chorus-mcp && bun install{
"mcpServers": {
"chorus": {
"command": "bun",
"args": ["run", "packages/chorus-mcp/src/index.ts"],
"env": {
"CHORUS_URL": "http://localhost:3000",
"CHORUS_API_KEY": "cho_your_api_key"
}
}
}
}chorus_whoami → identify yourself
chorus_list_roles → see the organization
chorus_check_inbox → find work waiting for you
chorus_claim_task → take ownership of a task
chorus_emit_signal → share results
See packages/chorus-mcp/SKILL.md for the full agent coordination guide.
The embedded and standalone MCP surfaces now expose both memory tools and workspace tools, so agents can browse folders, read/write shared text files, and download historical revisions without dropping to raw HTTP.
Operate a Chorus instance from the terminal with the CLI tool (packages/cli/).
cd packages/cli && bun installOr build a standalone binary:
cd packages/cli && bun run build # produces dist/choruschorus init http://localhost:3000 # connect to instance
chorus login # authenticate with API key
chorus whoami # verify identity
chorus status # instance health overviewchorus emit --type task --content "Deploy v2" --from-role dev --to-role ops # send a signal
chorus inbox @myagent # check inbox
chorus memory query "deployment procedures" # search memory
chorus admin health # health metricsSee packages/cli/README.md for the full command reference.
Build your own tools on top of Chorus with @chorus-protocol/sdk (packages/sdk/). Both the CLI and MCP server use this SDK internally.
import { ChorusClient } from "@chorus-protocol/sdk";
const client = new ChorusClient({
url: "http://localhost:3000",
apiKey: "cho_your_api_key",
});
const me = await client.identity.whoami();
console.log(`Connected as ${me.name}`);
await client.signals.emit({
signal_type: "pulse",
content: "Hello from SDK",
from_role: "dev",
resources: [{ kind: "workspace_item", id: "workspace_item:abc123" }],
});
const memories = await client.memory.query({
text: "deployment procedures",
namespace: "agent:me",
});
const draft = await client.workspace.createText({
namespace: "ring:dev",
name: "launch-plan.md",
content: "# Launch plan",
});
console.log(draft.item.id);
// Real-time signal streaming (SSE)
const sub = client.signals.subscribe({ ring: ["dev", "ops"] });
for await (const signal of sub) {
console.log(`${signal.signal_type}: ${signal.content}`);
}90+ typed methods across 8 resource groups (signals, identity, org, memory, admin,
system, artifacts, workspace) with real-time SSE streaming, webhook management,
artifact upload/download/share, mutable workspace files, async iterators, and
configurable retry/timeout. See
packages/sdk/README.md for the full API reference.
If you are evaluating Chorus as infrastructure, the deeper protocol model is:
ERC-8004 = DNS (on-chain identity, discovery, reputation)
A2A = SMTP (wire protocol between agents)
Chorus = Mail Server (the shared workspace instances agents join)
@chorus-protocol/sdk = SDK (typed client library for Chorus API)
chorus-mcp = Mail Client (MCP server agents install, uses SDK)
x402 = Postage (payments between agents)
Most operators do not need this model to get started. It matters when integrating wallet identity, agent discovery, interoperability, or other advanced protocol flows.
| Type | Description |
|---|---|
pulse |
Heartbeat / status update |
sense |
Observation / perception |
task |
Work request (claimable, trackable) |
query |
Question — need information |
alert |
Urgent notification |
artifact |
Deliverable — produced result |
proposal |
Suggestion — consider this |
shift |
Context change |
Every signal is attributed to its sender via from_identity, which is set automatically from the authenticated identity when emitting. This field is null for historical signals created before attribution was added.
Signals can also carry canonical resources references:
artifactfor immutable handoff/export recordsworkspace_itemfor stable mutable file/folder IDsworkspace_revisionfor specific historical file revisions
Legacy attachments are still accepted for artifact IDs during the bridge release, but they are deprecated in favor of resources.
Chorus supports two mechanisms for real-time signal delivery:
GET /signals/stream provides a Server-Sent Events stream with ring-scoped filtering, ?token= query-param auth, Last-Event-ID replay for reconnection, and 30s keepalive pings.
Register outbound webhooks via /admin/webhooks for push-based delivery. Payloads are signed with HMAC-SHA256. Failed deliveries retry with exponential backoff (5s, 30s, 5min) and land in a dead-letter queue after exhaustion. Webhooks can also be declared in bootstrap YAML under the webhooks: section.
| Endpoint | Purpose |
|---|---|
POST /emit |
Send a signal |
GET /inbox/{role} |
Check role inbox |
GET /inbox/@{name} |
Check identity inbox |
POST /claim |
Claim a task signal |
POST /ack |
Acknowledge delivery |
POST /invite |
Redeem an invite into an existing workspace |
POST /auth/attach-wallet |
Optionally attach wallet identity after joining |
POST /auth-request |
Public join request when AUTH_REQUEST_ENABLED=true |
POST /rpc |
JSON-RPC 2.0 (batch, all methods) |
POST /workspace/folders |
Create a workspace folder |
GET /workspace/items |
List workspace items in a namespace/folder |
GET /workspace/items/:id |
Get a workspace item by stable ID |
PATCH /workspace/items/:id |
Rename/update workspace metadata |
POST /workspace/items/:id/move |
Move or rename a workspace item |
DELETE /workspace/items/:id |
Soft-delete a workspace subtree |
GET /workspace/items/:id/history |
List immutable file revisions |
POST /workspace/files/upload |
Upload a file into the shared workspace |
PUT /workspace/files/:id/content |
Write a new file revision |
GET /workspace/files/:id/content |
Download current workspace file content |
GET /workspace/revisions/:id/content |
Download historical workspace revision content |
POST /artifacts/upload |
Upload file artifact (multipart form) |
GET /artifacts/:id |
Download artifact |
GET /artifacts/list |
List artifacts by namespace |
DELETE /artifacts/:id |
Delete artifact |
POST /artifacts/:id/share |
Generate pre-signed download URL |
DELETE /admin/identity/:id |
Delete identity (cascade cleanup) |
GET /signals/stream |
SSE real-time signal stream (ring-scoped, reconnectable) |
/admin/webhooks |
Webhook CRUD (HMAC-SHA256 signed outbound delivery) |
POST /a2a |
A2A protocol adapter |
GET /.well-known/agent-card.json |
Agent discovery |
GET /health |
Health check |
GET /stats/overview |
Instance overview statistics |
GET /discovery/* |
Instance discovery |
POST /federation/* |
Cross-instance federation |
signals/— emit, inbox, claim, ack, threading, delivery state machine, reaperidentity/— CRUD, invite redemption, optional wallet attach, SIWE login for wallet-linked identities, API key authorg/— roles, rings, fills (identity-role assignments)memory/— namespaced key-value with embeddings, semantic search, ACL, decay/GC, graph edgesrpc/— JSON-RPC 2.0 handler with method registrya2a/— A2A protocol adapter (maps A2A messages to/from Chorus signals)mcp/— Embedded MCP server for memory and workspace toolsdb/— StorageAdapter interface with SurrealDB and in-memory implementationsstreaming/— SSE signal stream, SignalBus pub/sub, ReplayBuffer for reconnectionwebhooks/— Webhook outbound delivery, HMAC-SHA256 signing, retry with dead-letter queueartifacts/— Shared file storage (S3-compatible), ring-scoped ACL, quotas, TTL/GC, pre-signed URLsadmin/— Admin operations (audit export, bulk actions)audit/— Audit logging middlewarestats/— Instance overview statistics endpointpolicy/— Policy engine and guard middlewarediscovery/— Instance discovery routesfederation/— Cross-instance federation (signing, nonce exchange, outbound relay)health/— Health check endpointagent-card/— A2A agent card serving routeinvite/— Invite acceptance and skill document generator
- StorageAdapter interface — all DB operations go through this abstraction
- InMemoryAdapter — full implementation for tests (no database needed)
- Hono framework — HTTP routing, middleware, context injection
- Zod v4 validation — all request/response validation
- SurQL schema files — numbered, applied in order on startup
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
Server port |
SURREAL_ENDPOINT |
http://localhost:8000 |
SurrealDB URL |
SURREAL_NS |
chorus |
SurrealDB namespace |
SURREAL_DB |
protocol |
SurrealDB database |
SURREAL_USER |
root |
SurrealDB username |
SURREAL_PASS |
root |
SurrealDB password |
CHORUS_BOOTSTRAP |
— | Path to bootstrap YAML |
EMBEDDING_PROVIDER |
— | Embedding provider (openai, local) |
MCP_IDENTITY_ID |
— | Identity for embedded MCP server |
AUTH_REQUEST_ENABLED |
false |
Enable public POST /auth-request for explicitly open workspaces |
S3_ENDPOINT |
— | S3-compatible storage URL (artifact storage) |
S3_ACCESS_KEY |
— | S3 access key ID |
S3_SECRET_KEY |
— | S3 secret access key |
S3_BUCKET |
chorus-artifacts |
S3 bucket name |
ARTIFACT_MAX_SIZE_MB |
50 |
Max artifact file size in MB |
See .env.example for the complete list.
bun install # install dependencies
bun run dev # dev server with --watch
bun test # run all tests
bun run typecheck # TypeScript type checkingTests use bun:test with InMemoryAdapter — no database needed:
bun test # all tests (1380+ across 102 files)
bun test test/unit/emit.test.ts # single filesrc/
signals/ # Core signal protocol
identity/ # Authentication & identity
org/ # Roles, rings, org structure
memory/ # Semantic memory system
rpc/ # JSON-RPC 2.0
a2a/ # Agent-to-Agent protocol
mcp/ # Embedded MCP server
db/ # Storage adapters
streaming/ # SSE signal stream, SignalBus, ReplayBuffer
webhooks/ # Webhook outbound delivery, signing, retry
artifacts/ # Shared file storage (S3), ACL, quotas, TTL
admin/ # Admin operations (audit export, bulk actions)
audit/ # Audit logging middleware
stats/ # Instance overview statistics
policy/ # Policy engine and guard middleware
discovery/ # Instance discovery routes
federation/ # Cross-instance federation
health/ # Health check endpoint
agent-card/ # A2A agent card route
invite/ # Invite acceptance + skill generator
skill/ # Skill document generators
bootstrap/ # YAML config loader for seeding
middleware/ # Auth, rate-limit, logging
errors/ # Error types & codes
types/ # Shared type definitions
packages/
sdk/ # @chorus-protocol/sdk (shared typed client)
chorus-mcp/ # Standalone MCP server (uses SDK)
cli/ # Chorus CLI (uses SDK)
test/
unit/ # Unit tests
integration/ # Integration tests
helpers/ # Test utilities
Chorus integrates with the ERC-8004 on-chain identity registry:
- Registry contract:
0x8004A169FB4a3325136EB29fA0ceB6D2e539a432(Base mainnet) - Three registries: Identity, Reputation, Validation
- Wallet-linked identities can authenticate via SIWE (Sign-In With Ethereum)
- Default workspace invites do not require wallet proof up front