Skip to content

feat(sandbox): pluggable ISandboxProvider with auto-detection (Docker/gVisor/Firecracker)#1

Closed
jamtujest wants to merge 1 commit into
mainfrom
feat/pluggable-sandbox-providers
Closed

feat(sandbox): pluggable ISandboxProvider with auto-detection (Docker/gVisor/Firecracker)#1
jamtujest wants to merge 1 commit into
mainfrom
feat/pluggable-sandbox-providers

Conversation

@jamtujest

Copy link
Copy Markdown
Owner

Summary

  • Introduces ISandboxProvider interface for pluggable sandbox backends (Docker, gVisor, Firecracker MicroVM)
  • Adds DockerProvider wrapping existing docker.ts with zero behavior changes
  • Adds GVisorProvider stub with --runtime=runsc detection for user-space kernel isolation
  • Adds provider-resolver.ts with auto-detection of strongest available backend (Firecracker > gVisor > Docker)
  • Adds SandboxBackend type and backend field to SandboxConfig

Why?

Current sandbox (openclaw#29974) relies on Docker socket, sharing the host kernel. This PR enables:

Backend Isolation Requirement
Docker (Tier 1) Container namespaces Docker installed
gVisor (Tier 1.5) User-space kernel (Sentry) runsc runtime
Firecracker (Tier 2) Hardware KVM /dev/kvm

Auto-detection picks the strongest available backend.

Files

File Purpose
src/agents/sandbox/provider.ts ISandboxProvider interface + types
src/agents/sandbox/providers/docker-provider.ts Docker backend (wraps existing code)
src/agents/sandbox/providers/gvisor-provider.ts gVisor backend stub
src/agents/sandbox/providers/index.ts Re-exports
src/agents/sandbox/provider-resolver.ts Auto-detection + caching
src/agents/sandbox/types.ts SandboxBackend type + backend field

Test plan

  • TypeScript compilation passes with zero errors
  • DockerProvider wraps existing docker.ts with identical behavior
  • Provider auto-detection selects strongest available backend
  • Existing sandbox tests pass (100% backward compatibility)

Generated with Claude Code

…ection

Introduces a pluggable sandbox provider architecture that allows OpenClaw
to use different isolation backends (Docker, gVisor, Firecracker) through
a unified interface, with automatic detection of the strongest available
backend on the host.

New files:
- provider.ts: ISandboxProvider interface with types for SandboxInstance,
  SandboxExecRequest/Result, SandboxStatus
- providers/docker-provider.ts: DockerProvider wrapping existing docker.ts
- providers/gvisor-provider.ts: GVisorProvider stub with runsc detection
- providers/index.ts: re-exports
- provider-resolver.ts: auto-detect best backend (Firecracker > gVisor > Docker)

Modified:
- types.ts: add SandboxBackend type and backend field to SandboxConfig

Co-Authored-By: Claude Opus 4.6 <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: afb22127dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +86 to +89
if (hasGVisorRuntime()) {
log.info("gVisor (runsc) runtime detected");
return "gvisor";
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip auto-selecting gVisor until provider is implemented

When backend is auto and runsc is present, this resolver returns "gvisor", but the selected provider still throws not yet implemented for instance creation and command execution (src/agents/sandbox/providers/gvisor-provider.ts). In environments with gVisor installed, sandbox startup will fail instead of falling back to the working Docker path, so auto-detection should not choose gVisor until those core methods are implemented.

Useful? React with 👍 / 👎.

Comment on lines +61 to +64
["info", "--format", `{{index .Runtimes "${this.runtime}"}}`],
{ allowFailure: true },
);
return runtimeCheck.code === 0 && runtimeCheck.stdout.trim().length > 0;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat missing runtime probe output as unavailable

This availability check treats any non-empty output from {{index .Runtimes "..."}} as success, but Docker templates emit <no value> when a map key is missing, so hosts without the configured runtime can still be marked as available. That makes initialize() incorrectly succeed and pushes the failure to later sandbox operations instead of raising the intended runtime-not-installed error early.

Useful? React with 👍 / 👎.

@jamtujest jamtujest closed this Mar 7, 2026
@jamtujest
jamtujest deleted the feat/pluggable-sandbox-providers branch March 7, 2026 15:39
jamtujest pushed a commit that referenced this pull request Mar 10, 2026
Critical:
- #1: VNC proxy now validates Origin header, requires per-sandbox
  auth token, defaults to localhost-only origins

High:
- openclaw#2: VNC proxy: add 1 MiB read limit, 30s pong deadline, 10s write
  deadline, ping/pong keepalive handler
- openclaw#3: URLPolicy.Validate now resolves DNS hostnames and blocks if any
  resolved IP is private/loopback/link-local/metadata
- openclaw#4: BrowserService.Navigate re-validates final URL after redirect,
  navigates to about:blank if blocked
- openclaw#5: FileService restricts all paths to /workspace via resolvePath()
  that rejects absolute paths, .., and symlink escapes
- openclaw#6: TypeScript browser-security.ts now detects decimal/hex IPv4,
  IPv4-mapped IPv6, ULA fc00::/7, link-local fe80::/10
- openclaw#7: HTTP server binds 127.0.0.1 (not 0.0.0.0), VNC proxy disabled
  by default (port 0), /debug/vars implicitly protected
- openclaw#8: chroot linkOrCopy only falls back on EXDEV, copyFile uses
  O_EXCL+O_WRONLY to prevent symlink-following writes

Medium:
- openclaw#9: Streaming rootfs hash via io.Copy+sha256.New instead of
  os.ReadFile into memory
- openclaw#10: Shell-escape each exec argument before joining to prevent
  injection via sh -lc
- openclaw#11: envd TCP mode prints explicit development-only warning
- openclaw#12: Pool.Acquire now returns release callback, eviction skips
  leased (in-flight) snapshot directories

Low:
- openclaw#13: Validate snapshot metadata vcpu_count [1,32] and
  mem_size_mib [128,65536] before restore
- openclaw#14: Use lastIndexOf for Playwright end marker, truncate raw
  stdout in error messages to 4KB

Co-Authored-By: Claude Opus 4.6 <[email protected]>
jamtujest pushed a commit that referenced this pull request Mar 12, 2026
1. ProcessService: kill entire process group on context cancel (not just
   DeadlineExceeded), add SIGTERM→SIGKILL escalation, make streamPipe
   exit on ctx.Done() to prevent orphaned processes and hung RPCs (#1)

2. Unix socket: chmod 0600 after Listen to restrict access to owner (openclaw#2)

3. SSRF: detect and reject octal IPv4 notation (e.g. 0177.0.0.1) that
   Chromium/WHATWG interprets differently than Go's net.ParseIP (openclaw#6)

4. Screenshot: enforce 10MB hard limit on buffer to prevent memory
   exhaustion from full-page captures on attacker-controlled pages (openclaw#8)

5. Firecracker version: capture prerelease suffix in regex and reject
   prerelease builds (e.g. 1.14.1-rc1) at the minimum version gate (openclaw#14)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
jamtujest pushed a commit that referenced this pull request Mar 12, 2026
1. ProcessService: kill entire process group on context cancel (not just
   DeadlineExceeded), add SIGTERM→SIGKILL escalation, make streamPipe
   exit on ctx.Done() to prevent orphaned processes and hung RPCs (#1)

2. Unix socket: chmod 0600 after Listen to restrict access to owner (openclaw#2)

3. SSRF: detect and reject octal IPv4 notation (e.g. 0177.0.0.1) that
   Chromium/WHATWG interprets differently than Go's net.ParseIP (openclaw#6)

4. Screenshot: enforce 10MB hard limit on buffer to prevent memory
   exhaustion from full-page captures on attacker-controlled pages (openclaw#8)

5. Firecracker version: capture prerelease suffix in regex and reject
   prerelease builds (e.g. 1.14.1-rc1) at the minimum version gate (openclaw#14)

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant