Documentation
¶
Overview ¶
Package sandbox confines a child process's filesystem and network access using OS-level primitives.
Backends:
- native — no-op pass-through. For tests and explicit opt-out.
- bwrap — bubblewrap on Linux. Mounts a fresh root with allowlisted read/write binds, optional --unshare-net.
- sandboxexec — macOS Seatbelt via `sandbox-exec` and a generated .sb profile. Apple has deprecated the CLI but still uses it internally for App Sandbox; no replacement covers ad-hoc CLI sandboxing.
API shape mirrors Anthropic's sandbox-runtime: declare read paths, write paths, network policy, unix-socket allowlist, then call Wrap on a prepared exec.Cmd. The wrap is one-way — the caller drives Start / Wait as usual.
sb, err := sandbox.New() // bwrap on Linux, sandbox-exec on macOS
sb.WithReadPaths("/etc", "/usr").
WithWritePaths(workDir).
WithNetwork(sandbox.NetworkDeny)
cmd := exec.Command("bash", "-c", "...")
if err := sb.Wrap(cmd); err != nil { return err }
return cmd.Run()
Threat model: this package defends against accidental scope creep from tools spawned by an LLM agent — writes outside the workspace, surprise network calls, reads of $HOME secrets. It is NOT a defense against a hostile binary that's actively trying to escape; both bwrap and Seatbelt have known bypasses for unprivileged code that the kernel can't fully isolate (CVE history exists for both).
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNetworkLoopbackUnsupported = fmt.Errorf("sandbox: NetworkLoopback unsupported on this backend")
ErrNetworkLoopbackUnsupported is retained for the SandboxExec backend's parity. It used to fire for bwrap too; that's now implemented (see Wrap above). Kept as an exported sentinel in case a future backend reverts to "unsupported."
Functions ¶
This section is empty.
Types ¶
type NetworkPolicy ¶
type NetworkPolicy int
NetworkPolicy controls the child process's network access.
const ( // NetworkDeny severs the child's network entirely (Linux: // --unshare-net; macOS: deny network*). Default for new sandboxes. NetworkDeny NetworkPolicy = iota // NetworkOpen leaves networking unrestricted. Explicit opt-in only, // because nothing surfaces "this tool just made an outbound call." NetworkOpen // NetworkLoopback allows local 127.0.0.1 traffic only — the // plugin can bind/connect on loopback (so its gRPC handshake to // the parent works) but EVERY outbound or external connection // is blocked by the OS sandbox. // // This is the secure-by-default policy for codefly plugins: // they need loopback for the agent handshake, and they should // NOT be making outbound calls without an explicit grant. // // Backend status: // macOS sandbox-exec — implemented (rule on localhost ip). // Linux bwrap — NOT implemented; needs `ip link set lo up` // inside the unshared netns (the new netns // has lo DOWN by default). Falls back to // a clear error from Wrap so callers see // the limitation rather than silent // under/over-permissive behavior. NetworkLoopback )
type Sandbox ¶
type Sandbox interface {
// WithReadPaths grants read-only access to absolute paths.
WithReadPaths(paths ...string) Sandbox
// WithWritePaths grants read+write access to absolute paths.
WithWritePaths(paths ...string) Sandbox
// WithNetwork sets the network policy. Default is NetworkDeny.
WithNetwork(policy NetworkPolicy) Sandbox
// WithUnixSockets allows access to specific unix socket paths.
// Distinct from WithReadPaths because socket access on macOS is a
// separate Seatbelt category from filesystem read.
WithUnixSockets(paths ...string) Sandbox
// Wrap takes a prepared exec.Cmd and rewrites it to run inside the
// sandbox. The original cmd.Path / cmd.Args become the wrapped
// command's payload. Stdin/Stdout/Stderr, Env, Dir, and SysProcAttr
// are preserved.
//
// On native (no-op) the cmd is returned unmodified.
//
// Returns an error if the backend is unavailable (bwrap not on
// PATH, sandbox-exec missing) or the policy is malformed.
Wrap(cmd *exec.Cmd) error
// Backend returns the backend in use, for diagnostics.
Backend() Backend
}
Sandbox confines a child process's filesystem and network access.
Path declarations are advisory in the sense that the caller is responsible for declaring everything the child legitimately needs; missing declarations cause runtime denials, not silent breakage. Read paths grant ancestor traversal automatically (bwrap models this; the macOS profile mirrors it).
Sandboxes are single-use. Wrap mutates cmd.Args / cmd.Path — calling Wrap on the same cmd twice will produce nonsense.