|
| 1 | +# Odek — Docker Compose examples |
| 2 | + |
| 3 | +Ready-to-run Compose setup for Odek in two permission profiles: |
| 4 | + |
| 5 | +| Profile | Meaning | Use for | |
| 6 | +| --- | --- | --- | |
| 7 | +| **Restricted** (default) | Commands are risk-classified; destructive ones denied, the rest require approval. | Day-to-day use, untrusted tasks, human-in-the-loop. | |
| 8 | +| **Godmode** (all permissions) | "YOLO" mode — every risk class auto-allowed (except a hardcoded blocklist like fork bombs). No prompts. | Sealed, throwaway containers / CI. | |
| 9 | + |
| 10 | +> **Why no `--sandbox`?** Odek's own `--sandbox` flag spawns *nested* Docker |
| 11 | +> containers. Here the Compose container **is** the sandbox, so commands run |
| 12 | +> directly inside it. The profile controls *what the agent may do inside that |
| 13 | +> boundary*. (`serve` defaults sandbox on, so its command passes `--no-sandbox`; |
| 14 | +> `run`/`repl`/`telegram` are unsandboxed by default.) |
| 15 | +
|
| 16 | +For the full walkthrough, threat model, and tuning, see |
| 17 | +[`../DOCKER_COMPOSE_USER_GUIDE.md`](../DOCKER_COMPOSE_USER_GUIDE.md). |
| 18 | + |
| 19 | +## Files |
| 20 | + |
| 21 | +``` |
| 22 | +docker/ |
| 23 | +├── Dockerfile # multi-stage build of the odek binary |
| 24 | +├── docker-compose.yml # 4 services across 4 profiles |
| 25 | +├── config.restricted.json # Restricted permission policy |
| 26 | +├── config.godmode.json # Godmode (YOLO) permission policy |
| 27 | +├── .env.example # copy to .env, add your API key |
| 28 | +└── workspace/ # the dir the agent works in (mounted in) |
| 29 | +``` |
| 30 | + |
| 31 | +## Quick start |
| 32 | + |
| 33 | +All commands are run **from this `docker/` directory** so relative paths and |
| 34 | +`.env` resolve correctly: |
| 35 | + |
| 36 | +```bash |
| 37 | +cd docker |
| 38 | +cp .env.example .env # then edit .env: set ODEK_API_KEY (+ model/base URL) |
| 39 | +``` |
| 40 | + |
| 41 | +### Restricted (recommended) |
| 42 | + |
| 43 | +Interactive Web UI with approval prompts. |
| 44 | + |
| 45 | +```bash |
| 46 | +docker compose --profile restricted up --build |
| 47 | +``` |
| 48 | + |
| 49 | +Open <http://127.0.0.1:8080>, type a task. When the agent attempts a higher-risk |
| 50 | +command (network, install, code execution) an **approval modal** appears — approve |
| 51 | +or deny. Destructive commands are rejected automatically. Stop with `Ctrl-C`, then |
| 52 | +`docker compose --profile restricted down`. |
| 53 | + |
| 54 | +Prefer a terminal REPL (approvals come from the TTY, so `-it` is required): |
| 55 | + |
| 56 | +```bash |
| 57 | +docker compose run --rm -it odek-restricted repl |
| 58 | +``` |
| 59 | + |
| 60 | +### Godmode (all permissions) |
| 61 | + |
| 62 | +No prompts. Best for sealed, disposable containers. One-shot task: |
| 63 | + |
| 64 | +```bash |
| 65 | +docker compose --profile godmode run --rm odek-godmode \ |
| 66 | + run "Create build.sh, make it executable, and run it." |
| 67 | +``` |
| 68 | + |
| 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. |
| 72 | + |
| 73 | +### Telegram bot |
| 74 | + |
| 75 | +Drive the agent from a Telegram chat. Outbound long-polling — **no inbound ports |
| 76 | +needed**. Approvals (Restricted) arrive as inline `[Approve] [Deny] [Trust]` |
| 77 | +keyboards. |
| 78 | + |
| 79 | +1. Create a bot with **@BotFather**, copy the token. |
| 80 | +2. In `.env`, set `ODEK_TELEGRAM_BOT_TOKEN` and **always** restrict access with |
| 81 | + `ODEK_TELEGRAM_ALLOWED_CHATS` (and/or `_USERS`) — a bot token is a public |
| 82 | + endpoint. |
| 83 | +3. Start one of: |
| 84 | + |
| 85 | +```bash |
| 86 | +docker compose --profile telegram-restricted up --build -d # approvals in chat |
| 87 | +docker compose --profile telegram-godmode up --build -d # no prompts |
| 88 | +``` |
| 89 | + |
| 90 | +Message your bot `/start`. Sessions persist in the `odek-tg-state` volume. |
| 91 | + |
| 92 | +> **Only run one Telegram profile at a time per token** — Telegram allows a single |
| 93 | +> long-poller per bot (a second gets `409 Conflict`). Create a second bot via |
| 94 | +> @BotFather if you want both. |
| 95 | +
|
| 96 | +## Verify the profiles differ |
| 97 | + |
| 98 | +- **Restricted**: ask it to `rm -rf` everything in `/workspace` → denied, never runs. |
| 99 | +- **Godmode**: the same request executes without a prompt (use a throwaway `workspace/`). |
| 100 | + |
| 101 | +Print the active policy mounted in a container: |
| 102 | + |
| 103 | +```bash |
| 104 | +docker compose --profile restricted run --rm --entrypoint cat \ |
| 105 | + odek-restricted /home/odek/.odek/config.json |
| 106 | +``` |
| 107 | + |
| 108 | +## Tuning |
| 109 | + |
| 110 | +Edit `config.restricted.json`. Precedence (highest first): `allowlist` (exact |
| 111 | +match) → `denylist` (prefix) → per-class `classes` → global `action` → built-in |
| 112 | +defaults. The `blocked` class (fork bombs, etc.) is always denied. Recreate the |
| 113 | +container after editing (`... up` again) since the config is mounted at startup. |
| 114 | + |
| 115 | +```jsonc |
| 116 | +{ |
| 117 | + "dangerous": { |
| 118 | + "action": "prompt", |
| 119 | + "allowlist": ["go test ./...", "npm test"], // always allowed |
| 120 | + "denylist": ["git push --force"], // always blocked |
| 121 | + "classes": { "network_egress": "allow" } // loosen one class |
| 122 | + } |
| 123 | +} |
| 124 | +``` |
| 125 | + |
| 126 | +## Security notes |
| 127 | + |
| 128 | +- Container runs as **non-root** (uid 1000). Keep it that way. |
| 129 | +- Mount only what the agent needs (`./workspace`). Never mount `/`, `$HOME`, SSH |
| 130 | + keys, cloud creds, or `/var/run/docker.sock`. |
| 131 | +- Keep the Web UI on `127.0.0.1`; front it with an authenticated reverse proxy for |
| 132 | + remote access. |
| 133 | +- Prefer `network_mode: none` (or a scoped network), especially for Godmode. |
| 134 | +- `.env` and `workspace/` are gitignored — never commit secrets or scratch files. |
0 commit comments