ScuttleBot

scuttlebot / bootstrap.md
Source Blame History 410 lines
6d0d615… lmata 1 # scuttlebot Bootstrap
6d0d615… lmata 2
6d0d615… lmata 3 This is the primary conventions document. All agent shims (`CLAUDE.md`, `AGENTS.md`, `GEMINI.md`, `calliope.md`) point here.
6d0d615… lmata 4
6d0d615… lmata 5 An agent given this document and a business requirement should be able to generate correct, idiomatic code without exploring the codebase.
6d0d615… lmata 6
6d0d615… lmata 7 ---
6d0d615… lmata 8
1ab586b… lmata 9 ## Why IRC (and not NATS or RabbitMQ)
1ab586b… lmata 10
1ab586b… lmata 11 The short answer: IRC is a coordination protocol. NATS and RabbitMQ are message brokers. The difference matters.
1ab586b… lmata 12
1ab586b… lmata 13 ### IRC
1ab586b… lmata 14
1ab586b… lmata 15 IRC has presence, identity, channels, topics, ops hierarchy, DMs, and bots — natively. These map directly to agent coordination concepts without bolting anything on. A channel is a team. A topic is shared state. Ops are authority. Bots are services. It all just works.
1ab586b… lmata 16
1ab586b… lmata 17 It is also **human observable by default**. No dashboards, no special tooling, no translation layer. Open any IRC client, join a channel, and you see exactly what agents are doing. This is the single biggest advantage for debugging and operating agent systems.
1ab586b… lmata 18
1ab586b… lmata 19 Other properties that matter for agent coordination:
1ab586b… lmata 20 - **Latency tolerant** — fire-and-forget, designed for unreliable networks. Agents can reconnect, miss messages, catch up via history. This is a feature, not a limitation.
1ab586b… lmata 21 - **Battle-tested** — 35+ years, RFC 1459 (1993), proven at scale. Not going anywhere.
1ab586b… lmata 22 - **Self-hostable, zero vendor lock-in** — Ergo is MIT, single Go binary. No cloud, no subscription.
1ab586b… lmata 23 - **Bots are a solved problem** — NickServ, ChanServ, BotServ, 35 years of tooling. We inherit all of it.
1ab586b… lmata 24 - **Simple enough to debug naked** — the protocol is plain text. When something breaks, you can read it.
1ab586b… lmata 25
1ab586b… lmata 26 ### Why not NATS
1ab586b… lmata 27
1ab586b… lmata 28 NATS is excellent and fast. It is the right choice when you need guaranteed delivery, high-throughput pub/sub, or JetStream persistence at scale. It is not the right choice here because:
1ab586b… lmata 29
1ab586b… lmata 30 - No native presence model — you cannot `WHOIS` a subject or see who is subscribed to a stream
1ab586b… lmata 31 - No ops hierarchy — authority and trust are not protocol concepts
1ab586b… lmata 32 - Not human observable without NATS-specific tooling (no standard client exists for "just watching")
1ab586b… lmata 33 - More moving pieces — JetStream, clustering, leaf nodes, consumers, streams. Powerful but not simple.
1ab586b… lmata 34 - The subject hierarchy (`project.myapp.tasks`) is conceptually identical to our channel naming convention — if we ever needed to swap, the mapping is straightforward
1ab586b… lmata 35
1ab586b… lmata 36 ### Why not RabbitMQ
1ab586b… lmata 37
1ab586b… lmata 38 RabbitMQ is a serious enterprise message broker designed for guaranteed delivery workflows. It is operationally heavy (Erlang runtime, clustering, exchanges, bindings, queues), not human observable without a management UI, and not designed for real-time coordination between actors. Wrong tool for this job.
1ab586b… lmata 39
1ab586b… lmata 40 ### Swappability
1ab586b… lmata 41
1ab586b… lmata 42 The JSON envelope format and the SDK abstraction (`pkg/client/`) are intentionally transport-agnostic. The channel naming convention maps cleanly to NATS subjects. If a use case demands NATS-level throughput or delivery guarantees, swapping the transport is a backend concern that does not affect the agent-facing API.
1ab586b… lmata 43
1ab586b… lmata 44 ---
1ab586b… lmata 45
6d0d615… lmata 46 ## What is scuttlebot
6d0d615… lmata 47
6d0d615… lmata 48 An agent coordination backplane built on IRC. Agents connect as IRC users, coordinate via channels, and communicate via structured messages. IRC is an implementation detail — users configure scuttlebot, never Ergo directly.
6d0d615… lmata 49
6d0d615… lmata 50 **Why IRC:** lightweight TCP transport, encryption, channels, presence, ops hierarchy, DMs, human observable by default. Humans and agents share the same backplane with no translation layer.
6d0d615… lmata 51
6d0d615… lmata 52 **Ergo** (https://ergo.chat) is the IRC server. scuttlebot manages its lifecycle and config. Federation, auth, history, TLS, rate limiting — all Ergo. scuttlebot abstracts it.
6d0d615… lmata 53
6d0d615… lmata 54 ---
6d0d615… lmata 55
6d0d615… lmata 56 ## Monorepo Layout
6d0d615… lmata 57
6d0d615… lmata 58 ```
6d0d615… lmata 59 cmd/
5ac549c… lmata 60 scuttlebot/ # daemon binary
5ac549c… lmata 61 scuttlectl/ # admin CLI
5ac549c… lmata 62 internal/apiclient/ # typed API client used by scuttlectl
6d0d615… lmata 63 internal/
5ac549c… lmata 64 api/ # HTTP API server (Bearer auth) + embedded web UI at /ui/
5ac549c… lmata 65 ui/index.html # single-file operator web UI
5ac549c… lmata 66 auth/ # admin account store — bcrypt hashed, persisted to JSON
5ac549c… lmata 67 bots/
5ac549c… lmata 68 manager/ # bot lifecycle — starts/stops bots on policy change
5ac549c… lmata 69 auditbot/ # immutable append-only audit trail
5ac549c… lmata 70 herald/ # external event → channel routing (webhooks)
5ac549c… lmata 71 oracle/ # on-demand channel summarization via LLM (PM only)
5ac549c… lmata 72 scribe/ # structured logging to rotating files
5ac549c… lmata 73 scroll/ # history replay to PM on request
5ac549c… lmata 74 snitch/ # flood + join/part cycling detection → operator alerts
5ac549c… lmata 75 systembot/ # IRC system events (joins, parts, modes, kicks)
5ac549c… lmata 76 warden/ # channel moderation — warn → mute → kick
5ac549c… lmata 77 config/ # YAML config loading + validation
5ac549c… lmata 78 ergo/ # Ergo IRC server lifecycle + config generation
5ac549c… lmata 79 mcp/ # MCP server for AI agent connectivity
5ac549c… lmata 80 registry/ # agent registration + SASL credential issuance
5ac549c… lmata 81 topology/ # channel provisioning + mode/topic management
6d0d615… lmata 82 pkg/
5ac549c… lmata 83 client/ # Go agent SDK (public)
5ac549c… lmata 84 protocol/ # JSON envelope wire format
6d0d615… lmata 85 deploy/
5ac549c… lmata 86 docker/ # Dockerfile(s)
5ac549c… lmata 87 compose/ # Docker Compose (local dev + single-host)
5ac549c… lmata 88 k8s/ # Kubernetes manifests
5ac549c… lmata 89 standalone/ # single binary, no container required
5ac549c… lmata 90 tests/
5ac549c… lmata 91 e2e/ # Playwright end-to-end tests (require scuttlebot running)
6d0d615… lmata 92 go.mod
6d0d615… lmata 93 go.sum
5ac549c… lmata 94 bootstrap.md
5ac549c… lmata 95 CLAUDE.md # Claude Code shim — points here
6d0d615… lmata 96 ```
6d0d615… lmata 97
5ac549c… lmata 98 Single Go module. All state persisted as JSON files under `data/` (no database required).
6d0d615… lmata 99
6d0d615… lmata 100 ---
6d0d615… lmata 101
6d0d615… lmata 102 ## Architecture
6d0d615… lmata 103
6d0d615… lmata 104 ### Ergo relationship
6d0d615… lmata 105
6d0d615… lmata 106 scuttlebot owns the Ergo process and config. Users never edit `ircd.yaml` directly. scuttlebot generates it from its own config and manages Ergo as a subprocess.
6d0d615… lmata 107
6d0d615… lmata 108 - Ergo provides: TLS, SASL accounts, channel persistence, message history, ops hierarchy, server federation, rate limiting
6d0d615… lmata 109 - scuttlebot provides: agent registration, topology provisioning, rules-of-engagement delivery, built-in bots, SDK/MCP layer
6d0d615… lmata 110
6d0d615… lmata 111 ### Agent lifecycle
6d0d615… lmata 112
6d0d615… lmata 113 1. Agent calls scuttlebot registration endpoint
6d0d615… lmata 114 2. scuttlebot creates Ergo account, issues SASL credentials
6d0d615… lmata 115 3. On connect, agent receives signed rules-of-engagement payload (channel assignments, engagement rules, permissions)
6d0d615… lmata 116 4. Agent connects to Ergo with SASL credentials
6d0d615… lmata 117 5. scuttlebot verifies presence, assigns channel modes
6d0d615… lmata 118
6d0d615… lmata 119 ### Channel topology
6d0d615… lmata 120
6d0d615… lmata 121 Hierarchical, configurable. Convention:
6d0d615… lmata 122
6d0d615… lmata 123 ```
6d0d615… lmata 124 #fleet fleet-wide, quiet, announcements only
6d0d615… lmata 125 #project.{name} project coordination
6d0d615… lmata 126 #project.{name}.{topic} swarming, chatty, active work
6d0d615… lmata 127 #project.{name}.{topic}.{subtopic} deep nesting
6d0d615… lmata 128 #task.{id} ephemeral, auto-created/destroyed
6d0d615… lmata 129 #agent.{name} agent-specific inbox
6d0d615… lmata 130 ```
6d0d615… lmata 131
6d0d615… lmata 132 Users define topology in scuttlebot config. scuttlebot provisions the channels, sets modes and topics.
6d0d615… lmata 133
6d0d615… lmata 134 ### Wire format
6d0d615… lmata 135
6d0d615… lmata 136 - **Agent messages:** JSON envelope in `PRIVMSG`
6d0d615… lmata 137 - **System/status:** `NOTICE` — human readable, machines ignore
6d0d615… lmata 138 - **Agent context packets** (summarization, history replay): TOON format (token-efficient for LLM consumption)
6d0d615… lmata 139
6d0d615… lmata 140 JSON envelope structure:
6d0d615… lmata 141
6d0d615… lmata 142 ```json
6d0d615… lmata 143 {
6d0d615… lmata 144 "v": 1,
6d0d615… lmata 145 "type": "task.create",
6d0d615… lmata 146 "id": "ulid",
6d0d615… lmata 147 "from": "agent-nick",
6d0d615… lmata 148 "ts": 1234567890,
6d0d615… lmata 149 "payload": {}
6d0d615… lmata 150 }
6d0d615… lmata 151 ```
6d0d615… lmata 152
6d0d615… lmata 153 ### Authority / trust hierarchy
6d0d615… lmata 154
6d0d615… lmata 155 IRC ops model maps directly:
6d0d615… lmata 156 - `+o` (channel op) — orchestrator agents, privileged
6d0d615… lmata 157 - `+v` (voice) — trusted worker agents
6d0d615… lmata 158 - no mode — standard agents
6d0d615… lmata 159
6d0d615… lmata 160 ### Built-in bots
6d0d615… lmata 161
a1cd907… noreply 162 All 10 bots are implemented. Enabled/configured via the web UI or `scuttlectl bot list`. The manager (`internal/bots/manager/`) starts/stops them dynamically when policies change. All bots set `+B` (bot) user mode on connect and auto-accept INVITE.
5ac549c… lmata 163
5ac549c… lmata 164 | Bot | Nick | Role |
5ac549c… lmata 165 |-----|------|------|
5ac549c… lmata 166 | `auditbot` | auditbot | Immutable append-only audit trail of agent actions and credential events |
5ac549c… lmata 167 | `herald` | herald | Routes inbound webhook events to IRC channels |
5ac549c… lmata 168 | `oracle` | oracle | On-demand channel summarization via DM — calls any OpenAI-compatible LLM |
5ac549c… lmata 169 | `scribe` | scribe | Structured message logging to rotating files (jsonl/csv/text) |
a1cd907… noreply 170 | `scroll` | scroll | History replay to PM on request (`replay #channel [format=toon]`) |
a1cd907… noreply 171 | `sentinel` | sentinel | LLM-powered channel observer — detects policy violations, posts structured incident reports to mod channel. Never takes enforcement action. |
a1cd907… noreply 172 | `snitch` | snitch | Flood and join/part cycling detection, MONITOR-based presence tracking, away-notify alerts |
a1cd907… noreply 173 | `steward` | steward | Acts on sentinel incident reports — issues warnings, mutes (extended ban `m:`), or kicks based on severity |
5ac549c… lmata 174 | `systembot` | systembot | Logs IRC system events (joins, parts, quits, mode changes) |
a1cd907… noreply 175 | `warden` | warden | Channel moderation — warn → mute (extended ban) → kick on flood |
5ac549c… lmata 176
a1cd907… noreply 177 Oracle uses TOON format (`pkg/toon/`) for token-efficient LLM context. Scroll supports `format=toon` for compact replay output. Configure `api_key_env` to the name of the env var holding the API key (e.g. `ORACLE_OPENAI_API_KEY`), and `base_url` for non-OpenAI providers.
6d0d615… lmata 178
6d0d615… lmata 179 ### Scale
6d0d615… lmata 180
6d0d615… lmata 181 Target: 100s to low 1000s of agents on a private network. Single Ergo instance handles this comfortably (documented up to 10k clients, 2k per channel). Ergo scales up (multi-core), not out — no horizontal clustering today. Federation is planned upstream but has no timeline; not a scuttlebot concern for now.
6d0d615… lmata 182
6d0d615… lmata 183 ### Persistence
6d0d615… lmata 184
5ac549c… lmata 185 No database required. All state is persisted as JSON files under `data/` by default.
5ac549c… lmata 186
5ac549c… lmata 187 | What | File | Notes |
5ac549c… lmata 188 |------|------|-------|
5ac549c… lmata 189 | Agent registry | `data/ergo/registry.json` | Agent records + SASL credentials |
5ac549c… lmata 190 | Admin accounts | `data/ergo/admins.json` | bcrypt-hashed; created by `scuttlectl admin add` |
5ac549c… lmata 191 | Policies | `data/ergo/policies.json` | Bot config, agent policy, logging settings |
5ac549c… lmata 192 | Bot passwords | `data/ergo/bot_passwords.json` | Auto-generated SASL passwords for system bots |
a1cd907… noreply 193 | API token | `data/ergo/api_token` | Legacy token; migrated to api_keys.json on first run |
a1cd907… noreply 194 | API keys | `data/ergo/api_keys.json` | Per-consumer tokens with scoped permissions (SHA-256 hashed) |
5ac549c… lmata 195 | Ergo state | `data/ergo/ircd.db` | Ergo-native: accounts, channels, topics, history |
5ac549c… lmata 196 | scribe logs | `data/logs/scribe/` | Rotating log files (jsonl/csv/text); configurable |
5ac549c… lmata 197
5ac549c… lmata 198 K8s / Docker: mount a PersistentVolume at `data/`. Ergo is single-instance — HA = fast pod restart with durable storage, not horizontal scaling.
6d0d615… lmata 199
6d0d615… lmata 200 ---
6d0d615… lmata 201
6d0d615… lmata 202 ## Conventions
6d0d615… lmata 203
6d0d615… lmata 204 ### Go
6d0d615… lmata 205
6d0d615… lmata 206 - Go 1.22+
6d0d615… lmata 207 - `gofmt` + `golangci-lint`
6d0d615… lmata 208 - Errors returned, not panicked. Wrap with context: `fmt.Errorf("registry: create account: %w", err)`
6d0d615… lmata 209 - Interfaces defined at point of use, not in the package that implements them
6d0d615… lmata 210 - No global state. Dependencies injected via struct fields or constructor args.
6d0d615… lmata 211 - Config via struct + YAML/TOML — no env var spaghetti (env vars for secrets only)
6d0d615… lmata 212
6d0d615… lmata 213 ### Tests
6d0d615… lmata 214
6d0d615… lmata 215 - `go test ./...`
6d0d615… lmata 216 - Integration tests use a real Ergo instance (Docker Compose in CI)
6d0d615… lmata 217 - Assert against observable state — channel membership, messages received, account existence
6d0d615… lmata 218 - Both happy path and error cases
6d0d615… lmata 219 - No mocking the IRC connection in integration tests
6d0d615… lmata 220
6d0d615… lmata 221 ### Commits + branches
6d0d615… lmata 222
6d0d615… lmata 223 - Branch: `feature/{issue}-short-description` or `fix/{issue}-short-description`
6d0d615… lmata 224 - No rebases. New commits only.
6d0d615… lmata 225 - No AI attribution in commits.
6d0d615… lmata 226
6d0d615… lmata 227 ---
6d0d615… lmata 228
5ac549c… lmata 229 ## HTTP API
5ac549c… lmata 230
5ac549c… lmata 231 `internal/api/` — two-mux pattern:
5ac549c… lmata 232
5ac549c… lmata 233 - **Outer mux** (unauthenticated): `POST /login`, `GET /` (redirect), `GET /ui/` (web UI)
5ac549c… lmata 234 - **Inner mux** (`/v1/` routes): require `Authorization: Bearer <token>` header
5ac549c… lmata 235
5ac549c… lmata 236 ### Auth
5ac549c… lmata 237
a1cd907… noreply 238 API keys are per-consumer tokens with scoped permissions. Each key has a name, scopes, optional expiry, and last-used tracking. Scopes: `admin`, `agents`, `channels`, `chat`, `topology`, `bots`, `config`, `read`. The `admin` scope implies all others.
a1cd907… noreply 239
a1cd907… noreply 240 `POST /login` accepts `{username, password}` and returns a 24h session token with admin scope. Rate limited to 10 attempts per minute per IP.
a1cd907… noreply 241
a1cd907… noreply 242 On first run, the legacy `api_token` file is migrated into `api_keys.json` as the first admin-scope key. New keys are created via `POST /v1/api-keys`, `scuttlectl api-key create`, or the web UI settings tab.
5ac549c… lmata 243
a1cd907… noreply 244 Admin accounts managed via `scuttlectl admin` or web UI. First run auto-creates `admin` with a random password printed to the log.
5ac549c… lmata 245
5ac549c… lmata 246 ### Key endpoints
5ac549c… lmata 247
a1cd907… noreply 248 All `/v1/` endpoints require a Bearer token with the appropriate scope.
a1cd907… noreply 249
a1cd907… noreply 250 | Method | Path | Scope | Description |
a1cd907… noreply 251 |--------|------|-------|-------------|
a1cd907… noreply 252 | `POST` | `/login` | — | Username/password login (unauthenticated) |
a1cd907… noreply 253 | `GET` | `/v1/status` | read | Server status |
a1cd907… noreply 254 | `GET` | `/v1/metrics` | read | Runtime metrics + bridge stats |
a1cd907… noreply 255 | `GET` | `/v1/settings` | read | Full settings (policies, TLS, bot commands) |
a1cd907… noreply 256 | `GET/PUT/PATCH` | `/v1/settings/policies` | admin | Bot config, agent policy, logging |
a1cd907… noreply 257 | `GET` | `/v1/agents` | agents | List all registered agents |
a1cd907… noreply 258 | `GET` | `/v1/agents/{nick}` | agents | Get single agent |
a1cd907… noreply 259 | `PATCH` | `/v1/agents/{nick}` | agents | Update agent |
a1cd907… noreply 260 | `POST` | `/v1/agents/register` | agents | Register an agent |
a1cd907… noreply 261 | `POST` | `/v1/agents/{nick}/rotate` | agents | Rotate credentials |
a1cd907… noreply 262 | `POST` | `/v1/agents/{nick}/adopt` | agents | Adopt existing IRC nick |
a1cd907… noreply 263 | `POST` | `/v1/agents/{nick}/revoke` | agents | Revoke agent credentials |
a1cd907… noreply 264 | `DELETE` | `/v1/agents/{nick}` | agents | Delete agent |
a1cd907… noreply 265 | `GET` | `/v1/channels` | channels | List joined channels |
a1cd907… noreply 266 | `POST` | `/v1/channels/{ch}/join` | channels | Join channel |
a1cd907… noreply 267 | `DELETE` | `/v1/channels/{ch}` | channels | Leave channel |
a1cd907… noreply 268 | `GET` | `/v1/channels/{ch}/messages` | channels | Get message history |
a1cd907… noreply 269 | `POST` | `/v1/channels/{ch}/messages` | chat | Send message |
a1cd907… noreply 270 | `POST` | `/v1/channels/{ch}/presence` | chat | Touch presence (keep web user visible) |
a1cd907… noreply 271 | `GET` | `/v1/channels/{ch}/users` | channels | User list with IRC modes |
a1cd907… noreply 272 | `GET` | `/v1/channels/{ch}/config` | channels | Per-channel display config |
a1cd907… noreply 273 | `PUT` | `/v1/channels/{ch}/config` | channels | Set display config (mirror detail, render mode) |
a1cd907… noreply 274 | `GET` | `/v1/channels/{ch}/stream` | channels | SSE stream (`?token=` query param auth) |
a1cd907… noreply 275 | `POST` | `/v1/channels` | topology | Provision channel via ChanServ |
a1cd907… noreply 276 | `DELETE` | `/v1/topology/channels/{ch}` | topology | Drop channel |
a1cd907… noreply 277 | `GET` | `/v1/topology` | topology | Channel types, static channels, active channels |
a1cd907… noreply 278 | `GET/PUT` | `/v1/config` | config | Server config read/write |
a1cd907… noreply 279 | `GET` | `/v1/config/history` | config | Config change history |
a1cd907… noreply 280 | `GET/POST` | `/v1/admins` | admin | List / add admin accounts |
a1cd907… noreply 281 | `DELETE` | `/v1/admins/{username}` | admin | Remove admin |
a1cd907… noreply 282 | `PUT` | `/v1/admins/{username}/password` | admin | Change password |
a1cd907… noreply 283 | `GET/POST` | `/v1/api-keys` | admin | List / create API keys |
a1cd907… noreply 284 | `DELETE` | `/v1/api-keys/{id}` | admin | Revoke API key |
a1cd907… noreply 285 | `GET/POST/PUT/DELETE` | `/v1/llm/backends[/{name}]` | bots | LLM backend CRUD |
a1cd907… noreply 286 | `GET` | `/v1/llm/backends/{name}/models` | bots | List models for backend |
a1cd907… noreply 287 | `POST` | `/v1/llm/discover` | bots | Discover models from provider |
a1cd907… noreply 288 | `POST` | `/v1/llm/complete` | bots | LLM completion proxy |
5ac549c… lmata 289
5ac549c… lmata 290 ---
5ac549c… lmata 291
6d0d615… lmata 292 ## Adding a New Bot
6d0d615… lmata 293
5ac549c… lmata 294 1. Create `internal/bots/{name}/` package with a `Bot` struct and `Start(ctx context.Context) error` method
a1cd907… noreply 295 2. Set `+B` user mode on connect, handle INVITE for auto-join
a1cd907… noreply 296 3. Add a `BotSpec` config struct if the bot needs user-configurable settings
a1cd907… noreply 297 4. Register in `internal/bots/manager/manager.go`:
5ac549c… lmata 298 - Add a case to `buildBot()` that constructs your bot from the spec config
5ac549c… lmata 299 - Add a `BehaviorConfig` entry to `defaultBehaviors` in `internal/api/policies.go`
a1cd907… noreply 300 5. Add commands to `botCommands` map in `internal/api/policies.go` for the web UI command reference
a1cd907… noreply 301 6. Add the UI config schema to `BEHAVIOR_SCHEMAS` in `internal/api/ui/index.html`
a1cd907… noreply 302 7. Use `internal/bots/cmdparse/` for command routing if the bot accepts DM commands
a1cd907… noreply 303 8. Write tests: bot logic, config parsing, edge cases. IRC connection can be skipped in unit tests.
a1cd907… noreply 304 9. Update this bootstrap
5ac549c… lmata 305
5ac549c… lmata 306 No separate registration file or global registry. The manager builds bots by ID from the `BotSpec`. Bots satisfy the `bot` interface (unexported in manager package):
5ac549c… lmata 307
5ac549c… lmata 308 ```go
5ac549c… lmata 309 type bot interface {
5ac549c… lmata 310 Start(ctx context.Context) error
5ac549c… lmata 311 }
5ac549c… lmata 312 ```
6d0d615… lmata 313
6d0d615… lmata 314 ---
6d0d615… lmata 315
6d0d615… lmata 316 ## Adding a New SDK
6d0d615… lmata 317
6d0d615… lmata 318 1. Create `sdk/{language}/` as its own module
6d0d615… lmata 319 2. Implement the client interface defined in `pkg/client/` as reference
6d0d615… lmata 320 3. Cover: connect, register, send message, receive message, disconnect
6d0d615… lmata 321 4. Own CI workflow in `.github/workflows/sdk-{language}.yml`
6d0d615… lmata 322
6d0d615… lmata 323 ---
6d0d615… lmata 324
6d0d615… lmata 325 ## Ports (local)
6d0d615… lmata 326
6d0d615… lmata 327 | Service | Address |
6d0d615… lmata 328 |---------|---------|
6d0d615… lmata 329 | Ergo IRC | `ircs://localhost:6697` |
6d0d615… lmata 330 | scuttlebot API | `http://localhost:8080` |
6d0d615… lmata 331 | MCP server | `http://localhost:8081` |
6d0d615… lmata 332
6d0d615… lmata 333 ---
6d0d615… lmata 334
6d0d615… lmata 335 ## Common Commands
6d0d615… lmata 336
6d0d615… lmata 337 ```bash
5ac549c… lmata 338 # Dev helper (recommended)
5ac549c… lmata 339 ./run.sh # build + start
5ac549c… lmata 340 ./run.sh restart # rebuild + restart
5ac549c… lmata 341 ./run.sh stop # stop
5ac549c… lmata 342 ./run.sh token # print current API token
5ac549c… lmata 343 ./run.sh log # tail the log
5ac549c… lmata 344 ./run.sh test # go test ./...
5ac549c… lmata 345 ./run.sh e2e # Playwright e2e (requires scuttlebot running)
5ac549c… lmata 346
5ac549c… lmata 347 # Direct Go commands
6d0d615… lmata 348 go build ./cmd/scuttlebot # build daemon
6d0d615… lmata 349 go build ./cmd/scuttlectl # build CLI
6d0d615… lmata 350 go test ./... # run all tests
6d0d615… lmata 351 golangci-lint run # lint
5ac549c… lmata 352
5ac549c… lmata 353 # Admin CLI
a1cd907… noreply 354 scuttlectl status # server health
5ac549c… lmata 355 scuttlectl admin list # list admin accounts
5ac549c… lmata 356 scuttlectl admin add alice # add admin (prompts for password)
5ac549c… lmata 357 scuttlectl admin passwd alice # change password
5ac549c… lmata 358 scuttlectl admin remove alice # remove admin
a1cd907… noreply 359 scuttlectl api-key list # list API keys
a1cd907… noreply 360 scuttlectl api-key create --name "relay" --scopes chat,channels
a1cd907… noreply 361 scuttlectl api-key revoke <id> # revoke key
a1cd907… noreply 362 scuttlectl topology list # show channel types + static channels
a1cd907… noreply 363 scuttlectl topology provision #channel # create channel
a1cd907… noreply 364 scuttlectl topology drop #channel # remove channel
a1cd907… noreply 365 scuttlectl config show # dump config JSON
a1cd907… noreply 366 scuttlectl config history # config change history
a1cd907… noreply 367 scuttlectl bot list # show system bot status
a1cd907… noreply 368 scuttlectl agent list # list agents
a1cd907… noreply 369 scuttlectl agent register <nick> --type worker --channels #fleet
a1cd907… noreply 370 scuttlectl agent rotate <nick> # rotate credentials
a1cd907… noreply 371 scuttlectl backend list # LLM backends
5ac549c… lmata 372
5ac549c… lmata 373 # Docker
5ac549c… lmata 374 docker compose -f deploy/compose/docker-compose.yml up
5ac549c… lmata 375 ```
10b6d92… lmata 376
10b6d92… lmata 377 ## Optional: IRC Chatbot Agents
10b6d92… lmata 378
10b6d92… lmata 379 `cmd/claude-agent`, `cmd/codex-agent`, and `cmd/gemini-agent` are standalone IRC bots that connect to a channel and respond to prompts using an LLM backend. They are **not part of the default build** — they exist as a reference pattern for operators who want a persistent chatbot presence in a channel.
10b6d92… lmata 380
10b6d92… lmata 381 These are distinct from the relay brokers (`claude-relay`, `codex-relay`, `gemini-relay`). The difference:
10b6d92… lmata 382
10b6d92… lmata 383 | | Chatbot agent | Relay broker |
10b6d92… lmata 384 |---|---|---|
10b6d92… lmata 385 | Wraps a coding CLI | No | Yes |
10b6d92… lmata 386 | Reads/writes files, runs commands | No | Yes (via the CLI) |
10b6d92… lmata 387 | Always-on, responds to any mention | Yes | No — tied to an active session |
10b6d92… lmata 388 | Useful for fleet coordination | Novelty only | Core pattern |
10b6d92… lmata 389
10b6d92… lmata 390 The relay broker is the right tool for agent work. The chatbot agent is a nice-to-have for operators who want an LLM available in IRC for quick Q&A, but it cannot act — it can only respond.
10b6d92… lmata 391
10b6d92… lmata 392 ### Running one
10b6d92… lmata 393
10b6d92… lmata 394 ```bash
10b6d92… lmata 395 # Build (not included in make all)
10b6d92… lmata 396 make chatbots
10b6d92… lmata 397
10b6d92… lmata 398 # Register a nick in scuttlebot
10b6d92… lmata 399 TOKEN=$(./run.sh token)
10b6d92… lmata 400 curl -s -X POST -H "Authorization: Bearer $TOKEN" \
10b6d92… lmata 401 -H "Content-Type: application/json" \
10b6d92… lmata 402 -d '{"nick":"claude","type":"worker","channels":["#general"]}' \
10b6d92… lmata 403 http://localhost:8080/v1/agents/register
10b6d92… lmata 404
10b6d92… lmata 405 # Connect (use the passphrase from the register response)
10b6d92… lmata 406 bin/claude-agent --irc 127.0.0.1:6667 --nick claude --pass <passphrase> \
10b6d92… lmata 407 --api-url http://localhost:8080 --token $TOKEN --backend anthro
10b6d92… lmata 408 ```
10b6d92… lmata 409
10b6d92… lmata 410 Swap `claude-agent` → `codex-agent` (backend `openai`) or `gemini-agent` (backend `gemini`) for other providers. All three accept the same flags.

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button