Skip to content

Run the chat coding adapter's commands in a per-turn container (cwl-runner style) - #74

Merged
wilke merged 2 commits into
chian:mainfrom
wilke:feat/chat-coding-container-shell
Oct 8, 2026
Merged

wilke merged 2 commits into
chian:mainfrom
wilke:feat/chat-coding-container-shell

Conversation

@wilke

@wilke wilke commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Stage 1 of #73; addresses #72 for the Argo chat-completions coding adapter (#67).

Problem

#67's adapter confines its file tools to the coding workspace, but run_command was /bin/sh -c running as the user. In the first live refiner build, 64 of 75 commands read outside the workspace: OpenChia's installed sources and earlier source packages. Nothing prevented reading host credentials (~/.hermes/.env), writing elsewhere, or reaching the network.

Change

New agent/transports/coding_container.py (ContainerCommandShell, container_shell_for). When the Run executor is the container backend, iterative_episode_refiner/coding.py gives the chat adapter a per-turn container shell, and run_command executes there, the way a CWL runner executes a tool:

docker run -d --rm --network none --read-only --cap-drop ALL --security-opt no-new-privileges \
  --user <workspace owner uid:gid> --tmpfs /tmp --env HOME=/tmp <executor resource limits> \
  -v <workspace>:<same path>:rw  -v <OpenChia sources>:/ctx/openchia:ro \
  --env PYTHONPATH=/ctx/openchia  <executor's digest-pinned image> sleep infinity
docker exec <ctr> sh -c "<command>"     # each run_command
docker rm -f <ctr>                      # close, interrupt or timeout
  • Only the workspace is writable, mounted at its own path so absolute paths in the assignment stay valid. Files created inside are owned by the host user.
  • OpenChia's sources are mounted read-only at /ctx/openchia on PYTHONPATH, so diagnostics can import the libraries without browsing the host. The system message tells the model this.
  • One container per turn; each command is a docker exec.
  • A timeout or interrupt removes the container, which kills everything inside it; killing the exec client alone would not. The next command starts a fresh container.
  • No host-shell fallback: if the runtime can't start, the model gets error: container shell unavailable ….
  • Unchanged:

Verification

  • tests/agent/test_coding_container_shell.py, 6 tests with a fake Docker CLI:

    • isolation flags, with only the workspace mounted rw;
    • the run → exec → exec → rm lifecycle;
    • a timeout removes the container and the next command restarts it;
    • an unavailable runtime is reported and nothing runs on the host;
    • non-container executors keep the existing path;
    • the adapter routes run_command through the shell.

    Together with the existing adapter tests: 16 pass.

  • Real Docker 29.5.3 on macOS (python:3.14-slim):

    • start plus first command 0.2 s, then about 0.06 s per command;
    • network blocked (DNS failure);
    • /Users and ~/.hermes absent;
    • writes outside the workspace fail;
    • import method_loop works from /ctx/openchia;
    • a timeout removes the container and the next command recovers;
    • no containers are left behind.

Not in this PR (#73)

  • Stage 2: diagnostics parity (prepared site-packages mounted read-only).
  • Stage 3: predecessor package and curated context mounts, or a coder image.
  • Stage 4: Claude Code and Codex in a container or microVM behind a host model proxy; sbx is the candidate.

🤖 Generated with Claude Code

, chian#73 stage 1)

The Argo chat-completions coding adapter (chian#67) confined its file tools to the
coding workspace, but run_command was /bin/sh as the user: in the first live
build 64 of 75 commands read outside the workspace, and nothing prevented
reading host credentials, writing elsewhere, or reaching the network.

When the Run executor is the container backend, run_command now executes in a
per-turn container, the way a CWL runner executes a tool:
- docker run -d --rm --network none --read-only --cap-drop ALL
  --security-opt no-new-privileges --user <workspace owner> --tmpfs /tmp,
  the executor's resource limits and digest-pinned image;
- the coding workspace is the only writable mount (at its own path), and
  OpenChia's sources are mounted read-only at /ctx/openchia on PYTHONPATH;
- each command is a docker exec (~60 ms); a timeout or interrupt removes the
  container (killing everything inside) and the next command restarts it;
- a runtime that cannot start is reported to the model; there is no host
  shell fallback.

The model loop stays on the host; other backends and the systemd Run backend
keep their current behaviour (chian#73 stages 2-4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wilke added a commit to wilke/OpenChia that referenced this pull request Oct 8, 2026
chian#74)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wilke
wilke merged commit 3eb49a9 into chian:main Oct 8, 2026
33 of 52 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant