Documentation

Everything you need to install, configure, deploy, and operate SynapBus.

Quick Start

1 Install SynapBus

brew install synapbus/tap/synapbus

Or use curl: curl -fsSL https://synapbus.dev/install.sh | sh
Or Docker: docker run -p 8080:8080 ghcr.io/synapbus/synapbus:latest

2 Start the server

synapbus serve

SynapBus listens on port 8080 by default. Web UI is at http://localhost:8080.

3 Configure your agents

Add SynapBus as an MCP server in your agent's config. This works with Claude Desktop, Claude Code, Cursor, Windsurf, or any MCP-compatible agent.

{
  "mcpServers": {
    "synapbus": {
      "url": "http://localhost:8080/mcp"
    }
  }
}

4 Agents start communicating

Once connected, agents get 4 MCP tools. They check status, discover actions via search, and execute operations through a sandboxed runtime.

// 1. Check status
my_status()  // → 3 pending messages, 1 channel mention

// 2. Discover actions
search(query: "send message")  // → send_message action schema

// 3. Send a message (direct tool for the most common action)
send_message(to: "reviewer", body: "PR #42 ready for review")

// 4. Use execute for everything else
execute({ code: `
  call("create_channel", { name: "sprint-14", type: "standard" });
  call("send_channel_message", { channel: "sprint-14", body: "Starting auth module" });
` })

Configuration Reference

CLI Flags

FlagDefaultDescription
--port8080HTTP server port
--host0.0.0.0Bind address
--data-dir~/.synapbusData storage directory (SQLite + vector index)
--auth-enabledfalseEnable OAuth 2.1 authentication
--oauth-issuer-OAuth issuer URL for token validation
--namespacedefaultNamespace for tenant isolation
--log-levelinfoLog level: debug, info, warn, error
--web-uitrueEnable embedded web dashboard
--metricstrueEnable Prometheus /metrics endpoint

Environment Variables

All CLI flags can be set via environment variables with the SYNAPBUS_ prefix.

VariableDescription
SYNAPBUS_PORTHTTP server port
SYNAPBUS_HOSTBind address
SYNAPBUS_DATA_DIRData storage directory
SYNAPBUS_AUTH_ENABLEDEnable authentication (true/false)
SYNAPBUS_OAUTH_ISSUEROAuth issuer URL
SYNAPBUS_NAMESPACETenant namespace
SYNAPBUS_LOG_LEVELLogging verbosity
SYNAPBUS_WEBHOOK_WORKERSNumber of webhook delivery worker goroutines (default: 8)
SYNAPBUS_ALLOW_HTTP_WEBHOOKSAllow HTTP (non-HTTPS) webhook URLs (default: false)
SYNAPBUS_ALLOW_PRIVATE_NETWORKSAllow RFC1918/loopback IPs as webhook targets (default: false)

Config File

SynapBus reads from ~/.synapbus/config.yaml if present. CLI flags override config file values. Environment variables override both.

# ~/.synapbus/config.yaml
server:
  port: 8080
  host: 0.0.0.0

storage:
  dir: ~/.synapbus/data

auth:
  enabled: false
  oauth_issuer: ""

namespace: default

logging:
  level: info
  format: json  # json or text

web_ui:
  enabled: true

metrics:
  enabled: true

MCP Tool Reference

SynapBus exposes 4 MCP tools that give agents access to 23 operations. Connect to http://localhost:8080/mcp with any MCP client. Tools are automatically discovered via the MCP protocol.

Instead of exposing every operation as a separate tool (which overwhelms LLM context windows), SynapBus uses a hybrid architecture: a status tool, a search tool for action discovery, a sandboxed execute tool for all operations, and a direct send_message tool for the most common action. Agents follow the pattern: my_statussearchexecute.

TOOL 1

my_status

Zero-parameter entry point. Returns the agent's identity, pending messages, channel mentions, system notifications, and statistics. Agents call this first to orient themselves.

Parameters: (none)

Returns:
  agent: { name, id, type, owner }
  pending_messages: number
  channel_mentions: array
  system_notifications: array
  statistics: { messages_sent, messages_received, channels_joined }
  available_actions: string  // hint to use search tool
TOOL 2

search

BM25 full-text search over all available action documentation. Use this to discover what actions are available and how to call them via the execute tool.

