ScuttleBot

feat: scuttlectl topology/config/bot commands, bootstrap.md refresh (#104, #101, #102) scuttlectl: add topology (list/provision/drop), config (show/history), and bot (list) subcommands. All backed by existing API endpoints. bootstrap.md: update bot count to 10 (add sentinel + steward), expand API endpoint table from 13 to 35+ with scope annotations, document API key system, add scuttlectl topology/config/bot/api-key commands, update persistence table, update "Adding a New Bot" checklist.

lmata 2026-04-05 15:23 trunk
Commit c77ae38a39b0ed67f58f76e2cd663faff5a011267b67c48c1d3911a7c09b939f
1 file changed +78 -30
+78 -30
--- bootstrap.md
+++ bootstrap.md
@@ -157,24 +157,26 @@
157157
- `+v` (voice) — trusted worker agents
158158
- no mode — standard agents
159159
160160
### Built-in bots
161161
162
-All 8 bots are implemented. Enabled/configured via the web UI or `scuttlectl`. The manager (`internal/bots/manager/`) starts/stops them dynamically when policies change.
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.
163163
164164
| Bot | Nick | Role |
165165
|-----|------|------|
166166
| `auditbot` | auditbot | Immutable append-only audit trail of agent actions and credential events |
167167
| `herald` | herald | Routes inbound webhook events to IRC channels |
168168
| `oracle` | oracle | On-demand channel summarization via DM — calls any OpenAI-compatible LLM |
169169
| `scribe` | scribe | Structured message logging to rotating files (jsonl/csv/text) |
170
-| `scroll` | scroll | History replay to PM on request |
171
-| `snitch` | snitch | Flood and join/part cycling detection — alerts operators via DM or channel |
170
+| `scroll` | scroll | History replay to PM on request (`replay #channel [format=toon]`) |
171
+| `sentinel` | sentinel | LLM-powered channel observer — detects policy violations, posts structured incident reports to mod channel. Never takes enforcement action. |
172
+| `snitch` | snitch | Flood and join/part cycling detection, MONITOR-based presence tracking, away-notify alerts |
173
+| `steward` | steward | Acts on sentinel incident reports — issues warnings, mutes (extended ban `m:`), or kicks based on severity |
172174
| `systembot` | systembot | Logs IRC system events (joins, parts, quits, mode changes) |
173
-| `warden` | warden | Channel moderation — warn → mute → kick on flood |
175
+| `warden` | warden | Channel moderation — warn → mute (extended ban) → kick on flood |
174176
175
-Oracle reads history from scribe's log files (pointed at the same dir). 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.
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.
176178
177179
### Scale
178180
179181
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.
180182
@@ -186,11 +188,12 @@
186188
|------|------|-------|
187189
| Agent registry | `data/ergo/registry.json` | Agent records + SASL credentials |
188190
| Admin accounts | `data/ergo/admins.json` | bcrypt-hashed; created by `scuttlectl admin add` |
189191
| Policies | `data/ergo/policies.json` | Bot config, agent policy, logging settings |
190192
| Bot passwords | `data/ergo/bot_passwords.json` | Auto-generated SASL passwords for system bots |
191
-| API token | `data/ergo/api_token` | Bearer token for API auth; stable across restarts |
193
+| API token | `data/ergo/api_token` | Legacy token; migrated to api_keys.json on first run |
194
+| API keys | `data/ergo/api_keys.json` | Per-consumer tokens with scoped permissions (SHA-256 hashed) |
192195
| Ergo state | `data/ergo/ircd.db` | Ergo-native: accounts, channels, topics, history |
193196
| scribe logs | `data/logs/scribe/` | Rotating log files (jsonl/csv/text); configurable |
194197
195198
K8s / Docker: mount a PersistentVolume at `data/`. Ergo is single-instance — HA = fast pod restart with durable storage, not horizontal scaling.
196199
@@ -228,48 +231,79 @@
228231
`internal/api/` — two-mux pattern:
229232
230233
- **Outer mux** (unauthenticated): `POST /login`, `GET /` (redirect), `GET /ui/` (web UI)
231234
- **Inner mux** (`/v1/` routes): require `Authorization: Bearer <token>` header
232235
233
-The API token is a random hex string generated once at startup, persisted to `data/ergo/api_token`.
234
-
235236
### Auth
236237
237
-`POST /login` accepts `{username, password}` and returns `{token, username}`. The token is the shared server API token. Rate limited to 10 attempts per minute per IP.
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.
239
+
240
+`POST /login` accepts `{username, password}` and returns a 24h session token with admin scope. Rate limited to 10 attempts per minute per IP.
241
+
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.
238243
239
-Admin accounts are managed via `scuttlectl admin` or the web UI settings → admin accounts card. First run auto-creates an `admin` account with a random password printed to the log.
244
+Admin accounts managed via `scuttlectl admin` or web UI. First run auto-creates `admin` with a random password printed to the log.
240245
241246
### Key endpoints
242247
243
-| Method | Path | Description |
244
-|--------|------|-------------|
245
-| `POST` | `/login` | Username/password login (unauthenticated) |
246
-| `GET` | `/v1/status` | Server status |
247
-| `GET` | `/v1/metrics` | Runtime metrics + bridge stats |
248
-| `GET/PUT` | `/v1/settings/policies` | Bot config, agent policy, logging |
249
-| `GET` | `/v1/agents` | List all registered agents |
250
-| `POST` | `/v1/agents/register` | Register an agent |
251
-| `POST` | `/v1/agents/{nick}/rotate` | Rotate credentials |
252
-| `POST` | `/v1/agents/{nick}/revoke` | Revoke agent |
253
-| `GET` | `/v1/channels` | List joined channels |
254
-| `GET` | `/v1/channels/{ch}/stream` | SSE stream of channel messages |
255
-| `GET/POST` | `/v1/admins` | List / add admin accounts |
256
-| `DELETE` | `/v1/admins/{username}` | Remove admin |
257
-| `PUT` | `/v1/admins/{username}/password` | Change password |
248
+All `/v1/` endpoints require a Bearer token with the appropriate scope.
249
+
250
+| Method | Path | Scope | Description |
251
+|--------|------|-------|-------------|
252
+| `POST` | `/login` | — | Username/password login (unauthenticated) |
253
+| `GET` | `/v1/status` | read | Server status |
254
+| `GET` | `/v1/metrics` | read | Runtime metrics + bridge stats |
255
+| `GET` | `/v1/settings` | read | Full settings (policies, TLS, bot commands) |
256
+| `GET/PUT/PATCH` | `/v1/settings/policies` | admin | Bot config, agent policy, logging |
257
+| `GET` | `/v1/agents` | agents | List all registered agents |
258
+| `GET` | `/v1/agents/{nick}` | agents | Get single agent |
259
+| `PATCH` | `/v1/agents/{nick}` | agents | Update agent |
260
+| `POST` | `/v1/agents/register` | agents | Register an agent |
261
+| `POST` | `/v1/agents/{nick}/rotate` | agents | Rotate credentials |
262
+| `POST` | `/v1/agents/{nick}/adopt` | agents | Adopt existing IRC nick |
263
+| `POST` | `/v1/agents/{nick}/revoke` | agents | Revoke agent credentials |
264
+| `DELETE` | `/v1/agents/{nick}` | agents | Delete agent |
265
+| `GET` | `/v1/channels` | channels | List joined channels |
266
+| `POST` | `/v1/channels/{ch}/join` | channels | Join channel |
267
+| `DELETE` | `/v1/channels/{ch}` | channels | Leave channel |
268
+| `GET` | `/v1/channels/{ch}/messages` | channels | Get message history |
269
+| `POST` | `/v1/channels/{ch}/messages` | chat | Send message |
270
+| `POST` | `/v1/channels/{ch}/presence` | chat | Touch presence (keep web user visible) |
271
+| `GET` | `/v1/channels/{ch}/users` | channels | User list with IRC modes |
272
+| `GET` | `/v1/channels/{ch}/config` | channels | Per-channel display config |
273
+| `PUT` | `/v1/channels/{ch}/config` | channels | Set display config (mirror detail, render mode) |
274
+| `GET` | `/v1/channels/{ch}/stream` | channels | SSE stream (`?token=` query param auth) |
275
+| `POST` | `/v1/channels` | topology | Provision channel via ChanServ |
276
+| `DELETE` | `/v1/topology/channels/{ch}` | topology | Drop channel |
277
+| `GET` | `/v1/topology` | topology | Channel types, static channels, active channels |
278
+| `GET/PUT` | `/v1/config` | config | Server config read/write |
279
+| `GET` | `/v1/config/history` | config | Config change history |
280
+| `GET/POST` | `/v1/admins` | admin | List / add admin accounts |
281
+| `DELETE` | `/v1/admins/{username}` | admin | Remove admin |
282
+| `PUT` | `/v1/admins/{username}/password` | admin | Change password |
283
+| `GET/POST` | `/v1/api-keys` | admin | List / create API keys |
284
+| `DELETE` | `/v1/api-keys/{id}` | admin | Revoke API key |
285
+| `GET/POST/PUT/DELETE` | `/v1/llm/backends[/{name}]` | bots | LLM backend CRUD |
286
+| `GET` | `/v1/llm/backends/{name}/models` | bots | List models for backend |
287
+| `POST` | `/v1/llm/discover` | bots | Discover models from provider |
288
+| `POST` | `/v1/llm/complete` | bots | LLM completion proxy |
258289
259290
---
260291
261292
## Adding a New Bot
262293
263294
1. Create `internal/bots/{name}/` package with a `Bot` struct and `Start(ctx context.Context) error` method
264
-2. Add a `BotSpec` config struct if the bot needs user-configurable settings
265
-3. Register in `internal/bots/manager/manager.go`:
295
+2. Set `+B` user mode on connect, handle INVITE for auto-join
296
+3. Add a `BotSpec` config struct if the bot needs user-configurable settings
297
+4. Register in `internal/bots/manager/manager.go`:
266298
- Add a case to `buildBot()` that constructs your bot from the spec config
267299
- Add a `BehaviorConfig` entry to `defaultBehaviors` in `internal/api/policies.go`
268
-4. Add the UI config schema to `BEHAVIOR_SCHEMAS` in `internal/api/ui/index.html`
269
-5. Write tests: bot logic, config parsing, edge cases. IRC connection can be skipped in unit tests.
270
-6. Update this bootstrap
300
+5. Add commands to `botCommands` map in `internal/api/policies.go` for the web UI command reference
301
+6. Add the UI config schema to `BEHAVIOR_SCHEMAS` in `internal/api/ui/index.html`
302
+7. Use `internal/bots/cmdparse/` for command routing if the bot accepts DM commands
303
+8. Write tests: bot logic, config parsing, edge cases. IRC connection can be skipped in unit tests.
304
+9. Update this bootstrap
271305
272306
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):
273307
274308
```go
275309
type bot interface {
@@ -315,14 +349,28 @@
315349
go build ./cmd/scuttlectl # build CLI
316350
go test ./... # run all tests
317351
golangci-lint run # lint
318352
319353
# Admin CLI
354
+scuttlectl status # server health
320355
scuttlectl admin list # list admin accounts
321356
scuttlectl admin add alice # add admin (prompts for password)
322357
scuttlectl admin passwd alice # change password
323358
scuttlectl admin remove alice # remove admin
359
+scuttlectl api-key list # list API keys
360
+scuttlectl api-key create --name "relay" --scopes chat,channels
361
+scuttlectl api-key revoke <id> # revoke key
362
+scuttlectl topology list # show channel types + static channels
363
+scuttlectl topology provision #channel # create channel
364
+scuttlectl topology drop #channel # remove channel
365
+scuttlectl config show # dump config JSON
366
+scuttlectl config history # config change history
367
+scuttlectl bot list # show system bot status
368
+scuttlectl agent list # list agents
369
+scuttlectl agent register <nick> --type worker --channels #fleet
370
+scuttlectl agent rotate <nick> # rotate credentials
371
+scuttlectl backend list # LLM backends
324372
325373
# Docker
326374
docker compose -f deploy/compose/docker-compose.yml up
327375
```
328376
329377
--- bootstrap.md
+++ bootstrap.md
@@ -157,24 +157,26 @@
157 - `+v` (voice) — trusted worker agents
158 - no mode — standard agents
159
160 ### Built-in bots
161
162 All 8 bots are implemented. Enabled/configured via the web UI or `scuttlectl`. The manager (`internal/bots/manager/`) starts/stops them dynamically when policies change.
163
164 | Bot | Nick | Role |
165 |-----|------|------|
166 | `auditbot` | auditbot | Immutable append-only audit trail of agent actions and credential events |
167 | `herald` | herald | Routes inbound webhook events to IRC channels |
168 | `oracle` | oracle | On-demand channel summarization via DM — calls any OpenAI-compatible LLM |
169 | `scribe` | scribe | Structured message logging to rotating files (jsonl/csv/text) |
170 | `scroll` | scroll | History replay to PM on request |
171 | `snitch` | snitch | Flood and join/part cycling detection — alerts operators via DM or channel |
 
 
172 | `systembot` | systembot | Logs IRC system events (joins, parts, quits, mode changes) |
173 | `warden` | warden | Channel moderation — warn → mute → kick on flood |
174
175 Oracle reads history from scribe's log files (pointed at the same dir). 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.
176
177 ### Scale
178
179 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.
180
@@ -186,11 +188,12 @@
186 |------|------|-------|
187 | Agent registry | `data/ergo/registry.json` | Agent records + SASL credentials |
188 | Admin accounts | `data/ergo/admins.json` | bcrypt-hashed; created by `scuttlectl admin add` |
189 | Policies | `data/ergo/policies.json` | Bot config, agent policy, logging settings |
190 | Bot passwords | `data/ergo/bot_passwords.json` | Auto-generated SASL passwords for system bots |
191 | API token | `data/ergo/api_token` | Bearer token for API auth; stable across restarts |
 
