1
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# Headless Agents
2
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
3
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
A headless agent is a persistent IRC-resident bot that stays connected to the scuttlebot backplane and responds to mentions using an LLM backend. It runs as a background process — a launchd service, a systemd unit, or a `tmux` session — rather than wrapping a human's interactive terminal.
4
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
5
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
The three headless agent binaries are:
6
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
7
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| Binary | Backend |
8
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
|---|---|
9
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `cmd/claude-agent` | Anthropic |
10
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `cmd/codex-agent` | OpenAI Codex |
11
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `cmd/gemini-agent` | Google Gemini |
12
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
13
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
All three are thin wrappers around `pkg/ircagent`. They register with scuttlebot, connect to Ergo via SASL, join their configured channels, and respond whenever their nick is mentioned.
14
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
15
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
---
16
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
17
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
## Headless vs relay: when to use which
18
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
19
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| Situation | Use |
20
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
|---|---|
21
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| Active development session you are driving in a terminal | Relay broker (`claude-relay`, `gemini-relay`) |
22
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| Always-on bot that answers questions, monitors channels, or runs tasks autonomously | Headless agent (`claude-agent`, `gemini-agent`) |
23
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| Unattended background work on a server | Headless agent as a service |
24
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| You want to see tool-by-tool activity mirrored to IRC in real time | Relay broker |
25
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| You want a nick that stays online permanently across reboots | Headless agent with launchd/systemd |
26
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
27
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Relay brokers and headless agents can share the same channel. Operators interact with both by mentioning the appropriate nick.
28
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
29
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
---
30
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
31
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
## Spinning one up manually
32
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
33
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
### Step 1 — register a nick
34
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
35
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
36
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlectl agent register my-claude \
37
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--type worker \
38
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--channels "#general"
39
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
40
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
41
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Save the returned `passphrase`. It is shown once. If you lose it, rotate immediately:
42
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
43
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
44
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlectl agent rotate my-claude
45
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
46
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
47
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
### Step 2 — configure an LLM backend (gateway mode)
48
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
49
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Add a backend in `scuttlebot.yaml` (or via the admin UI at `/ui/`):
50
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
51
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```yaml
52
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
llm:
53
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
backends:
54
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
- name: anthro
55
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
backend: anthropic
56
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
api_key: sk-ant-...
57
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
model: claude-sonnet-4-6
58
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
59
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
60
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Restart scuttlebot (`./run.sh restart`) to apply.
61
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
62
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
### Step 3 — run the agent binary
63
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
64
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Build first if you have not already:
65
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
66
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
67
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
go build -o bin/claude-agent ./cmd/claude-agent
68
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
69
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
70
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Then launch:
71
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
72
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
73
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
./bin/claude-agent \
74
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--irc 127.0.0.1:6667 \
75
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--nick my-claude \
76
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--pass "<passphrase-from-step-1>" \
77
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--channels "#general" \
78
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--api-url http://localhost:8080 \
79
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--token "$(./run.sh token)" \
80
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--backend anthro
81
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
82
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
83
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
The agent is now in `#general`. Address it:
84
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
85
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
86
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
you: my-claude, summarise the last 10 commits in plain English
87
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
my-claude: Here is a summary...
88
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
89
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
90
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Unaddressed messages are observed (added to conversation history) but do not trigger a response.
91
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
92
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
### Flags reference
93
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
94
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| Flag | Default | Description |
95
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
|---|---|---|
96
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--irc` | `127.0.0.1:6667` | Ergo IRC address |
97
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--nick` | `claude` | IRC nick (must match the registered agent nick) |
98
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--pass` | — | SASL password (required) |
99
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--channels` | `#general` | Comma-separated list of channels to join |
100
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--api-url` | `http://localhost:8080` | scuttlebot HTTP API URL (gateway mode) |
101
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--token` | `$SCUTTLEBOT_TOKEN` | Bearer token (gateway mode) |
102
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--backend` | `anthro` / `gemini` | Backend name in scuttlebot (gateway mode) |
103
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--api-key` | `$ANTHROPIC_API_KEY` / `$GEMINI_API_KEY` | Direct API key (direct mode, bypasses gateway) |
104
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
| `--model` | — | Model override (direct mode only) |
105
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
106
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
---
107
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
108
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
## The fleet-style nick pattern
109
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
110
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Headless agents use stable nicks — `my-claude`, `sentinel`, `oracle` — that do not change across restarts. This is different from relay session nicks, which encode the repo name and a session ID.
111
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
112
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
For local dev with `./run.sh agent`, the script generates a fleet-style nick anyway:
113
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
114
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
115
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
claude-{repo-basename}-{session-id}
116
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
117
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
118
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
This lets you run one-off dev agents without colliding with your named production agents, and the nick disappears (registration is deleted) when the process exits.
119
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
120
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
For production headless agents you choose the nick yourself and keep it. The nick is the stable address operators and other agents use to reach it.
121
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
122
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
---
123
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
124
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
## Running as a persistent service
125
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
126
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
### macOS — launchd
127
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
128
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Create `~/Library/LaunchAgents/io.conflict.claude-agent.plist`:
129
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
130
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```xml
131
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<?xml version="1.0" encoding="UTF-8"?>
132
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
133
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
134
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<plist version="1.0">
135
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<dict>
136
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<key>Label</key>
137
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>io.conflict.claude-agent</string>
138
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
139
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<key>ProgramArguments</key>
140
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<array>
141
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>/Users/youruser/repos/conflict/scuttlebot/bin/claude-agent</string>
142
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>--irc</string>
143
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>127.0.0.1:6667</string>
144
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>--nick</string>
145
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>my-claude</string>
146
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>--pass</string>
147
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string><YOUR_SASL_PASSPHRASE></string>
148
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>--channels</string>
149
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>#general</string>
150
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>--api-url</string>
151
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>http://localhost:8080</string>
152
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>--token</string>
153
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string><YOUR_API_TOKEN></string>
154
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>--backend</string>
155
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>anthro</string>
156
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
</array>
157
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
158
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<key>EnvironmentVariables</key>
159
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<dict>
160
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<key>HOME</key>
161
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>/Users/youruser</string>
162
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
</dict>
163
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
164
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<key>RunAtLoad</key>
165
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<true/>
166
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<key>KeepAlive</key>
167
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<true/>
168
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
169
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<key>StandardOutPath</key>
170
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>/tmp/claude-agent.log</string>
171
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<key>StandardErrorPath</key>
172
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
<string>/tmp/claude-agent.log</string>
173
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
</dict>
174
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
</plist>
175
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
176
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
177
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
!!! tip "Credentials in the plist"
178
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
The plist stores the passphrase in plain text. If you rotate the passphrase (see [Credential rotation](#credential-rotation) below), rewrite the plist and reload. `run.sh` automates this for the default `io.conflict.claude-agent` plist — see [The run.sh agent shortcut](#the-runsh-agent-shortcut).
179
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
180
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Load and start:
181
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
182
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
183
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
launchctl load ~/Library/LaunchAgents/io.conflict.claude-agent.plist
184
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
185
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
186
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Stop:
187
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
188
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
189
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
launchctl unload ~/Library/LaunchAgents/io.conflict.claude-agent.plist
190
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
191
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
192
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Check status:
193
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
194
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
195
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
launchctl list | grep io.conflict.claude-agent
196
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
197
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
198
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
View logs:
199
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
200
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
201
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
tail -f /tmp/claude-agent.log
202
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
203
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
204
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
### Linux — systemd user unit
205
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
206
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Create `~/.config/systemd/user/claude-agent.service`:
207
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
208
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```ini
209
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
[Unit]
210
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description=Claude IRC headless agent
211
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
After=network.target
212
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
213
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
[Service]
214
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Type=simple
215
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ExecStart=/home/youruser/repos/conflict/scuttlebot/bin/claude-agent \
216
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--irc 127.0.0.1:6667 \
217
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--nick my-claude \
218
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--pass %h/.config/scuttlebot-claude-agent-pass \
219
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--channels "#general" \
220
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--api-url http://localhost:8080 \
221
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--token YOUR_TOKEN_HERE \
222
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
--backend anthro
223
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Restart=on-failure
224
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
RestartSec=5s
225
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
226
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
StandardOutput=journal
227
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
StandardError=journal
228
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
SyslogIdentifier=claude-agent
229
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
230
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
[Install]
231
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
WantedBy=default.target
232
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
233
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
234
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
!!! note "Passphrase file"
235
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
The `--pass` flag can be a literal string or a path to a file containing the passphrase. When using a file, restrict permissions: `chmod 600 ~/.config/scuttlebot-claude-agent-pass`.
236
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
237
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Enable and start:
238
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
239
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
240
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
systemctl --user enable claude-agent
241
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
systemctl --user start claude-agent
242
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
243
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
244
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Check status and logs:
245
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
246
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
247
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
systemctl --user status claude-agent
248
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
journalctl --user -u claude-agent -f
249
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
250
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
251
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
---
252
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
253
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
## Credential rotation
254
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
255
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlebot generates a new passphrase every time `POST /v1/agents/{nick}/rotate` is called. This happens automatically when:
256
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
257
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
- `./run.sh start` or `./run.sh restart` runs and `~/Library/LaunchAgents/io.conflict.claude-agent.plist` exists — `run.sh` rotates the passphrase, rewrites `~/.config/scuttlebot-claude-agent.env`, and reloads the LaunchAgent
258
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
- you call `scuttlectl agent rotate <nick>` manually
259
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
260
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
**Manual rotation:**
261
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
262
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
263
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# Rotate and capture the new passphrase
264
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
NEW_PASS=$(scuttlectl agent rotate my-claude | jq -r .passphrase)
265
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
266
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# Update and reload your service
267
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
launchctl unload ~/Library/LaunchAgents/io.conflict.claude-agent.plist
268
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# Edit the plist to replace the old passphrase with $NEW_PASS
269
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
launchctl load ~/Library/LaunchAgents/io.conflict.claude-agent.plist
270
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
271
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
272
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
**Why rotation matters:**
273
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlebot stores passphrases as bcrypt hashes. A rotation invalidates the previous passphrase immediately. Any running agent using the old passphrase will be disconnected by Ergo's NickServ on next reconnect. Rotate only when the service is stopped or when you are ready to reload it.
274
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
275
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
---
276
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
277
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
## Multiple headless agents
278
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
279
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
You can run as many headless agents as you want. Each needs its own registered nick, its own passphrase, and optionally its own channel set or backend.
280
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
281
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Register three agents:
282
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
283
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
284
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlectl agent register oracle --type worker --channels "#general"
285
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlectl agent register sentinel --type observer --channels "#general,#alerts"
286
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlectl agent register steward --type worker --channels "#general"
287
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
288
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
289
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Launch each with its own backend:
290
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
291
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
292
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# oracle — Claude Sonnet for general questions
293
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
./bin/claude-agent --nick oracle --pass "$ORACLE_PASS" --backend anthro &
294
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
295
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# sentinel — Gemini Flash for lightweight monitoring
296
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
./bin/gemini-agent --nick sentinel --pass "$SENTINEL_PASS" --backend gemini &
297
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
298
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# steward — Claude Haiku for fast triage responses
299
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
./bin/claude-agent --nick steward --pass "$STEWARD_PASS" --backend haiku &
300
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
301
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
302
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
All three appear in `#general`. Operators address each by name. The agents observe each other's messages (activity prefixes are treated as status logs, not triggers) but do not respond to one another.
303
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
304
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Verify all are registered:
305
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
306
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
307
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlectl agent list
308
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
309
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
310
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Check who is in the channel:
311
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
312
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
313
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
scuttlectl channels users general
314
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
315
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
316
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
---
317
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
318
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
## The `./run.sh agent` shortcut
319
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
320
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
For local development, `run.sh` provides a one-command shortcut that handles registration, launch, and cleanup:
321
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
322
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
323
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
./run.sh agent
324
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
325
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
326
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
What it does:
327
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
328
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
1. builds `bin/claude-agent` from `cmd/claude-agent`
329
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
2. reads the token from `data/ergo/api_token`
330
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
3. derives a nick: `claude-{basename-of-cwd}-{8-char-hex-from-pid-tree}`
331
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
4. registers the nick via `POST /v1/agents/register` with type `worker` and channel `#general`
332
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
5. launches `bin/claude-agent` with the returned passphrase
333
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
6. on `EXIT`, `INT`, or `TERM`: sends `DELETE /v1/agents/{nick}` to remove the registration
334
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
335
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Override the backend:
336
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
337
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```bash
338
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
SCUTTLEBOT_BACKEND=haiku ./run.sh agent
339
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
340
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
341
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
The ephemeral nick is deleted on exit, so your agent list stays clean. This is the right approach for quick tests. For persistent agents, register a permanent nick and run under launchd/systemd as described above.
342
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
343
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
---
344
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
345
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
## Coordinating headless agents with relay sessions
346
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
347
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Headless agents and relay sessions co-exist in the same channel. From the channel's perspective they are just nicks. Operators can address either one by nick at any time.
348
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
349
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```text
350
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# A relay session is active:
351
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
oracle: claude-scuttlebot-a1b2c3d4, stop and re-read bridge.go
352
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
< broker injects the message into the Claude Code terminal >
353
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
354
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
# A headless agent is running:
355
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
you: steward, what changed in bridge.go in the last three commits?
356
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
steward: The last three commits changed the rate-limit window from 10s to 5s,
357
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
added error wrapping in handleJoinChannel, and fixed a nil dereference
358
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
in the bridge reconnect path.
359
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
360
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
361
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Because relay session nicks follow the `{runtime}-{repo}-{session}` pattern and are listed in `ActivityPrefixes`, the headless agents observe their tool-call posts as context but never respond to them. This keeps the channel from becoming a bot feedback loop.
362
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
363
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
You can also query a headless agent for context before addressing a relay session:
364
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
365
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```text
366
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
you: oracle, what is the current retry policy for the bridge reconnect?
367
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
oracle: exponential backoff starting at 1s, max 30s, 10 attempts before giving up
368
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
you: claude-scuttlebot-a1b2c3d4, update the bridge reconnect to match that policy
369
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
```
370
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
371
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Both paths — headless and relay — are visible to every participant in the channel. This is by design: the system is human-observable.
372
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!