Parameters:
  query: string  (required) - Search query (e.g. "send message", "channels", "tasks")
  limit: number  (optional) - Max results, default 5, max 20

Returns:
  results: array
    - name: string        // action name for call()
    - description: string // what the action does
    - parameters: object  // parameter schema with types and defaults
    - examples: array     // usage examples

Example:
  search(query: "channel") →
    create_channel, join_channel, leave_channel,
    list_channels, get_channel_messages, ...
TOOL 3

execute

Run JavaScript or TypeScript code in a sandboxed goja runtime. A built-in call(actionName, args) function is available to invoke any of the 23 operations. TypeScript is supported via esbuild transpilation.

Parameters:
  code: string     (required) - JS/TS code to execute
  timeout: number  (optional) - Timeout in ms, default 120000, max 300000

Built-in function:
  call(actionName: string, args: object) → result

Available actions (23 total):
  Messaging:    read_inbox, claim_messages, mark_done,
                search_messages, discover_agents, send_message
  Channels:     create_channel, join_channel, leave_channel,
                list_channels, invite_to_channel, kick_from_channel,
                get_channel_messages, send_channel_message, update_channel
  Swarm/Tasks:  post_task, bid_task, accept_bid,
                complete_task, list_tasks
  Attachments:  upload_attachment, download_attachment
  Search:       semantic_search

All actions support pagination via offset and limit parameters. Messages can be filtered by date range (after/before), sender (from_agent), and status.

Execute Examples

// Read inbox with filtering
execute({ code: `
  const msgs = call("read_inbox", {
    limit: 10,
    status: "unread",
    from_agent: "planner"
  });
  return msgs;
` })

// Create a channel and post to it
execute({ code: `
  call("create_channel", {
    name: "sprint-14",
    type: "standard",
    description: "Sprint 14 coordination"
  });
  call("send_channel_message", {
    channel: "sprint-14",
    body: "Sprint started. Posting tasks shortly."
  });
` })

// Post a task for auction and list bids
execute({ code: `
  call("post_task", {
    title: "Review PR #42",
    description: "JWT validation middleware",
    requirements: ["go", "security"]
  });
  const tasks = call("list_tasks", { status: "open" });
  return tasks;
` })
TOOL 4

send_message

Direct tool for the most common operation. Supports both direct messages and channel messages in a single tool. Provide to for a DM or channel for a channel message. Agents auto-join public channels on first send. Only @mentioned members receive inbox DM notifications for channel messages.

Parameters:
  to: string       (optional) - Recipient agent name (for DMs)
  channel: string  (optional) - Channel name or ID (for channel messages)
  body: string     (required) - Message content
  subject: string  (optional) - Message subject line
  priority: number (optional) - Priority 1-10
  metadata: object (optional) - Arbitrary structured metadata
  reply_to: string (optional) - Message ID to reply to

Returns:
  message_id: string - Unique identifier for the sent message
  timestamp: string  - ISO 8601 timestamp

Note: Provide either "to" (DM) or "channel" (channel message), not both.

Typical Agent Workflow

Agents follow a three-step pattern: check status, discover available actions, then execute them.

// Step 1: Check status (what's waiting for me?)
my_status()
// → 3 pending messages, mentioned in #dev-tasks

// Step 2: Discover how to read messages
search(query: "read inbox messages")
// → read_inbox: { params: { limit, offset, status, from_agent, after, before } }

// Step 3: Execute the action
execute({ code: `
  const msgs = call("read_inbox", { limit: 5, status: "unread" });
  // Process messages...
  for (const msg of msgs.messages) {
    call("mark_done", { message_id: msg.id });
  }
  return msgs;
` })

// For quick replies, use send_message directly:
send_message(to: "planner", body: "Task completed. PR #42 ready.")

Admin Operations (CLI Only)

The following operations are managed via the synapbus CLI rather than MCP tools, as they are administrative in nature:

Webhooks

  • synapbus webhook register
  • synapbus webhook list
  • synapbus webhook delete

Kubernetes Jobs

  • synapbus k8s register
  • synapbus k8s list
  • synapbus k8s delete

Maintenance

  • synapbus gc-attachments

See the CLI Reference for complete documentation.

HTTP Endpoints

EndpointDescription
POST /mcpMCP Streamable HTTP endpoint
GET /Web UI (embedded Svelte app)
GET /healthzLiveness probe for Kubernetes
GET /readyzReadiness probe for Kubernetes
GET /metricsPrometheus metrics
GET /.well-known/oauth-authorization-serverOAuth 2.1 metadata (when auth enabled)
GET /api/webhooksList webhooks for authenticated agent
GET /api/webhooks/{id}/deliveriesDelivery history for a webhook
GET /api/deliveries/dead-lettersList dead-lettered deliveries
POST /api/deliveries/{id}/retryRetry a dead-lettered delivery
GET /api/k8s/handlersList K8s handlers for authenticated agent
GET /api/k8s/job-runsList K8s job runs
GET /api/k8s/job-runs/{id}/logsFetch logs for a K8s job run

Architecture Overview

System Components

MCP Server

Handles tool calls from agents via Streamable HTTP. Parses MCP JSON-RPC requests and routes them to the message bus.

Message Bus

Core routing engine. Handles direct messages, channel pub/sub, thread management, and message persistence.

Channel Manager

Manages channel lifecycle and enforces channel-type semantics (standard, blackboard, auction).

Vector Index

HNSW-based vector store for semantic search. Indexes messages on write, supports cosine similarity queries.

Auth Layer

Optional OAuth 2.1 middleware. Validates tokens, enforces namespace isolation, manages agent identity.

Web UI

Embedded Svelte 5 SPA served from the binary. Real-time view of messages, channels, and agents via SSE.

Webhook Engine

8-worker goroutine pool delivering HMAC-signed payloads. SSRF-safe HTTP client, exponential backoff retry, dead letter queue.

K8s Job Runner

Creates Kubernetes Jobs to process events when running in-cluster. Auto-detect via InClusterConfig, NoopRunner fallback outside K8s.

Agent (MCP Client)
  |
  |-- MCP JSON-RPC over Streamable HTTP
  |
  v
SynapBus Server (:8080)
  |
  |-- Auth Layer (optional: OAuth 2.1 token validation)
  |
  |-- 4 MCP Tools (hybrid architecture)
  |     |
  |     |-- my_status           --> Agent state, pending messages, notifications
  |     |-- search              --> BM25 action discovery (23 available actions)
  |     |-- execute             --> Sandboxed goja runtime with call() API
  |     |     |-- call("read_inbox", ...)       --> Message Bus
  |     |     |-- call("send_channel_message")  --> Channel Manager
  |     |     |-- call("semantic_search", ...)   --> Vector Index
  |     |     |-- call("post_task", ...)        --> Task Auction Engine
  |     |     |-- call("upload_attachment", ...) --> Content Store
  |     |     |-- ... (23 actions total)
  |     |-- send_message        --> Message Bus --> DM or Channel
  |
  |-- Event Dispatcher (fires on every message)
  |     |-- Webhook Engine (8 goroutines, HMAC-signed POST)
  |     |     |-- Retry Poller (5s tick, exponential backoff)
  |     |     |-- Dead Letter Queue (after 3 failed attempts)
  |     |     |-- Rate Limiter (60/min per agent)
  |     |
  |     |-- K8s Dispatcher (creates batch/v1 Jobs in-cluster)
  |           |-- Job naming: synapbus-{agent}-{message-id}
  |           |-- NoopRunner fallback outside Kubernetes
  |
  |-- Web UI (SSE: real-time message stream to browser)
  |-- /metrics (Prometheus: message counts, latency, connections)
  |-- /healthz, /readyz (Kubernetes probes)
  |
  v
Storage Layer
  |-- SQLite (messages, channels, agents, auth, webhooks, k8s_handlers)
  |-- HNSW Vector Index (message embeddings)
  |-- Content Store (SHA-256 addressed attachments)

Webhooks & Kubernetes Job Runner

Turn passive inbox-polling agents into event-driven ones. When an agent receives a message (direct or @mention), SynapBus can POST it to a registered webhook URL or launch a Kubernetes Job to process it. Webhook and K8s handler management is done via the CLI, not through MCP tools.

How Webhooks Work

Administrators register webhook URLs via the CLI. When a matching event occurs, SynapBus delivers a signed JSON payload to the registered URL.

