Documentation
¶
Overview ¶
bubblewrap (bwrap) adapter — Linux primary engine.
Wrap rewrites the supplied *exec.Cmd to invoke bwrap with the flags compiled from Profile, then exec the original binary inside the sandbox. We never run unsharing logic ourselves; per ADR-007 bwrap owns the namespace setup, FS bind-mounts, and capability scrubbing. clawtool's polish layer is the Profile→argv translator.
Lifecycle:
- Wrap mutates cmd.Path + cmd.Args. The original binary path becomes the trailing argument bwrap exec's.
- cmd.Env is REPLACED with the env-allowlisted subset (bwrap itself --setenv preserves; we also re-build cmd.Env for callers that consult Process.Env directly).
- sysproc.ApplyGroupWithCtxCancel is the caller's job (supervisor.dispatch). On ctx cancel, the process group SIGKILL reaps bwrap + the agent inside it.
Docker fallback — ADR-020. Available on every OS as long as the daemon is reachable. v0.18.3 lands the actual `docker run` translation (volume mounts for paths, --network none/host for network policy, --memory / --cpus / --pids-limit for limits).
Lives outside any //go:build tag so the adapter is registered on every platform; Available() does the real probe.
install_hints.go — operator-friendly install instructions for the sandbox engines `clawtool sandbox doctor` reports as MISSING. Per ADR-020 §Resolved (2026-05-02): the doctor flow surfaces hints; it never drives an install. Auto-install would require sudo and silently widen the trust surface — operators must run the install command themselves so the credential prompt + package source stays under their control.
Shape: InstallHint(goos, engine) → multi-line string. Empty string means "no hint applies" (engine name unknown for that OS, or the engine is intrinsic and never missing — e.g. noop).
Wired into `clawtool sandbox doctor` (see internal/cli/sandbox.go) so each engine reported Available=false gets the matching hint appended to the human output. JSON output is unchanged — the hint is operator-facing prose, not a wire field.
Package sandbox — runner.go is the one-shot execution helper shared by `clawtool sandbox run` (CLI escape hatch) and the SandboxRun MCP tool (chat-driven callers). Both surfaces want the same primitive: take a parsed Profile + (command, args, stdin), spawn it inside the host-native engine, capture stdout/stderr/exit_code, return after a deadline.
Intentionally tiny — engines already do the heavy lifting via Engine.Wrap; this file only adds:
- exec.Cmd construction with stdin / pipe wiring
- timeout + process-group reaping (mirrors the Bash tool's contract: output preserved across SIGKILL)
- a wire-shape struct callers can serialize directly
Lives next to sandbox.go so it picks up engineRegistry + SelectEngine without importing core / cli (no cycle risk — callers in CLI / MCP tool layers consume RunOneShot).
Package sandbox implements ADR-020. Engine adapters wrap an exec.Cmd with host-native isolation primitives — bwrap on Linux, sandbox-exec on macOS, Docker as a portable fallback, noop where nothing is available.
Per ADR-007 each engine shells out to its primitive's binary; we never re-implement seccomp / AppContainer / namespaces.
v0.18 (this iteration) ships the surface + Engine interface + Profile parser + a working noop engine. Real bwrap / sandbox-exec / docker adapters land in v0.18.1+ — the same incremental pattern v0.16.4 used for `mcp` before v0.17.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func InstallHint ¶ added in v0.22.135
InstallHint returns operator-friendly multi-line install instructions for (goos, engine), or an empty string when no hint applies. Caller checks `if hint := InstallHint(...); hint != "" { ... }` and renders the hint verbatim.
Recognised engines: "bwrap", "sandbox-exec", "docker", "noop". Recognised goos: "linux", "darwin", "windows". Anything else returns "".
NOTE: hints describe operator-driven installs. We never run sudo, never invoke a package manager, never download a binary on the operator's behalf. The function returns prose; the human types the command.
Types ¶
type Engine ¶
type Engine interface {
// Name is the engine's identifier — e.g. "bwrap",
// "sandbox-exec", "docker", "noop". Surfaced in
// `clawtool sandbox doctor` output.
Name() string
// Available reports whether the engine's underlying primitive
// is usable on this host (binary on PATH, kernel feature
// present, etc.).
Available() bool
// Wrap mutates cmd so it runs inside the engine's sandbox
// using the supplied profile. Caller still calls cmd.Start /
// cmd.Wait — Wrap doesn't run anything itself.
Wrap(ctx context.Context, cmd *exec.Cmd, profile *Profile) error
}
Engine wraps an exec.Cmd with sandbox constraints.
func SelectEngine ¶
func SelectEngine() Engine
SelectEngine picks the primary engine available on this host, or the noop engine when nothing is. Engines are registered by per-OS init() calls into engineRegistry.
type EngineStatus ¶
AvailableEngines returns every registered engine's Available status. Used by `clawtool sandbox doctor`.
func AvailableEngines ¶
func AvailableEngines() []EngineStatus
type EnvPolicy ¶
EnvPolicy filters host env vars. Both Allow and Deny accept glob patterns matched via filepath.Match. Allow is checked first; Deny then trims matching entries from the result.
type Limits ¶
type Limits struct {
Timeout time.Duration // 0 = no per-call timeout
MemoryBytes int64 // 0 = unconstrained
ProcessCount int // 0 = unconstrained (cgroup pids.max)
}
Limits packages the resource caps.
type NetworkPolicy ¶
type NetworkPolicy struct {
// Mode is one of: "none" | "loopback" | "allowlist" | "open".
Mode string
// Allow is honoured only when Mode == "allowlist". Each
// entry is "host:port" — engines translate to nft rules /
// pf anchors / docker --add-host depending on the primitive.
Allow []string
}
NetworkPolicy describes egress restrictions.
type PathRule ¶
PathRule is one filesystem entry. Path is resolved against the caller's CWD when relative; engines bind it into the sandboxed view at the same logical location.
type Profile ¶
type Profile struct {
Name string
Description string
Paths []PathRule
Network NetworkPolicy
Limits Limits
Env EnvPolicy
}
Profile is the typed view of one [sandboxes.<name>] block. Engines convert this into their primitive's flags.
func ParseProfile ¶
func ParseProfile(name string, cfg config.SandboxConfig) (*Profile, error)
ParseProfile turns a config.SandboxConfig into a typed Profile. Returns a clear error per malformed field so the wizard / CLI can surface exactly what the operator typed wrong.
type RunRequest ¶ added in v0.22.124
type RunRequest struct {
Profile *Profile
Command string
Args []string
Stdin string
Timeout time.Duration
}
RunRequest is the typed input for RunOneShot. Profile is the already-parsed sandbox profile (caller did config lookup + ParseProfile); Command + Args are what to actually exec inside the sandbox; Stdin is an optional payload piped to the child.
Timeout = 0 means "use the engine's default" (60s). Negative values are clamped to the default.
type RunResult ¶ added in v0.22.124
type RunResult struct {
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
ExitCode int `json:"exit_code"`
TimedOut bool `json:"timed_out"`
Engine string `json:"engine"`
Profile string `json:"profile"`
}
RunResult is the wire-shape every RunOneShot caller surfaces — CLI's `sandbox run` JSON path and the SandboxRun MCP tool both serialize this directly. Field names mirror the Bash tool's response so chat-driven callers can compose the two without a translation layer.
func RunOneShot ¶ added in v0.22.124
func RunOneShot(ctx context.Context, req RunRequest) (RunResult, error)
RunOneShot spawns command+args inside the engine selected for this host, wrapping with profile, with the supplied stdin and timeout. Output is captured even when the deadline kills the child — same contract as core.executeBash, just sandbox-aware.
Returns an error only on setup failure (no command, profile nil, engine wrap rejection). Process exit codes — including non-zero — are returned via RunResult.ExitCode without an error so callers can render a structured failure rather than crashing on a SIGTERM exit-status.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package egress is the HTTP/HTTPS allowlist proxy that sandbox workers route their network traffic through (ADR-029 phase 4, task #209).
|
Package egress is the HTTP/HTTPS allowlist proxy that sandbox workers route their network traffic through (ADR-029 phase 4, task #209). |
|
Package worker — daemon-side client for the sandbox worker (ADR-029 phase 1).
|
Package worker — daemon-side client for the sandbox worker (ADR-029 phase 1). |