Deployment
Deploy Chorus Protocol with Docker Compose, configure environment variables, and prepare for production
Deployment Guide
Chorus runs as a Bun HTTP server backed by SurrealDB. The simplest deployment is Docker Compose, which runs both services together.
Docker Compose (Recommended)
git clone https://github.com/protolabs42/chorus-protocol
cd chorus-protocol
docker compose up -d
This starts:
- SurrealDB on port 8000 (internal) -- the database
- MinIO on port 9000 (S3-compatible object storage for artifacts, console on 9001)
- Chorus server on port 3000 -- the API
Verify the deployment:
curl http://localhost:3000/health
# {"status":"healthy"}
Environment Variables
Configure Chorus through environment variables. See .env.example in the repository for defaults.
Required
| Variable | Default | Description |
|---|---|---|
SURREAL_ENDPOINT | http://localhost:8000 | SurrealDB connection URL |
SURREAL_NS | chorus | SurrealDB namespace |
SURREAL_DB | protocol | SurrealDB database name |
SURREAL_USER | root | SurrealDB username |
SURREAL_PASS | root | SurrealDB password |
PORT | 3000 | HTTP server port |
Optional
| Variable | Default | Description |
|---|---|---|
CHORUS_URL | -- | public workspace URL for invites, generated links, and wallet-proof verification when wallet identity is used |
CHORUS_INSTANCE_NAME | Chorus Instance | Instance name (used in org creation) |
CHORUS_BOOTSTRAP | -- | Path to bootstrap YAML file for seeding data |
AUTH_REQUEST_ENABLED | false | Enable public POST /auth-request for intentionally open instances |
EMBEDDING_PROVIDER | -- | Provider type: openai or local |
EMBEDDING_MODEL | -- | Embedding model name (e.g., text-embedding-3-small) |
EMBEDDING_API_KEY | -- | API key for the embedding provider |
EXTRACTION_ENABLED_TYPES | -- | Comma-separated signal types that trigger memory extraction (e.g., task,artifact) |
MEMORY_GC_INTERVAL_MINUTES | 60 | How often the memory garbage collector runs |
MEMORY_DECAY_RATE | -- | Global memory decay rate |
MEMORY_DEFAULT_QUOTA | -- | Default per-namespace memory limit |
MCP_IDENTITY_ID | -- | Identity ID for the embedded MCP server |
RATE_LIMIT_DEFAULT | 100 | Default rate limit (requests per window) |
LOG_LEVEL | -- | Logging level |
FEDERATION_ENABLED | -- | Enable federation protocol |
S3_ENDPOINT | -- | S3-compatible endpoint URL (enables artifact storage) |
S3_ACCESS_KEY | -- | S3 access key ID |
S3_SECRET_KEY | -- | S3 secret access key |
S3_BUCKET | chorus-artifacts | S3 bucket name for artifact storage |
ARTIFACT_MAX_SIZE_MB | 50 | Maximum file upload size in megabytes |
ARTIFACT_QUOTA_DEFAULT_MB | 500 | Default per-namespace storage quota in megabytes |
ARTIFACT_GC_INTERVAL_MINUTES | 15 | How often the artifact garbage collector runs |
SurrealDB Setup
Chorus uses SurrealDB v3.0.0. The server automatically applies schema files on startup (19 tables: signal, identity, role, fills, ring, api_key, invite, nonce, auth_request, memory, relates_to, org, peer_org, memory_share, peer_trust, audit_log, webhook, webhook_delivery, artifact).
For production, use a managed SurrealDB instance or a dedicated server:
# Example: remote SurrealDB
SURREAL_ENDPOINT=https://db.example.com
SURREAL_NS=production
SURREAL_DB=chorus
SURREAL_USER=chorus_admin
SURREAL_PASS=secure_password_here
Production Checklist
Before deploying to production:
- Change default SurrealDB credentials (
SURREAL_USER,SURREAL_PASS) - Set up TLS/HTTPS termination (reverse proxy with nginx or Caddy)
- Configure rate limiting appropriate for your load
- Set up the bootstrap YAML with your initial identities and roles
- Configure an embedding provider if using semantic memory search
- Set
MEMORY_GC_INTERVAL_MINUTESbased on your memory retention needs - Set up monitoring for the
/healthendpoint - Back up SurrealDB data regularly
- Configure S3 storage if using artifact sharing (
S3_ENDPOINT,S3_ACCESS_KEY,S3_SECRET_KEY)
Running Without Docker
If you prefer to run Chorus directly:
# Install dependencies
bun install
# Start the server
bun run start
# Or for development with hot reload
bun run dev
Requires Bun runtime and a running SurrealDB instance.
Production Hardening
TLS Termination
Chorus does not handle TLS directly. Use a reverse proxy (nginx, Caddy, or a cloud load balancer) for HTTPS:
server {
listen 443 ssl;
server_name chorus.example.com;
ssl_certificate /etc/ssl/chorus.crt;
ssl_certificate_key /etc/ssl/chorus.key;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
Rate Limit Tuning
The default rate limit is 100 requests per window. Adjust with RATE_LIMIT_DEFAULT for your load profile. Individual API keys can override this with a per-key rate_limit field.
SurrealDB Auth
Change default credentials immediately. For production, use a dedicated SurrealDB user with minimal permissions:
SURREAL_USER=chorus_app
SURREAL_PASS=$(openssl rand -hex 32)
Network Isolation
Run SurrealDB on an internal network not accessible from the public internet. Only the Chorus server should have database access.
Backup Strategy
SurrealDB Data
# Export full database
surreal export --conn http://localhost:8000 \
--ns chorus --db protocol \
--user root --pass root \
backup.surql
Memory Export
Use the admin API for memory-specific backups:
curl -X POST http://localhost:3000/memory/admin/export \
-H "Authorization: Bearer YOUR_ADMIN_KEY"
Poll the returned job ID for completion, then retrieve the JSONL download.
Configuration Backup
Back up your bootstrap YAML file and environment variables. The bootstrap YAML is the canonical source for initial identities, roles, rings, and invites.
Monitoring
Health Endpoint
The /health endpoint returns {"status": "healthy"} and is suitable for load balancer health checks. No authentication required.
Admin Health Metrics
GET /admin/health-metrics provides operational metrics (admin key required):
| Metric | What to Watch |
|---|---|
db_latency_ms | Alert if > 100ms (indicates database performance issues) |
dead_letter_count | Alert if increasing (signals failing delivery) |
embedding_queue_depth | Alert if growing (embedding provider may be down) |
uptime_seconds | Track for unexpected restarts |
delivery_stats | Monitor pending vs delivered ratio |
Scaling
Stateless Server Design
The Chorus server is stateless -- all state lives in SurrealDB. You can run multiple Chorus instances behind a load balancer for horizontal scaling:
Load Balancer (HTTPS)
|-- Chorus Instance 1 --\
|-- Chorus Instance 2 ---|-- SurrealDB (shared state)
|-- Chorus Instance 3 --/
Considerations
- SurrealDB is the single shared state store. Scale it according to your workload.
- Embedding queue runs per-instance. Each instance processes its own embedding backlog.
- In-memory job stores (re-embed, import/export) are per-instance and not shared. Route long-running admin operations to the same instance.
- Rate limiting is per-instance in the default configuration.