feat: isolated code execution (IsolatedCodeRunner + backends) — v0.24.0 - #51
Merged
Conversation
Add an IsolatedCodeRunner that runs untrusted/LLM-generated code behind a real boundary, with tool access brokered back to the trusted host over a single audited unix-socket channel — the safe counterpart to the in-process, trusted-only CodeSandbox. Core (backend-agnostic): - IsolationLimits, IsolatedResult, IsolationBackend protocol, GuestJob/Outcome - ToolBroker: host-side RPC server; owns the registry, enforces a per-run token, tool allowlist, and max-tool-calls ceiling; JSON-only wire (never pickle) - guest_bootstrap: dependency-free guest entrypoint that execs the code with async tool proxies and reports the result back over the socket - IsolatedCodeRunner: ties broker + backend together; refuses non-isolating backends unless allow_no_isolation=True Backends: - SubprocessBackend base (staging, rlimits, wall-clock kill, output caps) - LocalProcessBackend: no isolation, dev/testing/reference only - SeatbeltBackend: macOS sandbox-exec; denies inet network and filesystem writes outside the work/tmp dirs, denies reads of well-known secret dirs Tests cover the wire framing, limits validation, fail-closed behaviour, and the full core via the local backend; the Seatbelt suite runs on macOS and asserts the boundary actually blocks network and filesystem access. Signed-off-by: chris hay <chris.hay@uk.ibm.com>
Build on the isolation core with two OS-level backends behind the same IsolationBackend protocol: - DockerBackend: one throwaway `docker run --rm` container per run — no network, read-only root, dropped caps, no-new-privileges, memory/pids limits; broker socket bind-mounted in. Force-removes the container by (token-derived) name on timeout. Works with any docker/podman-compatible CLI. - BubblewrapBackend: Linux `bwrap` namespace sandbox — read-only system view, private tmpfs, fresh /proc + /dev, net unshared unless allowed, only the broker socket bound in. Also make SeatbeltBackend's secret-read denylist configurable (deny_read_paths / add_deny_read_paths; DEFAULT_DENY_READ_PATHS as the base set) instead of a hard-coded list. Backends shell out to docker/bwrap/sandbox-exec (no Python deps). Tests cover argv construction (pure, run everywhere) and the configurable denylist; full container/namespace integration is gated on the runtime being present. Signed-off-by: chris hay <chris.hay@uk.ibm.com>
Add docs/isolated_execution.md: architecture (broker + guest + JSON RPC + limits), backend comparison and selection, usage, the security/threat model, the configurable Seatbelt denylist, and how to write a custom backend. Point docs/security.md's "running untrusted code safely" section and the programmatic_execution.md CodeSandbox warning at IsolatedCodeRunner as the concrete answer for untrusted/LLM-generated code. Signed-off-by: chris hay <chris.hay@uk.ibm.com>
README, CORE_CONCEPTS, and ADVANCED_TOPICS described the subprocess IsolatedStrategy as the way to run "untrusted"/"LLM-generated code" "safely" and called it a "security boundary". It is crash/fault isolation only — same OS user, no seccomp/namespaces, results cross via pickle — and it never executes an orchestration code string. Reframe it as crash isolation for tool dispatch and route untrusted/LLM *code* to IsolatedCodeRunner instead. Add docs/isolated_execution.md and docs/security.md to the README docs index. Signed-off-by: chris hay <chris.hay@uk.ibm.com>
…ends) Bump version to 0.24.0 and add CHANGELOG entry for the isolated code execution feature (IsolatedCodeRunner + Seatbelt/Docker/bubblewrap/local backends). Additive — no changes to existing public APIs. Signed-off-by: chris hay <chris.hay@uk.ibm.com>
Signed-off-by: chris hay <chris.hay@uk.ibm.com>
Signed-off-by: chris hay <chris.hay@uk.ibm.com>
chrishayuk
force-pushed
the
feat/isolated-code-execution
branch
from
July 28, 2026 15:45
d9e4c18 to
c9c7fd6
Compare
The pre-commit ruff was pinned to v0.7.1 while CI resolves ruff 0.15.x via 'uv run ruff', so the two disagreed on formatting and green local commits could still fail CI's 'ruff format --check .'. Align them. Signed-off-by: chris hay <chris.hay@uk.ibm.com>
- Add tests/execution/isolation/__init__.py and put repo root on pytest pythonpath so 'from tests.…' cross-test imports resolve under the pytest console script (CI), not only 'python -m pytest'. - Set mypy platform=linux so POSIX-only APIs used behind runtime guards (os.killpg, resource, asyncio.start_unix_server) don't fail type-checking on the Windows runner; Windows-only modules keep their ignore_errors override. Signed-off-by: chris hay <chris.hay@uk.ibm.com>
- Skip the isolation test modules on Windows: this release's broker uses unix domain sockets, so the feature is POSIX-only (Windows support is a later PR). - Gate Docker/bubblewrap integration tests behind CTP_TEST_ISOLATION_INTEGRATION so they don't run in the default matrix (ubuntu runners have Docker; the backend integration isn't verified in the mandatory suite). - Fix the cross-test import to a same-dir 'from test_runner import' (works under the pytest console script) and revert the global pythonpath change. - Keep mypy platform=linux so POSIX APIs behind runtime guards pass on Windows. Signed-off-by: chris hay <chris.hay@uk.ibm.com>
Signed-off-by: chris hay <chris.hay@uk.ibm.com>
Signed-off-by: chris hay <chris.hay@uk.ibm.com>
…op ALL) Signed-off-by: chris hay <chris.hay@uk.ibm.com>
…tation - Add isolation.yml: runs the Docker + bubblewrap integration tests on ubuntu (CTP_TEST_ISOLATION_INTEGRATION=1) so the container backend is CI-verified. - Remove the temporary docker-debug workflow. - docs: DockerBackend is CI-verified end-to-end on native Linux and runs as the host uid; note that Docker Desktop / podman-machine VM file sharing does not support the bind-mounted unix socket (ENOTSUP). Signed-off-by: chris hay <chris.hay@uk.ibm.com>
…loopback) Docker integration is CI-verified; bubblewrap needs a real Linux host with unprivileged user namespaces (GitHub runners reject bwrap's loopback RTM_NEWADDR). Signed-off-by: chris hay <chris.hay@uk.ibm.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
IsolatedCodeRunner— the safe counterpart toCodeSandbox— for running untrusted / LLM-generated code behind a real OS/runtime boundary, with tool access brokered back to the host over a single audited channel (JSON, never pickle).What's included
IsolatedCodeRunner,IsolationLimits,IsolatedResult, theIsolationBackendprotocol, and a host-sideToolBroker(per-run token, tool allowlist, call ceiling).SeatbeltBackend(macOSsandbox-exec),DockerBackend(throwaway container),BubblewrapBackend(Linux namespaces),LocalProcessBackend(no isolation — dev/testing only; refused unlessallow_no_isolation=True).docs/isolated_execution.md;security.md/programmatic_execution.mdnow point untrusted code atIsolatedCodeRunner; README/CORE_CONCEPTS/ADVANCED_TOPICS stop presenting the subprocessIsolatedStrategyas a security boundary.Compatibility
Purely additive — no existing public API changes.
from chuk_tool_processor.execution import CodeSandboxdoes not eagerly load the isolation package. Verified downstream (mcp-cli) imports unaffected.Verification
tests/execution/isolation/+ CodeSandbox tests: 68 passed / 4 skipped (backend integration gated on runtime availability).Stacked on the v0.23.0 CodeSandbox fix. Experimental Windows (AppContainer) and WASM backends follow on separate branches.
🤖 Generated with Claude Code