192 | Ergo state | `data/ergo/ircd.db` | Ergo-native: accounts, channels, topics, history |
193 | scribe logs | `data/logs/scribe/` | Rotating log files (jsonl/csv/text); configurable |
194
195 K8s / Docker: mount a PersistentVolume at `data/`. Ergo is single-instance — HA = fast pod restart with durable storage, not horizontal scaling.
196
@@ -228,48 +231,79 @@
228 `internal/api/` — two-mux pattern:
229
230 - **Outer mux** (unauthenticated): `POST /login`, `GET /` (redirect), `GET /ui/` (web UI)
231 - **Inner mux** (`/v1/` routes): require `Authorization: Bearer <token>` header
232
233 The API token is a random hex string generated once at startup, persisted to `data/ergo/api_token`.
234
235 ### Auth
236
237 `POST /login` accepts `{username, password}` and returns `{token, username}`. The token is the shared server API token. Rate limited to 10 attempts per minute per IP.
 
 
 
 
238
239 Admin accounts are managed via `scuttlectl admin` or the web UI settings → admin accounts card. First run auto-creates an `admin` account with a random password printed to the log.
240
241 ### Key endpoints
242
243 | Method | Path | Description |
244 |--------|------|-------------|
245 | `POST` | `/login` | Username/password login (unauthenticated) |
246 | `GET` | `/v1/status` | Server status |
247 | `GET` | `/v1/metrics` | Runtime metrics + bridge stats |
248 | `GET/PUT` | `/v1/settings/policies` | Bot config, agent policy, logging |
249 | `GET` | `/v1/agents` | List all registered agents |
250 | `POST` | `/v1/agents/register` | Register an agent |
251 | `POST` | `/v1/agents/{nick}/rotate` | Rotate credentials |
252 | `POST` | `/v1/agents/{nick}/revoke` | Revoke agent |
253 | `GET` | `/v1/channels` | List joined channels |
254 | `GET` | `/v1/channels/{ch}/stream` | SSE stream of channel messages |
255 | `GET/POST` | `/v1/admins` | List / add admin accounts |
256 | `DELETE` | `/v1/admins/{username}` | Remove admin |
257 | `PUT` | `/v1/admins/{username}/password` | Change password |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
259 ---
260
261 ## Adding a New Bot
262
263 1. Create `internal/bots/{name}/` package with a `Bot` struct and `Start(ctx context.Context) error` method
264 2. Add a `BotSpec` config struct if the bot needs user-configurable settings
265 3. Register in `internal/bots/manager/manager.go`:
 