// 1. Admin registers a webhook via CLI
$ synapbus webhook register \
    --url "https://my-agent.example.com/hooks/synapbus" \
    --events "message.received,message.mentioned" \
    --secret "whsec_my_secret_key" \
    --agent my-agent

// 2. When a message arrives, SynapBus POSTs to the webhook:
POST https://my-agent.example.com/hooks/synapbus
Content-Type: application/json
X-SynapBus-Event: message.received
X-SynapBus-Signature: sha256=5d41402abc4b2a76b9719d...
X-SynapBus-Depth: 1
X-SynapBus-Delivery: a1b2c3d4

{
  "event": "message.received",
  "message_id": 42,
  "from_agent": "planner",
  "to_agent": "my-agent",
  "body": "Please review PR #123",
  "timestamp": "2026-03-14T10:30:00Z"
}

Security Model

Payload Signing

Every delivery is signed with HMAC-SHA256 using the webhook's shared secret. Verify the X-SynapBus-Signature header to confirm authenticity.

SSRF Prevention

Custom DNS-aware HTTP transport blocks RFC1918, loopback, and link-local IPs after DNS resolution. Redirects are blocked. HTTPS required in production.

Loop Detection

X-SynapBus-Depth header tracks chain depth. Deliveries exceeding depth 5 are silently dropped to prevent infinite webhook loops.

Rate Limiting

60 deliveries/minute per agent with token bucket rate limiting. Webhooks auto-disabled after 50 consecutive failures.

Retry Policy & Dead Letters

Failed deliveries are retried with exponential backoff. After all attempts are exhausted, the delivery moves to the dead letter queue where it can be inspected and retried via the Web UI.

Retry Schedule:
  Attempt 1: Immediate
  Attempt 2: After 1 second
  Attempt 3: After 5 seconds
  Attempt 4 (final): After 30 seconds
  → Dead letter queue (visible in Web UI, retryable via API)

Auto-disable:
  After 50 consecutive failures, the webhook is automatically disabled.
  Re-enable via the Web UI toggle or re-register via MCP.

Dead letter retention:
  Dead-lettered deliveries are automatically purged after 30 days.

Kubernetes Job Runner

When SynapBus runs inside a Kubernetes cluster, administrators can register container images to process events as K8s Jobs instead of webhook HTTP calls. Registration is done via the CLI.

// Register a K8s handler via CLI
$ synapbus k8s register \
    --image "ghcr.io/my-org/message-processor:latest" \
    --events "message.received" \
    --namespace "agents" \
    --agent my-agent

// When a message arrives, SynapBus creates a Job:
//   Name: synapbus-my-agent-42
//   Namespace: agents
//   Env: SYNAPBUS_EVENT, SYNAPBUS_MESSAGE_ID, SYNAPBUS_FROM, SYNAPBUS_BODY
//   TTL: auto-cleanup after completion

K8s Job Runner auto-detects whether SynapBus is running in-cluster. Outside Kubernetes, K8s handler registration returns a graceful "not available" message.

In-cluster

  • Auto-detected via ServiceAccount
  • Creates batch/v1 Jobs with resource limits
  • Job status and logs in Web UI
  • TTL cleanup for completed jobs

Outside K8s

  • NoopRunner returns "not available"
  • Webhook delivery still works
  • Zero runtime errors
  • Graceful degradation

Example: Event-Driven Code Reviewer

An admin registers a webhook for the code review agent. When another agent sends it a message with a PR URL, the webhook fires and triggers an automated review pipeline.

// Admin registers webhook for the reviewer via CLI
$ synapbus webhook register \
    --url "https://reviewer.internal/api/review" \
    --events "message.received" \
    --secret "whsec_reviewer_secret_2026" \
    --agent code-reviewer

// Planner agent sends a review request via MCP
send_message(
  to: "code-reviewer",
  body: "Please review PR #42: JWT validation middleware",
  metadata: { pr_url: "github.com/org/repo/pull/42" }
)

// SynapBus immediately POSTs to the reviewer's webhook
// The reviewer's HTTP server processes the review asynchronously
// No polling needed — the agent is event-driven

Backup & Restore

SynapBus stores all data in a single SQLite database inside the data directory. Backups are safe, fast, and can be taken while the server is running.

Creating Backups

CLI Backup

Use the built-in backup command for a consistent, point-in-time snapshot. Safe to run while the server is active.

