Add semi-persistent code-execution sandbox (Jupyter Kernel Gateway runner)

Introduce a pluggable CodeSandbox abstraction with a SandboxManager and a
Jupyter Kernel Gateway backend: the app is a client of a single always-on
runner that executes code in stateful in-process kernels (no child-container
spawning, no docker socket). Sessions bind to a conversation or workflow run
with an agent-selectable TTL clamped by a global cap; execution enforces a
wall-clock deadline with interrupt-on-timeout and capped output, and file
transfer is workspace-contained with size and integrity checks. Adds a
docsgpt-sandbox docker-compose service (resource-capped, read-only, internal
network) plus settings, with a real local-gateway integration test.
This commit is contained in:
Alex committed 2026-06-24 11:26:23 +01:00
1 parent 922ed53a70
commit 3b795dbb83
14 files changed
+1332 -1

No files matched your search

+33
View File
@@ -24,6 +24,8 @@ services:
- CELERY_RESULT_BACKEND=redis://redis:6379/1
- CACHE_REDIS_URL=redis://redis:6379/2
- POSTGRES_URI=postgresql://docsgpt:docsgpt@postgres:5432/docsgpt
# Code-execution runner reached over HTTP + WebSocket (no docker socket).
- SANDBOX_GATEWAY_URL=http://docsgpt-sandbox:8888
ports:
- "7091:7091"
volumes:
@@ -49,6 +51,7 @@ services:
- API_URL=http://backend:7091
- CACHE_REDIS_URL=redis://redis:6379/2
- POSTGRES_URI=postgresql://docsgpt:docsgpt@postgres:5432/docsgpt
- SANDBOX_GATEWAY_URL=http://docsgpt-sandbox:8888
volumes:
- ../application/indexes:/app/indexes
- ../application/inputs:/app/inputs
@@ -59,6 +62,30 @@ services:
postgres:
condition: service_healthy
# Always-on code-execution runner (Jupyter Kernel Gateway). Sessions are
# in-process kernels, never child containers; the Docker socket is NOT
# mounted. On an internal-only network — no host port is published, so the
# runner is reachable only from backend/worker, not from the host/internet.
# Egress/SSRF blocks, the gVisor `runsc` runtime, and seccomp profile come in
# the hardening slice.
docsgpt-sandbox:
build: ./sandbox
mem_limit: ${SANDBOX_MEMORY:-1g}
cpus: ${SANDBOX_CPUS:-1.0}
pids_limit: 256
read_only: true
environment:
# Keep Jupyter's runtime/connection files on the writable tmpfs.
- JUPYTER_RUNTIME_DIR=/tmp/jupyter-runtime
- JUPYTER_DATA_DIR=/tmp/jupyter-data
tmpfs:
# Per-session workspaces (/tmp/docsgpt-sandbox/<session_id>) and Jupyter
# runtime files live on tmpfs; the root FS is read-only everywhere else.
- /tmp
networks:
- sandbox-net
- default
redis:
image: redis:6-alpine
ports:
@@ -80,6 +107,12 @@ services:
timeout: 5s
retries: 10
networks:
# Internal-only network for the sandbox runner (no external egress route via
# this network; the runner still reaches the internet via the default bridge).
sandbox-net:
internal: true
volumes:
postgres_data: