@@ -40,11 +40,12 @@ odek/
4040├── .env # your API key + model settings (gitignored)
4141├── config.restricted.json # Restricted permission policy
4242├── config.godmode.json # Godmode (YOLO) permission policy
43- └── workspace/ # the directory the agent works in (mounted into the container)
43+ ├── workspace/ # the directory the agent works in (mounted into the container)
44+ └── .odek/ # Telegram bot state: sessions, skills, lock (mounted in)
4445```
4546
46- > Add ` .env ` and ` workspace /` to your ` .gitignore ` so you never commit secrets or
47- > scratch files.
47+ > Add ` .env ` , ` workspace/ ` , and ` .odek /` to your ` .gitignore ` so you never commit secrets
48+ > or scratch files.
4849
4950---
5051
@@ -75,8 +76,8 @@ FROM alpine:latest
7576RUN apk add --no-cache ca-certificates git bash coreutils curl jq
7677
7778# Run as a non-root user — defense in depth even inside the container.
78- # Pre-create ~/.odek owned by the user so a mounted named volume (used for
79- # Telegram session state in §13) inherits uid 1000 ownership and is writable .
79+ # Pre-create ~/.odek owned by the user so it's writable for config, sessions,
80+ # and the Telegram lock (whether backed by an image dir or a mounted folder) .
8081RUN adduser -D -u 1000 odek \
8182 && mkdir -p /home/odek/.odek /workspace \
8283 && chown -R odek:odek /home/odek/.odek /workspace
@@ -452,9 +453,9 @@ ODEK_TELEGRAM_SESSION_TTL_HOURS=24 # optional
452453
453454# ## 13c. Compose services
454455
455- Add these to `docker-compose.yml`. Note the **named volume for `/home/odek/.odek`** : it
456- persists per‑chat sessions, the daily‑budget counter, and the singleton lock across
457- restarts. No `ports` are needed.
456+ Add these to `docker-compose.yml`. State (per‑chat sessions, the daily‑budget counter, and
457+ the singleton lock) lives in a local **`./.odek` folder** — an external host folder, just
458+ like `./workspace` — so it survives restarts and is easy to inspect . No `ports` are needed.
458459
459460` ` ` yaml
460461 # ── Telegram bot — Restricted (approvals via inline keyboards) ──
@@ -466,8 +467,8 @@ restarts. No `ports` are needed.
466467 command: ["telegram"]
467468 volumes:
468469 - ./workspace:/workspace
470+ - ./.odek:/home/odek/.odek
469471 - ./config.restricted.json:/home/odek/.odek/config.json:ro
470- - odek-tg-state:/home/odek/.odek
471472 restart: unless-stopped
472473
473474 # ── Telegram bot — Godmode (no prompts; disposable container) ──
@@ -479,19 +480,22 @@ restarts. No `ports` are needed.
479480 command: ["telegram"]
480481 volumes:
481482 - ./workspace:/workspace
483+ - ./.odek:/home/odek/.odek
482484 - ./config.godmode.json:/home/odek/.odek/config.json:ro
483- - odek-tg-state:/home/odek/.odek
484485 restart: unless-stopped
486+ ` ` `
487+
488+ Create the folder first (so the container's non‑root user can write to it) and gitignore
489+ its contents :
485490
486- volumes:
487- odek-tg-state:
491+ ` ` ` bash
492+ mkdir -p . odek && chmod 777 .odek && touch .odek/.gitkeep
488493` ` `
489494
490- > The `:ro` config mount and the writable `odek-tg-state` volume both target
491- > `/home/odek/.odek`. Compose layers them: the bind mount wins for `config.json`, the
492- > named volume holds everything else (sessions, lock, budget). If your Compose version
493- > errors on overlapping mounts, drop the `:ro` bind and instead bake the policy into the
494- > image, or copy it into the volume once at startup.
495+ > The `./.odek` bind mounts at `/home/odek/.odek`, and `config.json` is bind‑mounted on top
496+ > of it — a nested file‑over‑directory mount. Compose layers them: the `:ro` `config.json`
497+ > wins for that one file, and `./.odek` holds everything else (sessions, lock, budget).
498+ > Docker leaves a harmless empty `./.odek/config.json` stub on the host as the mount point.
495499
496500# ## 13d. Run it
497501
@@ -522,16 +526,16 @@ Stop either with `docker compose --profile telegram-restricted down` (matching p
522526| `/help` | List all commands |
523527| `/new` | Archive the current session, start fresh |
524528
525- Voice and photo messages are supported too. Sessions persist per chat (visible via
526- ` odek session list` against the mounted state volume ).
529+ Voice and photo messages are supported too. Sessions persist per chat in the local
530+ ` ./. odek` folder (inspect with `odek session list` against that directory ).
527531
528532# ## 13f. Telegram‑specific gotchas
529533
530534- **One poller per token.** Telegram allows a single long‑poller per bot token; a second
531535 one gets `409 Conflict`. So you **cannot run the Restricted and Godmode bot services at
532536 the same time with the same token** — pick one, or create a second bot via @BotFather
533- for the other. A singleton PID lock at `~/.odek/telegram.pid` (kept in the shared state
534- volume ) backs this up — a second `odek telegram` that finds a live lock won't start.
537+ for the other. A singleton PID lock at `~/.odek/telegram.pid` (kept in the shared `./.odek`
538+ folder ) backs this up — a second `odek telegram` that finds a live lock won't start.
535539- **Optional health endpoint.** The `telegram` command takes no CLI flags — configure it
536540 via env. Set `ODEK_TELEGRAM_HEALTH_ADDR=0.0.0.0:9090` in `.env` (and add a `ports:`
537541 mapping) to expose `GET /health` for an orchestrator's liveness probe.
0 commit comments