# Docker execution sandbox Status: implementation prepared; Docker Engine/Compose are not installed on the development host, so image build and runtime isolation tests have NOT been executed. Existing localhost:5173 is still the host process, not this sandbox. ## Scope An instance contains the complete Fastify/Pi runtime and its child processes, not only the PPT worker. Non-root UID 1000, read-only rootfs, no capabilities, no privilege escalation, CPU/RAM/PID limits, default Docker seccomp, no host PID/network namespace, no Docker socket, no home or project bind mount. Source/dependencies and three explicitly selected Skills are baked into the image. Extensions remain disabled. Each Compose project name receives a separate named data volume. This is **instance isolation**, not automatic per-message/per-task containers, and not tenant isolation within one instance. Do not share one instance across mutually untrusted users. The supervisor stops after one hour; data remains in its volume. Named volumes have no disk quota here. ## Start (once Docker Engine + Compose are installed) From the project directory: ```sh node sandbox/manage.mjs start demo node sandbox/manage.mjs verify demo ``` Open http://localhost:15173/ — it does not replace the existing port 5173 host service. Set the same model URL/name in the new UI. `.env` is used only as Compose interpolation input; only explicitly listed keys/tokens are passed. It is never copied or mounted. Container code can access those supplied credentials; use scoped, revocable keys. No fallback to host execution occurs if Docker or a runtime dependency is unavailable. Stop/remove containers while retaining reports: ```sh node sandbox/manage.mjs stop demo ``` For a separate isolated instance, choose another name and `SANDBOX_PORT`. Do not reuse an old data volume for untrusted new work. ## Network The agent connects only to an internal Docker network. A separate CONNECT proxy permits port 443 to exact names from `SANDBOX_EGRESS_HOSTS` (default: current model gateway and SW Ads MCP). The proxy resolves IPv4, rejects private/reserved and mixed public/private answers, then connects to the checked address. Arbitrary websites and web searches are blocked by default. Add only reviewed exact hostnames; do not add wildcards. Libraries that ignore the proxy must fail, not receive a direct route. This is constrained HTTP(S) egress, not a complete data-loss-prevention system. The configured model/provider can receive data, allowed servers can themselves relay traffic, Docker DNS is not a general DNS-exfiltration firewall, and a kernel/container-runtime vulnerability can break isolation. Do not treat this as adequate for hostile multi-tenant execution without further hardening/rootless Docker or stronger isolation. ## PPT support (optional, unverified in container) The private artifact runtime is not available from the public package registry. Without it, PPT generation fails explicitly. To enable, set `PPT_RUNTIME_HOST_DIR` to the exact bundled dependencies directory and `PPT_SKILL_HOST_DIR` to the exact presentations skill directory. The launcher adds `compose.sandbox-ppt.yaml`, mounting only these two paths read-only at fixed container paths. Never set either path to a home directory or repository root. Host runtime binaries must be Linux-compatible with the container; fonts/native dependencies and full rendering need actual container validation. ## Verification Static/unit: `node --test sandbox/*.test.mjs`. Runtime: `node sandbox/manage.mjs verify demo` checks actual container configuration, writable scope and blocked direct egress. Also test approved model HTTPS, denied private/unknown targets, cancellation/TTL, report download and restart persistence before trusting deployment. No authentication or multi-tenant authorization is added by this change. References: https://docs.docker.com/reference/compose-file/services/ ; https://docs.docker.com/engine/security/rootless/ ; https://nodejs.org/api/cli.html#node_use_env_proxy1