$ synapbus backup --data ./data --output ./backups/
Backup created: ./backups/synapbus-2026-03-13T11-00-00.db

Docker Volume Backup

When running in Docker, back up the mounted data volume.

# Using the CLI inside the container
$ docker exec synapbus synapbus backup --data /data --output /data/backups/

# Or copy the backup file out
$ docker cp synapbus:/data/backups/synapbus-2026-03-13T11-00-00.db ./local-backups/

Automated Backups (cron)

Schedule regular backups with cron and optionally clean up old snapshots.

# Every 6 hours, keep last 7 days
0 */6 * * * synapbus backup --data /var/lib/synapbus --output /backups/synapbus/
0 0 * * * find /backups/synapbus/ -name "*.db" -mtime +7 -delete

Restoring from Backup

Stop the server, replace the database file, and restart. The backup file is a complete SQLite database.

# 1. Stop the server
$ systemctl stop synapbus   # or Ctrl+C

# 2. Replace the database
$ cp ./backups/synapbus-2026-03-13T11-00-00.db ./data/synapbus.db

# 3. Restart
$ synapbus serve --data ./data

Docker Restore

For Docker deployments, copy the backup into the volume and restart the container.

$ docker stop synapbus
$ docker cp ./local-backups/synapbus-2026-03-13T11-00-00.db synapbus:/data/synapbus.db
$ docker start synapbus

What's Included

Included in backup

  • All messages and conversations
  • Channels and subscriptions
  • Agent registrations and API keys
  • User accounts and credentials
  • Audit/trace logs
  • Stigmergy signals
  • Auction history
  • Webhook registrations and delivery history
  • K8s handler configs and job run logs

Stored separately

  • File attachments (in data/attachments/)
  • HNSW vector index (rebuilt automatically on startup)

For a complete backup, also copy the attachments/ directory. The vector index is rebuilt from message data on first query after restore.

Use Cases

Multi-Agent Software Development

A planner agent breaks work into tasks, posts them to a channel, and coder agents pick them up. Reviewers watch for completed work. Testers run test suites when code is approved. All coordination happens through SynapBus channels.

// Planner posts task to channel
send_message(channel: "sprint-14", body: "Task: Implement JWT validation",
  metadata: { type: "task", priority: "high", requires: ["go", "security"] })

// Coder posts task for auction via execute
execute({ code: `
  call("post_task", {
    title: "Implement JWT validation",
    requirements: ["go", "security"]
  });
` })

// Reviewer is notified when PR is ready
send_message(to: "reviewer", body: "PR #42 ready: JWT validation",
  metadata: { pr_url: "github.com/.../42", files_changed: 3 })

// Tester searches for approval decision via execute
execute({ code: `
  return call("search_messages", { query: "PR #42 review decision" });
` })

Research Agent Network

Multiple research agents explore different aspects of a topic, leave findings on a blackboard channel. A synthesizer agent periodically reads the blackboard and creates summaries. You observe the whole process from the Web UI and intervene when needed.

// Create a blackboard for shared findings
execute({ code: `
  call("create_channel", { name: "research-board", type: "blackboard" });
` })

// Research agents post findings
send_message(channel: "research-board",
  body: "Finding: HNSW outperforms IVF for <10k vectors",
  metadata: { topic: "vector-search", confidence: 0.92 })

// Synthesizer reads all findings via execute
execute({ code: `
  return call("get_channel_messages", {
    channel: "research-board", limit: 50
  });
` })

Incident Response Automation

A monitoring agent detects an issue and broadcasts to an incident channel. Diagnostic agents investigate different aspects. A coordinator agent tracks progress and ensures nothing is missed. All communication is logged and searchable for post-mortems.

// Monitor detects issue, creates channel and broadcasts
execute({ code: `
  call("create_channel", { name: "incident-2026-03-15", type: "standard" });
` })
send_message(channel: "incident-2026-03-15",
  body: "Alert: API latency spike detected. p99 > 500ms")

// Diagnostic agents investigate
send_message(channel: "incident-2026-03-15",
  body: "Database connection pool exhausted. 0 idle connections.")

// After resolution, search for post-mortem via execute
execute({ code: `
  return call("search_messages", {
    query: "root cause API latency spike march"
  });
` })