Skip to content

Commit 66c085c

Browse files
jkyberneeesclaude
andcommitted
fix(docker): drop network_mode none (breaks LLM API call); align model example
Reassessment caught a correctness bug: the Godmode service used `network_mode: "none"`, but Odek must reach the LLM provider API on every turn, so a no-network container can't run at all. Remove it and reframe Godmode isolation as the container boundary + non-root + scoped ./workspace mount, with an egress-allowlist proxy noted as the advanced way to fence the agent's own network access. Also align the Anthropic .env example with the repo's documented claude-sonnet-4 profile. Applied consistently across docker/ and the guide. Verified approver wiring (WSApprover/TelegramApprover -> resolved.Dangerous) and that all config JSON keys/risk-class strings match the code. `docker compose config` passes for all four profiles. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 730756f commit 66c085c

4 files changed

Lines changed: 49 additions & 31 deletions

File tree

DOCKER_COMPOSE_USER_GUIDE.md

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ ODEK_SUPPRESS_SANDBOX_WARNING=1
123123
# ODEK_MODEL=gpt-4o
124124
# ODEK_BASE_URL=https://api.openai.com/v1
125125
126-
# Anthropic:
127-
# ODEK_MODEL=claude-opus-4-1
126+
# Anthropic (OpenAI-compatible endpoint; matches the claude-sonnet-4 profile):
127+
# ODEK_MODEL=claude-sonnet-4-5
128128
# ODEK_BASE_URL=https://api.anthropic.com/v1
129129
```
130130

@@ -196,8 +196,10 @@ configurable.
196196
```
197197

198198
> ⚠️ **Godmode gives the agent free rein inside the container.** Only use it with a
199-
> throwaway container, ideally with `network_mode: none` (see below) or a restricted
200-
> network, and never mount sensitive host paths or the Docker socket.
199+
> throwaway container, never mount sensitive host paths or the Docker socket, and keep
200+
> the only writable mount scoped to `./workspace`. (The container still needs outbound
201+
> network to reach the LLM API — see the notes under §6 — so isolation comes from the
202+
> container boundary, not from cutting the network.)
201203
202204
---
203205

@@ -224,14 +226,14 @@ services:
224226
- ./config.restricted.json:/home/odek/.odek/config.json:ro
225227
restart: "no"
226228

227-
# ── Godmode (all permissions) — non-interactive, sealed container ──
229+
# ── Godmode (all permissions) — non-interactive, disposable container ──
228230
odek-godmode:
229231
profiles: ["godmode"]
230232
build: .
231233
image: odek:local
232234
env_file: .env
233-
# No published ports. No network unless you need it (see notes).
234-
network_mode: "none"
235+
# No published ports (no inbound needed). Outbound networking stays on —
236+
# Odek must reach the LLM provider API to run.
235237
command: ["serve", "--addr", "0.0.0.0:8080", "--no-sandbox"]
236238
volumes:
237239
- ./workspace:/workspace
@@ -248,8 +250,11 @@ Notes:
248250
- The Web UI binds to `0.0.0.0:8080` *inside* the container; the `ports` mapping exposes
249251
it only on the host's `127.0.0.1`. Use a reverse proxy (Caddy/nginx) if you need remote
250252
access.
251-
- The Godmode service has `network_mode: "none"` so the agent cannot reach the network.
252-
Remove that line (or set a restricted network) if the task genuinely needs egress.
253+
- **Don't use `network_mode: "none"`.** Odek calls the LLM provider over the network every
254+
turn, so a no‑network container can't run at all. The container's isolation comes from
255+
the boundary itself, the non‑root user, and mounting only `./workspace`. To restrict the
256+
*agent's own* egress while still letting Odek reach the model, put it on a network behind
257+
an allowlisting egress proxy (advanced — out of scope here).
253258

254259
---
255260

@@ -301,7 +306,7 @@ docker compose run --rm -it \
301306

302307
## 8. Running — Godmode (all permissions)
303308

