Repository navigation
Description
Summary
Add an out-of-process code-runner and the backend client that drives it, as the foundation for code mode (executing short JS programs that orchestrate several MCP tool calls in one round trip). This issue tracks the runner subsystem and the transport client only; the execution-path, schema, governance and Cloud pieces are tracked in the linked issues.
Design
- Runner (
deploy/code-runner/): one container, long-lived supervisor (PID 1, root) that spawns one Deno subprocess per run under an unprivileged uid with a cleared environment. The subprocess runs user code with every Deno permission denied and an import map that blocks everyfile:/node:/npm:/remote specifier. The only way out is theamcpbridge (search/describe/run), shuttled back to the backend over a WebSocket. No egress, no filesystem, no env, no secrets in the child. - Backend client (
CodeExecutorService): opens the WebSocket per run, answers each bridge call via the existingexecuteToolunder the caller's role scope, enforces transport-level ceilings, and refuses when no runner is configured or reachable (never runs in-process).
Security model (defence in depth)
Deno --deny-* flags + --no-remote/--no-npm/--cached-only; an import map that closes the import-based file reads (import(..., {with:{type:'text'|'json'}}), relative imports, new Function, data:/blob: chains) that --deny-read alone does not stop; setpriv to an unprivileged uid; env -i so the run token is never in the child environment; container read_only + cap_drop: ALL (only SETUID/SETGID) + no-new-privileges.
Verification
deploy/code-runner/test.sh: 19/19 containment assertions (fetch, file read, env, subprocess, FFI, node:fs, node:child_process, /proc/self/environ, text/json/relative/new Function imports all denied; compute/bridge/console allowed; timeout kills; errors propagate) and the end-to-end WebSocket path. Verified on denoland/deno:debian-2.9.8.
Status
Implemented and verified on branch helpcodeai/code-mode, OFF by default (CODE_RUNNER_URL unset; code-runner compose profile). No schema change. Backend: tsc clean, eslint clean, unit tests pass.
Deployment behaviour
- Self-hosted:
COMPOSE_PROFILES=code-runner+CODE_RUNNER_URL=ws://code-runner:8777. - Cloud: dedicated service (wiring tracked separately).
- Both: unreachable runner ⇒ code mode unavailable, runs refused.
Activity
helpcodeai commentedon Oct 31, 2026
Implemented on branch
helpcodeai/code-mode(commit 6d872de). The code-runner (Dockerfile, supervisor, harness, import map) and the backendCodeExecutorServiceare in place and off by default. Containment + bridge verified withdeploy/code-runner/test.sh(19 containment assertions + the end-to-end WebSocket path) on deno 2.9.8. Done.helpcodeai commentedon Oct 31, 2026
Delivered in PR #1366 (merged into main as be4742a). Code mode ships off by default; the agent-proposal follow-up is tracked in #1369, and the Cloud runner operations in this issue remain operational/out-of-repo.
Add code mode: isolated runner, executor, saved code tools, schema
Add a live end-to-end code mode integration test