feat: merge chat container into edge — run chat server inside edge container with full permissions (reverts sandbox from #706) #1083

Closed
opened 2026-04-20 15:18:24 +00:00 by disinto-admin · 0 comments

Goal

Merge the chat container into the edge container. Run the chat Python server as a background process inside disinto-edge alongside Caddy. Reverts the sandboxing in #706.

Rationale (from operator): chat needs broad access to project files, ops repo, and docker socket anyway to be useful for "drive the factory from a chat window" — which is the #623 vision. The sandbox isolation adds operational complexity (separate container, separate lifecycle, shared-secret forward_auth) without preventing the actual blast radius. Move it inside edge and give it the same permissions as edge's existing supervisor-run + collect-engagement processes.

The change

Remove

  • docker-compose.yml — delete the chat: service block (currently lines 384-430) and the chat-config named volume
  • docker/chat/entrypoint-chat.sh — delete (sandbox sanity checks no longer apply)
  • docker/chat/Dockerfile — delete (chat is no longer a standalone image)

Move into edge

  • docker/chat/server.py → keep in place; copy into edge image instead of chat image
  • docker/chat/ui/ → keep in place; copy into edge image
  • docker/edge/Dockerfile — add:
    • nodejs / npm via apt
    • npm install -g @anthropic-ai/claude-code@2.1.84
    • COPY docker/chat/server.py /usr/local/bin/chat-server.py
    • COPY docker/chat/ui/ /var/chat/ui/
  • docker/edge/entrypoint-edge.sh — launch server as a background process before caddy run, same pattern as the existing collect-engagement loop:
    (python3 /usr/local/bin/chat-server.py 2>&1 | tee -a /opt/disinto-logs/chat.log) &
    
    Caddy must stay the foreground process (its exit triggers container restart).

Caddyfile update

  • lib/generators.sh emits handle /chat/* blocks — change the reverse_proxy target from chat:8080 to 127.0.0.1:8080. Same for /chat/login and /chat/oauth/callback handles.
  • forward_auth chat:8080 in the /chat/* block → forward_auth 127.0.0.1:8080. Keep the FORWARD_AUTH_SECRET header (still useful for intra-container path isolation).

Environment

Edge inherits chat's env vars. Move these from the deleted chat: service into the edge: service:

  • CHAT_OAUTH_CLIENT_ID, CHAT_OAUTH_CLIENT_SECRET
  • DISINTO_CHAT_ALLOWED_USERS
  • FORWARD_AUTH_SECRET (already on edge — verify)
  • CHAT_HOST, CHAT_PORT (default 127.0.0.1:8080 — NOT 0.0.0.0 anymore, since edge is the only consumer)
  • CHAT_HISTORY_DIR — bind mount moves to edge service as well: ${CHAT_HISTORY_DIR:-./state/chat-history}:/var/lib/chat/history

Acceptance criteria

  • docker compose up -d stands up the stack with no chat container; docker ps shows 1 fewer container than before
  • docker exec disinto-edge ps -ef shows the python3 chat server process running alongside Caddy
  • curl -sI http://localhost/chat/ still returns 401 (forward_auth) when no session cookie is present
  • With valid CHAT_OAUTH_CLIENT_ID/SECRET and a Forgejo login, /chat/ serves index.html; POST /chat can spawn Claude
  • Chat history persists across docker compose restart edge (via CHAT_HISTORY_DIR bind mount on edge)
  • tests/smoke-edge-subpath.sh passes for all /chat/* assertions (login/callback/forward_auth 401 on root)
  • docker/chat/Dockerfile and docker/chat/entrypoint-chat.sh deleted from the tree
  • shellcheck clean on entrypoint-edge.sh after changes

Side effects to handle

  • Session state lives in process memory (server.py _validate_session etc). An edge container restart invalidates all sessions. Previously users would log in again after a chat restart; now they log in again after any edge restart (which is more frequent due to Caddy config reloads). Consider moving sessions to disk if this becomes annoying, but not required for this issue.
  • #1027 (Claude workspace scope) — mostly obsolete since edge already has /opt/disinto mounted. Close #1027 after this lands, or re-scope it narrowly to "set Claude cwd to the project staging checkout" (~10 lines in handle_chat).
  • Image size — edge Dockerfile grows by ~150MB (Node.js + Claude CLI). Acceptable; edge is already the largest image.
  • #706 — original sandbox hardening that this reverts. Can be closed as "superseded by #NNNN" once this lands.
  • Vision #623 — edge-subpath-chat sprint
  • #1027 — Claude workspace scope. Re-scope or close after this lands.
  • #711 (companion) — drop chat rate limiting in the same architectural simplification pass
## Goal Merge the `chat` container into the `edge` container. Run the chat Python server as a background process inside `disinto-edge` alongside Caddy. Reverts the sandboxing in #706. **Rationale (from operator):** chat needs broad access to project files, ops repo, and docker socket anyway to be useful for "drive the factory from a chat window" — which is the #623 vision. The sandbox isolation adds operational complexity (separate container, separate lifecycle, shared-secret forward_auth) without preventing the actual blast radius. Move it inside edge and give it the same permissions as edge's existing supervisor-run + collect-engagement processes. ## The change ### Remove - `docker-compose.yml` — delete the `chat:` service block (currently lines 384-430) and the `chat-config` named volume - `docker/chat/entrypoint-chat.sh` — delete (sandbox sanity checks no longer apply) - `docker/chat/Dockerfile` — delete (chat is no longer a standalone image) ### Move into edge - `docker/chat/server.py` → keep in place; copy into edge image instead of chat image - `docker/chat/ui/` → keep in place; copy into edge image - `docker/edge/Dockerfile` — add: - `nodejs` / `npm` via apt - `npm install -g @anthropic-ai/claude-code@2.1.84` - `COPY docker/chat/server.py /usr/local/bin/chat-server.py` - `COPY docker/chat/ui/ /var/chat/ui/` - `docker/edge/entrypoint-edge.sh` — launch server as a background process before `caddy run`, same pattern as the existing `collect-engagement` loop: ```bash (python3 /usr/local/bin/chat-server.py 2>&1 | tee -a /opt/disinto-logs/chat.log) & ``` Caddy must stay the foreground process (its exit triggers container restart). ### Caddyfile update - `lib/generators.sh` emits `handle /chat/*` blocks — change the reverse_proxy target from `chat:8080` to `127.0.0.1:8080`. Same for `/chat/login` and `/chat/oauth/callback` handles. - `forward_auth chat:8080` in the `/chat/*` block → `forward_auth 127.0.0.1:8080`. Keep the `FORWARD_AUTH_SECRET` header (still useful for intra-container path isolation). ### Environment Edge inherits chat's env vars. Move these from the deleted `chat:` service into the `edge:` service: - `CHAT_OAUTH_CLIENT_ID`, `CHAT_OAUTH_CLIENT_SECRET` - `DISINTO_CHAT_ALLOWED_USERS` - `FORWARD_AUTH_SECRET` (already on edge — verify) - `CHAT_HOST`, `CHAT_PORT` (default `127.0.0.1:8080` — NOT `0.0.0.0` anymore, since edge is the only consumer) - `CHAT_HISTORY_DIR` — bind mount moves to edge service as well: `${CHAT_HISTORY_DIR:-./state/chat-history}:/var/lib/chat/history` ## Acceptance criteria - [ ] `docker compose up -d` stands up the stack with no `chat` container; `docker ps` shows 1 fewer container than before - [ ] `docker exec disinto-edge ps -ef` shows the python3 chat server process running alongside Caddy - [ ] `curl -sI http://localhost/chat/` still returns 401 (forward_auth) when no session cookie is present - [ ] With valid `CHAT_OAUTH_CLIENT_ID/SECRET` and a Forgejo login, `/chat/` serves index.html; POST `/chat` can spawn Claude - [ ] Chat history persists across `docker compose restart edge` (via `CHAT_HISTORY_DIR` bind mount on edge) - [ ] `tests/smoke-edge-subpath.sh` passes for all `/chat/*` assertions (login/callback/forward_auth 401 on root) - [ ] `docker/chat/Dockerfile` and `docker/chat/entrypoint-chat.sh` deleted from the tree - [ ] `shellcheck` clean on `entrypoint-edge.sh` after changes ## Side effects to handle - **Session state lives in process memory** (`server.py` _validate_session etc). An edge container restart invalidates all sessions. Previously users would log in again after a chat restart; now they log in again after any edge restart (which is more frequent due to Caddy config reloads). Consider moving sessions to disk if this becomes annoying, but not required for this issue. - **`#1027`** (Claude workspace scope) — mostly obsolete since edge already has `/opt/disinto` mounted. Close #1027 after this lands, or re-scope it narrowly to "set Claude cwd to the project staging checkout" (~10 lines in `handle_chat`). - **Image size** — edge Dockerfile grows by ~150MB (Node.js + Claude CLI). Acceptable; edge is already the largest image. ## Related - #706 — original sandbox hardening that this reverts. Can be closed as "superseded by #NNNN" once this lands. - Vision #623 — edge-subpath-chat sprint - #1027 — Claude workspace scope. Re-scope or close after this lands. - #711 (companion) — drop chat rate limiting in the same architectural simplification pass
disinto-admin added the
backlog
label 2026-04-20 15:18:24 +00:00
dev-bot self-assigned this 2026-04-20 15:20:52 +00:00
dev-bot added
in-progress
and removed
backlog
labels 2026-04-20 15:20:53 +00:00
dev-bot was unassigned by dev-qwen 2026-04-20 15:59:44 +00:00
dev-qwen removed the
in-progress
label 2026-04-20 15:59:45 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: disinto-admin/disinto#1083
No description provided.