feat(sandbox): add bridge binaries and container image tooling - #41
Merged
Conversation
PR #35 wires the server to three container binaries (t3-preview-bridge, t3-egress-proxy) plus a credential proxy, but no source for them existed in this repo or any registry, so the sandbox path could not run. Adds packages/sandbox-bridge, which esbuild-bundles three dependency-free .mjs executables for Node 22. The preview bridge's wire contract is derived from its only caller (ThreadPreviewProxy/DesktopHttpRoutes) rather than invented, including the zero-length frame it must emit when an upstream WebSocket opens so the server relay's 10s handshake timer is cleared. The egress proxy resolves, vets every resolved address, then dials the vetted literal, closing the DNS-rebinding hole a resolve-then-redial-by-name would leave. The credential proxy keeps provider secrets outside the workspace container: it serves 503 until the server pushes a config, compares the thread token over fixed-size digests, routes only on the first path segment, and streams bodies so SSE turns are not buffered. Adds deploy/openclaw/sandbox-image with a headless workspace Containerfile (no desktop, no WebRTC), a sidecar Containerfile carrying all three binaries, and a build script that emits the digest-pinned refs the server's image regex requires. Tests drive the bundled binaries as real child processes over stdio and HTTP, waiting only on real events. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…PROXY The credential proxy dialed provider APIs directly with node:http/node:https. Those core modules ignore HTTP_PROXY/HTTPS_PROXY entirely — only undici reads them, via EnvHttpProxyAgent — so every upstream request would have failed inside a real sandbox: the thread network is created `--internal` (ContainerSandboxBackend.ts), leaving the egress sidecar as the only route out. The existing tests missed it because they point baseUrl at a loopback origin in the same netns, the one topology where the omission is invisible. Adds src/proxyClient.ts, which selects a proxy per the usual environment grammar (including NO_PROXY with wildcard, leading-dot and :port forms) and then issues CONNECT-then-TLS for https upstreams or an absolute-URI request for http upstreams — the two shapes t3-egress-proxy already serves. SNI is omitted for IP literals per RFC 6066. The new tests drive the real chain, credential proxy -> egress proxy -> origin, over both http and https, plus NO_PROXY bypass and an unreachable proxy. Verified by mutation: reverting to a direct dial fails exactly these and no others. t3-egress-proxy now also logs forwarded requests, not just denials, so a successful traversal is observable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…testing build-sandbox-images.sh printed three of the four image variables the server reads, leaving the operator to hand-copy the credential-proxy digest. It is the same sidecar ref as the other two sidecar roles, so emit it in both the env and export blocks; pasting three of four required variables is exactly the silent misconfiguration this script exists to prevent. Also builds the bundles before running the package's tests. The suite spawns dist/*.mjs, which `vp test run` does not rebuild, so a source-only edit was invisible to the tests and could report a false green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner
Author
|
@coderabbitai review |
Owner
Author
|
@codex review |
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: unavailable · PR result: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
This was referenced Aug 17, 2026
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.
Summary
Part 1 of 3 for per-thread sandbox isolation (headless-first). The container backend for sandboxed threads (landed in #35) needs a preview bridge, an egress proxy, and container images to actually run — none of that existed. This adds them.
packages/sandbox-bridge/: three esbuild-bundled binaries (t3-preview-bridge,t3-egress-proxy,t3-credential-proxy) that speak the stdio/websocket-framed protocol the server already expects fromThreadPreviewProxy.tsandDesktopHttpRoutes.ts.deploy/openclaw/sandbox-image/: Containerfiles for the workspace and sidecar images, plus a build script that resolves and prints the digest-pinned image refs the server config needs.testnow rebuilds (node build.ts && vp test run) before running, so tests can't pass against a bundle that no longer matches source.Merges independently of the other two PRs (no file overlap), but sandboxing only becomes usable once all three plus the orphan-test PR are in and the host is bootstrapped.
Test plan
node build.ts && vp test runfrompackages/sandbox-bridge— 34/34 passing, including aproxyClientsuite (CONNECT-tunnelling throughHTTPS_PROXY, absolute-URI throughHTTP_PROXY, 502 on unreachable proxy) that only passes against a rebuilt bundle, catching a real proxy-chaining bug during review.dist/entirely,testrebuilt from nothing and passed 34/34.claude-opus-5 via Claude Code