304-
No prompts, no human in the loop. Best for sealed, disposable containers.
309+
No prompts, no human in the loop. Best for disposable containers.
305310

306311
### One‑shot task
307312

@@ -315,9 +320,10 @@ docker compose --profile godmode run --rm odek-godmode \
315320
The trailing `run "<task>"` overrides the service's default `command:` (`serve`). No
316321
`--no-sandbox` is needed — `run` is unsandboxed by default.
317322

318-
Every command the agent issues runs immediately. Because `network_mode: "none"` is set,
319-
nothing can leave the container; everything is confined to `./workspace` on your host (the
320-
only writable mount) and the container's ephemeral filesystem.
323+
Every command the agent issues runs immediately. The blast radius is the container: the
324+
only writable host mount is `./workspace`, everything else is the container's ephemeral
325+
filesystem, and it runs as a non‑root user. (The container does have outbound network —
326+
Odek needs it to reach the LLM — so this is isolation by *boundary*, not by airgap.)
321327

322328
### Long‑running / Web UI
323329

@@ -327,9 +333,9 @@ If you want the Web UI in Godmode too (e.g. a personal automation box):
327333
docker compose --profile godmode up --build
328334
```
329335

330-
Then add a port mapping to the `odek-godmode` service and remove `network_mode: none` if
331-
the agent needs the network. **Only do this on a trusted host** — in Godmode the UI grants
332-
unrestricted command execution inside the container.
336+
Then add a `ports:` mapping to the `odek-godmode` service so you can reach the UI. **Only
337+
do this on a trusted host** — in Godmode the UI grants unrestricted command execution
338+
inside the container.
333339

334340
---
335341

@@ -387,7 +393,9 @@ global `action` → built‑in defaults. The `blocked` class is always denied re
387393
SSH keys, cloud credentials, or `/var/run/docker.sock`.
388394
- ✅ Keep the Web UI bound to `127.0.0.1` on the host; front it with an authenticated
389395
reverse proxy for any remote access.
390-
- ✅ Prefer `network_mode: "none"` (or a tightly scoped network) — especially for Godmode.
396+
- ✅ Remember the container needs **outbound** network for the LLM API, so `network_mode:
397+
none` isn't an option. To fence the agent's *own* egress, use a firewalled network or an
398+
allowlisting egress proxy rather than relying on Docker's network mode.
391399
- ✅ Treat **Godmode containers as disposable**: `--rm`, no persistent secrets beyond the
392400
injected API key, throwaway `workspace/`.
393401
- ✅ Keep `.env` out of version control.
@@ -462,7 +470,7 @@ restarts. No `ports` are needed.
462470
- odek-tg-state:/home/odek/.odek
463471
restart: unless-stopped
464472
465-
# ── Telegram bot — Godmode (no prompts; sealed container) ──
473+
# ── Telegram bot — Godmode (no prompts; disposable container) ──
466474
odek-telegram-godmode:
467475
profiles: ["telegram-godmode"]
468476
build: .
@@ -498,7 +506,7 @@ Message your bot: `/start`, then try a task. When the agent hits a `prompt`‑cl
498506
you'll get an inline keyboard — tap **Approve**, **Deny**, or **Trust** (trust = allow that
499507
risk class for the rest of the session).
500508

501-
**Godmode** (no prompts — only on a trusted host, ideally with restricted egress):
509+
**Godmode** (no prompts — only on a trusted host):
502510

503511
```bash
504512
docker compose --profile telegram-godmode up --build -d

docker/.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ ODEK_BASE_URL=https://api.deepseek.com/v1
1414
# ODEK_MODEL=gpt-4o
1515
# ODEK_BASE_URL=https://api.openai.com/v1
1616

17-
# Anthropic:
18-
# ODEK_MODEL=claude-opus-4-1
17+
# Anthropic (OpenAI-compatible endpoint; matches the claude-sonnet-4 profile):
18+
# ODEK_MODEL=claude-sonnet-4-5
1919
# ODEK_BASE_URL=https://api.anthropic.com/v1
2020

2121
# We run unsandboxed on purpose (the container IS the boundary), so silence

docker/README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,16 +59,21 @@ docker compose run --rm -it odek-restricted repl
5959

6060
### Godmode (all permissions)
6161

62-
No prompts. Best for sealed, disposable containers. One-shot task:
62+
No prompts. Best for disposable containers. One-shot task:
6363

6464
```bash
6565
docker compose --profile godmode run --rm odek-godmode \
6666
run "Create build.sh, make it executable, and run it."
6767
```
6868

69-
The trailing `run "<task>"` overrides the service's default `serve` command. The
70-
service sets `network_mode: none`, so nothing leaves the container — remove that
71-
line in `docker-compose.yml` if the task genuinely needs network egress.
69+
The trailing `run "<task>"` overrides the service's default `serve` command.
70+
71+
> **The container has outbound network** — Odek must reach the LLM provider API,
72+
> so `network_mode: none` is *not* an option here (it would break the model call).
73+
> Isolation comes from the container boundary, the non-root user, and mounting only
74+
> `./workspace`. To also fence the agent's *own* egress while still letting Odek
75+
> reach the model, run it on a network behind an allowlisting egress proxy — that's
76+
> an advanced setup beyond these examples.
7277
7378
### Telegram bot
7479

@@ -130,5 +135,7 @@ container after editing (`... up` again) since the config is mounted at startup.
130135
keys, cloud creds, or `/var/run/docker.sock`.
131136
- Keep the Web UI on `127.0.0.1`; front it with an authenticated reverse proxy for
132137
remote access.
133-
- Prefer `network_mode: none` (or a scoped network), especially for Godmode.
138+
- The container needs outbound network for the LLM API, so don't rely on
139+
`network_mode: none` for isolation. To restrict the agent's own egress, front it
140+
with an allowlisting proxy / firewalled network (advanced).
134141
- `.env` and `workspace/` are gitignored — never commit secrets or scratch files.

docker/docker-compose.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#
66
# Profiles (opt into exactly one at a time):
77
# restricted Web UI + approval prompts (recommended)
8-
# godmode No prompts; sealed container
8+
# godmode No prompts; disposable container
99
# telegram-restricted Telegram bot; approvals via inline keyboards
1010
# telegram-godmode Telegram bot; no prompts
1111
#
@@ -31,16 +31,19 @@ services:
3131
- ./config.restricted.json:/home/odek/.odek/config.json:ro
3232
restart: "no"
3333

34-
# ── Godmode (all permissions) — non-interactive, sealed container ──
34+
# ── Godmode (all permissions) — non-interactive, disposable container ──
3535
odek-godmode:
3636
profiles: ["godmode"]
3737
build:
3838
context: ..
3939
dockerfile: docker/Dockerfile
4040
image: odek:local
4141
env_file: .env
42-
# No published ports. No network unless you need it (remove network_mode).
43-
network_mode: "none"
42+
# No published ports (no inbound needed). The container keeps default
43+
# outbound networking — Odek MUST reach the LLM provider API to run.
44+
# Isolation here is the container boundary + non-root + only ./workspace
45+
# mounted, NOT network restriction. To also fence the agent's own egress,
46+
# put it on a network behind an allowlisting proxy (advanced; see README).
4447
command: ["serve", "--addr", "0.0.0.0:8080", "--no-sandbox"]
4548
volumes:
4649
- ./workspace:/workspace
@@ -62,7 +65,7 @@ services:
6265
- odek-tg-state:/home/odek/.odek
6366
restart: unless-stopped
6467

65-
# ── Telegram bot — Godmode (no prompts; sealed container) ──
68+
# ── Telegram bot — Godmode (no prompts; disposable container) ──
6669
odek-telegram-godmode:
6770
profiles: ["telegram-godmode"]
6871
build:

0 commit comments

Comments
 (0)