266 - Add a case to `buildBot()` that constructs your bot from the spec config
267 - Add a `BehaviorConfig` entry to `defaultBehaviors` in `internal/api/policies.go`
268 4. Add the UI config schema to `BEHAVIOR_SCHEMAS` in `internal/api/ui/index.html`
269 5. Write tests: bot logic, config parsing, edge cases. IRC connection can be skipped in unit tests.
270 6. Update this bootstrap
 
 
271
272 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):
273
274 ```go
275 type bot interface {
@@ -315,14 +349,28 @@
315 go build ./cmd/scuttlectl # build CLI
316 go test ./... # run all tests
317 golangci-lint run # lint
318
319 # Admin CLI
 
320 scuttlectl admin list # list admin accounts
321 scuttlectl admin add alice # add admin (prompts for password)
322 scuttlectl admin passwd alice # change password
323 scuttlectl admin remove alice # remove admin
 
 
 
 
 
 
 
 
 
 
 
 
 
324
325 # Docker
326 docker compose -f deploy/compose/docker-compose.yml up
327 ```
328
329
--- bootstrap.md
+++ bootstrap.md
@@ -157,24 +157,26 @@
157 - `+v` (voice) — trusted worker agents
158 - no mode — standard agents
159
160 ### Built-in bots
161
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.
163
164 | Bot | Nick | Role |
165 |-----|------|------|
166 | `auditbot` | auditbot | Immutable append-only audit trail of agent actions and credential events |
167 | `herald` | herald | Routes inbound webhook events to IRC channels |
168 | `oracle` | oracle | On-demand channel summarization via DM — calls any OpenAI-compatible LLM |
169 | `scribe` | scribe | Structured message logging to rotating files (jsonl/csv/text) |
170 | `scroll` | scroll | History replay to PM on request (`replay #channel [format=toon]`) |
171 | `sentinel` | sentinel | LLM-powered channel observer — detects policy violations, posts structured incident reports to mod channel. Never takes enforcement action. |
172 | `snitch` | snitch | Flood and join/part cycling detection, MONITOR-based presence tracking, away-notify alerts |
173 | `steward` | steward | Acts on sentinel incident reports — issues warnings, mutes (extended ban `m:`), or kicks based on severity |
174 | `systembot` | systembot | Logs IRC system events (joins, parts, quits, mode changes) |
175 | `warden` | warden | Channel moderation — warn → mute (extended ban) → kick on flood |
176
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.
178
179 ### Scale
180
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.
182
@@ -186,11 +188,12 @@
188 |------|------|-------|
189 | Agent registry | `data/ergo/registry.json` | Agent records + SASL credentials |
190 | Admin accounts | `data/ergo/admins.json` | bcrypt-hashed; created by `scuttlectl admin add` |
191 | Policies | `data/ergo/policies.json` | Bot config, agent policy, logging settings |
192 | Bot passwords | `data/ergo/bot_passwords.json` | Auto-generated SASL passwords for system bots |
193 | API token | `data/ergo/api_token` | Legacy token; migrated to api_keys.json on first run |
194 | API keys | `data/ergo/api_keys.json` | Per-consumer tokens with scoped permissions (SHA-256 hashed) |
195 | Ergo state | `data/ergo/ircd.db` | Ergo-native: accounts, channels, topics, history |
196 | scribe logs | `data/logs/scribe/` | Rotating log files (jsonl/csv/text); configurable |
197
198 K8s / Docker: mount a PersistentVolume at `data/`. Ergo is single-instance — HA = fast pod restart with durable storage, not horizontal scaling.
199
@@ -228,48 +231,79 @@
231 `internal/api/` — two-mux pattern:
232
233 - **Outer mux** (unauthenticated): `POST /login`, `GET /` (redirect), `GET /ui/` (web UI)
234 - **Inner mux** (`/v1/` routes): require `Authorization: Bearer <token>` header
235
 
 
236 ### Auth
237
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.
239
240 `POST /login` accepts `{username, password}` and returns a 24h session token with admin scope. Rate limited to 10 attempts per minute per IP.
241
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.
243
244 Admin accounts managed via `scuttlectl admin` or web UI. First run auto-creates `admin` with a random password printed to the log.
245
246 ### Key endpoints
247
248 All `/v1/` endpoints require a Bearer token with the appropriate scope.
249
250 | Method | Path | Scope | Description |
251 |--------|------|-------|-------------|
252 | `POST` | `/login` | — | Username/password login (unauthenticated) |
253 | `GET` | `/v1/status` | read | Server status |
254 | `GET` | `/v1/metrics` | read | Runtime metrics + bridge stats |
255 | `GET` | `/v1/settings` | read | Full settings (policies, TLS, bot commands) |
256 | `GET/PUT/PATCH` | `/v1/settings/policies` | admin | Bot config, agent policy, logging |
257 | `GET` | `/v1/agents` | agents | List all registered agents |
258 | `GET` | `/v1/agents/{nick}` | agents | Get single agent |
259 | `PATCH` | `/v1/agents/{nick}` | agents | Update agent |
260 | `POST` | `/v1/agents/register` | agents | Register an agent |
261 | `POST` | `/v1/agents/{nick}/rotate` | agents | Rotate credentials |
262 | `POST` | `/v1/agents/{nick}/adopt` | agents | Adopt existing IRC nick |
263 | `POST` | `/v1/agents/{nick}/revoke` | agents | Revoke agent credentials |
264 | `DELETE` | `/v1/agents/{nick}` | agents | Delete agent |
265 | `GET` | `/v1/channels` | channels | List joined channels |
266 | `POST` | `/v1/channels/{ch}/join` | channels | Join channel |
267 | `DELETE` | `/v1/channels/{ch}` | channels | Leave channel |
268 | `GET` | `/v1/channels/{ch}/messages` | channels | Get message history |
269 | `POST` | `/v1/channels/{ch}/messages` | chat | Send message |
270 | `POST` | `/v1/channels/{ch}/presence` | chat | Touch presence (keep web user visible) |
271 | `GET` | `/v1/channels/{ch}/users` | channels | User list with IRC modes |
272 | `GET` | `/v1/channels/{ch}/config` | channels | Per-channel display config |
273 | `PUT` | `/v1/channels/{ch}/config` | channels | Set display config (mirror detail, render mode) |
274 | `GET` | `/v1/channels/{ch}/stream` | channels | SSE stream (`?token=` query param auth) |
275 | `POST` | `/v1/channels` | topology | Provision channel via ChanServ |
276 | `DELETE` | `/v1/topology/channels/{ch}` | topology | Drop channel |
277 | `GET` | `/v1/topology` | topology | Channel types, static channels, active channels |
278 | `GET/PUT` | `/v1/config` | config | Server config read/write |
279 | `GET` | `/v1/config/history` | config | Config change history |
280 | `GET/POST` | `/v1/admins` | admin | List / add admin accounts |
281 | `DELETE` | `/v1/admins/{username}` | admin | Remove admin |
282 | `PUT` | `/v1/admins/{username}/password` | admin | Change password |
283 | `GET/POST` | `/v1/api-keys` | admin | List / create API keys |
284 | `DELETE` | `/v1/api-keys/{id}` | admin | Revoke API key |
285 | `GET/POST/PUT/DELETE` | `/v1/llm/backends[/{name}]` | bots | LLM backend CRUD |
286 | `GET` | `/v1/llm/backends/{name}/models` | bots | List models for backend |
287 | `POST` | `/v1/llm/discover` | bots | Discover models from provider |
288 | `POST` | `/v1/llm/complete` | bots | LLM completion proxy |
289
290 ---
291
292 ## Adding a New Bot
293
294 1. Create `internal/bots/{name}/` package with a `Bot` struct and `Start(ctx context.Context) error` method
295 2. Set `+B` user mode on connect, handle INVITE for auto-join
296 3. Add a `BotSpec` config struct if the bot needs user-configurable settings
297 4. Register in `internal/bots/manager/manager.go`:
298 - Add a case to `buildBot()` that constructs your bot from the spec config
299 - Add a `BehaviorConfig` entry to `defaultBehaviors` in `internal/api/policies.go`
300 5. Add commands to `botCommands` map in `internal/api/policies.go` for the web UI command reference
301 6. Add the UI config schema to `BEHAVIOR_SCHEMAS` in `internal/api/ui/index.html`
302 7. Use `internal/bots/cmdparse/` for command routing if the bot accepts DM commands
303 8. Write tests: bot logic, config parsing, edge cases. IRC connection can be skipped in unit tests.
304 9. Update this bootstrap
305
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):
307
308 ```go
309 type bot interface {
@@ -315,14 +349,28 @@
349 go build ./cmd/scuttlectl # build CLI
350 go test ./... # run all tests
351 golangci-lint run # lint
352
353 # Admin CLI
354 scuttlectl status # server health
355 scuttlectl admin list # list admin accounts
356 scuttlectl admin add alice # add admin (prompts for password)
357 scuttlectl admin passwd alice # change password
358 scuttlectl admin remove alice # remove admin
359 scuttlectl api-key list # list API keys
360 scuttlectl api-key create --name "relay" --scopes chat,channels
361 scuttlectl api-key revoke <id> # revoke key
362 scuttlectl topology list # show channel types + static channels
363 scuttlectl topology provision #channel # create channel
364 scuttlectl topology drop #channel # remove channel
365 scuttlectl config show # dump config JSON
366 scuttlectl config history # config change history
367 scuttlectl bot list # show system bot status
368 scuttlectl agent list # list agents
369 scuttlectl agent register <nick> --type worker --channels #fleet
370 scuttlectl agent rotate <nick> # rotate credentials
371 scuttlectl backend list # LLM backends
372
373 # Docker
374 docker compose -f deploy/compose/docker-compose.yml up
375 ```
376
377

Keyboard Shortcuts

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