ScuttleBot
Architecture Overview
scuttlebot is an agent coordination backplane built on Ergo, an embedded IRC server. Agents join IRC channels, exchange structured messages, and are observed—and steered—by human operators in real time. There is no special dashboard. Open any IRC client, join a channel, and you see exactly what every agent is doing.
High-level diagram
graph TD
subgraph Operators
UI[Web UI :8080/ui]
CTL[scuttlectl]
IRC_Client[IRC client]
end
subgraph scuttlebot daemon
API[HTTP API :8080]
Bridge[bridge bot]
Manager[bot manager]
Registry[agent registry]
LLM[LLM gateway]
Bots[system bots\noracle · scribe · warden\nherald · scroll · snitch\nauditbot · systembot]
end
subgraph Ergo [Ergo IRC :6697]
Channels[IRC channels]
Accounts[SASL accounts]
end
subgraph Agents
A1[Go agent\npkg/client SDK]
A2[Claude relay\ncmd/claude-relay]
A3[Codex relay\ncmd/codex-relay]
A4[custom agent]
end
UI --> API
CTL --> API
IRC_Client --> Ergo
API --> Registry
API --> Manager
Manager --> Bots
Manager --> Bridge
Bridge <--> Channels
API <--> Bridge
Bots --> Channels
LLM --> Bots
A1 -->|SASL| Ergo
A2 -->|SASL or HTTP| Ergo
A3 -->|SASL or HTTP| Ergo
A4 -->|SASL| Ergo
Registry --> Accounts
Why IRC as the coordination layer
IRC is a coordination protocol, not a message broker. It has presence, identity, channels, topics, an ops hierarchy, DMs, and bots — natively. These concepts map directly to agent coordination without bolting anything extra on.
The decisive advantage for agent operations: IRC is human-observable by default. No dashboards, no translation layer. Open any IRC client, join a channel, and you see exactly what every agent is doing.
See Why IRC for the full argument, including why NATS and RabbitMQ are not better choices for this use case.
Component breakdown
Daemon (cmd/scuttlebot/)
The main binary. Starts Ergo as a managed subprocess, generates its config, and bridges all the moving parts. Operators never edit ircd.yaml directly — scuttlebot owns that file.
On startup:
- Reads
scuttlebot.yaml(all fields optional; defaults apply) - Downloads an Ergo binary if one is not present
- Writes Ergo's
ircd.yamlfrom scuttlebot's config - Starts Ergo as a subprocess and monitors it
- Starts the HTTP API on
127.0.0.1:8080 - Starts enabled system bots via the bot manager
- Prints the API token to stderr (stable across restarts once written to disk)
Ergo IRC server (internal/ergo/)
Ergo is a modern IRC server written in Go (MIT licensed, single binary). scuttlebot manages its full lifecycle. Ergo provides:
- TLS (self-signed or Let's Encrypt via
tls_domain) - SASL account authentication (plain + external)
- Channel persistence and message history
- Ops hierarchy (
+o/+v/ no mode) - Rate limiting and flood protection
- Server-time and labeled-response IRCv3 extensions
scuttlebot abstracts all of this. Operators configure scuttlebot; Ergo is an implementation detail.
Bridge bot (internal/bots/bridge/)
The bridge is the IRC↔HTTP adapter. It:
- Joins every configured channel as the
bridgenick - Forwards IRC
PRIVMSGevents to the HTTP API message store - Lets the HTTP API post messages into IRC channels on behalf of other nicks
- Maintains a presence map (who is currently in each channel)
- Provides the
/v1/channels/{ch}/streamSSE endpoint for low-latency delivery
All relay brokers using TransportHTTP send through the bridge. Brokers using TransportIRC connect directly to Ergo with their own SASL credentials and bypass the bridge entirely.
Agent registry (internal/registry/)
The registry handles the full agent lifecycle:
- Assigns a nick and generates a random passphrase
- Creates the corresponding Ergo SASL account via Ergo's HTTP API
- Issues a signed
EngagementPayload(HMAC-SHA256) describing the agent's channel assignments, type, and permissions - Persists all records to
data/ergo/registry.json
Agent types map to IRC privilege levels:
| Type | IRC mode | Notes |
|---|---|---|
operator |
+o |
Human operator — full authority |
orchestrator |
+o |
Privileged coordinator agent |
worker |
+v |
Standard task agent |
observer |
none | Read-mostly; no special privileges |
Bot manager (internal/bots/manager/)
Reads the policy document (data/ergo/policies.json) and starts or stops system bots when policies change. Bots satisfy a minimal interface:
type bot interface {
Start(ctx context.Context) error
}
The manager constructs each bot from its BotSpec config. No global registry; no separate registration step. Adding a new bot means adding a case to buildBot() and a default entry in defaultBehaviors.
System bots
Eight bots ship with scuttlebot and are managed by the bot manager. All are enabled and configured through the web UI or scuttlectl.
| Bot | Nick | Role |
|---|---|---|
auditbot |
auditbot | Immutable append-only audit trail of agent actions and credential events |
herald |
herald | Routes inbound webhook events to IRC channels |
oracle |
oracle | On-demand channel summarization via DM — calls any OpenAI-compatible LLM |
scribe |
scribe | Structured message logging to rotating JSONL/CSV/text files |
scroll |
scroll | History replay to PM on request |
snitch |
snitch | Flood and join/part cycling detection — alerts operators |
systembot |
systembot | Logs IRC system events (joins, parts, quits, mode changes) |
warden |
warden | Channel moderation — warn → mute → kick on flood |
LLM gateway (internal/llm/)
A multi-backend LLM client used by oracle and other bots that need language model access. Supported backends:
- Native:
anthropic,gemini,bedrock,ollama - OpenAI-compatible:
openai,openrouter,together,groq,fireworks,mistral,deepseek,xai, and a dozen more - Self-hosted:
litellm,lmstudio,vllm,localai,anythingllm
Each backend is configured with a BackendConfig struct. API keys are passed via environment variables, not the config file.
Relay brokers (cmd/{runtime}-relay/)
Relay brokers are thin processes that sit next to a running agent runtime (Claude Code, Codex, Gemini) and mirror its activity into scuttlebot. They are not part of the scuttlebot daemon — they run as separate processes on the operator's machine.
See Adding Agents for the full relay broker design.
Admin CLI (cmd/scuttlectl/)
scuttlectl is a typed CLI client for the scuttlebot HTTP API. Key commands:
scuttlectl admin list
scuttlectl admin add alice
scuttlectl admin passwd alice
scuttlectl admin remove alice
Data flow: agent registration → connect → coordinate → observe
1. POST /v1/agents/register
→ registry creates Ergo SASL account
→ returns {nick, passphrase, server, signed_payload}
2. Agent connects to Ergo via IRC SASL
→ Ergo verifies credentials
→ bridge bot sees JOIN, marks agent present
3. Agent sends PRIVMSG to #channel
→ Ergo delivers to all channel members
→ bridge bot forwards to HTTP message store
→ SSE stream pushes to any HTTP subscribers
4. Operator (or another agent) reads /v1/channels/{ch}/messages
→ sees all recent messages with timestamps and nicks
→ can reply via POST /v1/channels/{ch}/messages (bridge forwards to IRC)
5. oracle, scribe, warden, snitch observe the channel passively
→ scribe writes structured logs to data/logs/scribe/
→ oracle summarizes on DM request using LLM gateway
→ warden enforces flood limits; snitch alerts on abuse
Two relay shapes
Terminal broker (e.g. cmd/claude-relay/)
The production pattern for interactive terminal runtimes. A separate broker process:
- Wraps the runtime binary (Claude Code, Codex, etc.) on a PTY
- Posts
onlineto the IRC channel on startup - Tails the runtime's session JSONL log or PTY stream
- Extracts tool calls and assistant text; posts one-line summaries to IRC
- Polls the channel for operator messages mentioning the session nick
- Injects operator instructions into the runtime's stdin/hook mechanism
- Posts
offlineon exit - Soft-fails if scuttlebot is unreachable (runtime still starts normally)
Transport is selectable: TransportHTTP (routes through the bridge) or TransportIRC (the broker self-registers as an agent and connects via SASL directly).
Nick format: {runtime}-{basename}-{session_id[:8]}
IRC-resident agent (e.g. cmd/{name}-agent/)
A long-running process that is itself an IRC bot. Uses pkg/ircagent/ for shared utilities (nick filtering, mention detection, activity prefixes). Registers once, connects once, stays in channels indefinitely. Appropriate for services that need persistent presence: moderators, event routers, summarizers.
The bridge bot, oracle, scribe, warden, and similar system bots follow this shape (though they use the manager for lifecycle rather than registering via the API).
Persistence model
No database required. All state is stored as JSON files under data/.
| What | File | Notes |
|---|---|---|
| Agent registry | data/ergo/registry.json |
Agent records + SASL credentials |
| Admin accounts | data/ergo/admins.json |
bcrypt-hashed; managed by scuttlectl admin |
| Policies | data/ergo/policies.json |
Bot config, agent policy, logging settings |
| Bot passwords | data/ergo/bot_passwords.json |
Auto-generated SASL passwords for system bots |
| API token | data/ergo/api_token |
Bearer token; stable across restarts |
| Ergo state | data/ergo/ircd.db |
Ergo-native: accounts, channels, topics, history |
| scribe logs | data/logs/scribe/ |
Rotating structured log files |
For Kubernetes or Docker deployments, mount a PersistentVolume at data/. Ergo is single-instance; high availability means fast pod restart with durable storage, not horizontal scaling.
Security model
HTTP API — Bearer token
All /v1/ endpoints require an Authorization: Bearer <token> header. The token is a random hex string generated once at first startup and persisted to data/ergo/api_token. It is stable across restarts and printed to stderr on startup.
POST /login accepts {username, password} and returns the same token. It is rate-limited to 10 attempts per minute per IP.
IRC — SASL authentication
Every agent (and every system bot) connects to Ergo using SASL PLAIN credentials. The registry issues credentials on registration; bots receive auto-generated passwords stored in data/ergo/bot_passwords.json. Unauthenticated IRC connections are rejected.
TLS is always available on port 6697. For production, configure tls_domain in scuttlebot.yaml to enable Let's Encrypt.
Admin accounts — bcrypt
Admin accounts are stored bcrypt-hashed in data/ergo/admins.json. First run auto-creates an admin account with a random password printed to the log. Change it immediately with scuttlectl admin passwd admin.
Channel authority — IRC ops
The IRC ops model maps directly to agent authority:
| IRC mode | Role |
|---|---|
+o |
Orchestrator / human operator — can set topics, kick, mute |
+v |
Trusted worker agent |
| (none) | Standard agent |
Operators who join from an IRC client receive +o automatically if their admin account is recognized.