local

package
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package local is the IsolationNone sandbox backend: it runs commands as child processes on the host and confines file access to a root directory with a path fence. It is the lifted, generalized form of cmd/agent's old confineToRoot.

IsolationNone means there is NO real isolation for Exec — a shell command runs on the host with the agent's own privileges; the fence only governs the ReadFile/WriteFile/ListDir paths, not what a spawned process can touch. So this backend is for TRUSTED code only (code we wrote). Running untrusted, model-authored code requires a stronger backend (container/gVisor/microVM) behind the same sandbox.Sandbox interface — see ../../docs/specs/SANDBOX.md.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Options added in v0.2.0

type Options struct {
	// EnvAllowlist limits inherited command environment variables by name. A nil
	// allowlist preserves the full ambient environment.
	EnvAllowlist []string
}

Options configures a local sandbox.

type Sandbox

type Sandbox struct {
	// contains filtered or unexported fields
}

Sandbox is a host-local sandbox confined to a single root directory.

func New

func New(dir string) (*Sandbox, error)

New creates a local sandbox rooted at dir. dir must exist and be a directory; it is resolved to an absolute, cleaned path that becomes the fence boundary. It preserves the full ambient environment for commands.

func NewWithOptions added in v0.2.0

func NewWithOptions(dir string, opts Options) (*Sandbox, error)

NewWithOptions creates a local sandbox rooted at dir with opts.

func (*Sandbox) AppendFile

func (s *Sandbox) AppendFile(ctx context.Context, path string, data []byte, mode fs.FileMode) error

AppendFile implements sandbox.Appender: shell `>>` semantics inside the fence — data lands at the end of the file (created with mode if missing) without the existing contents ever entering memory.

func (*Sandbox) Capabilities

func (s *Sandbox) Capabilities() sandbox.Capabilities

Capabilities reports the truth: no isolation, host network. The runner can use this to refuse running untrusted code here.

func (*Sandbox) Close

func (s *Sandbox) Close() error

Close releases resources. For plain Exec the local backend holds none; but any long-lived StartProcess children are killed here so an abandoned run can't leak host processes (the local counterpart to docker's `rm -f`). Callers should still Kill processes they own; this is the backstop.

func (*Sandbox) Exec

func (s *Sandbox) Exec(ctx context.Context, cmd sandbox.Command) (*sandbox.Result, error)

Exec runs a command to completion. A non-zero exit (or a Timeout kill) is a normal *Result, not an error — only a genuine failure to start the command is returned as err (Principle 6: failures are observations).

func (*Sandbox) ListDir

func (s *Sandbox) ListDir(ctx context.Context, path string) ([]sandbox.DirEntry, error)

ListDir lists a directory within the fence.

func (*Sandbox) MakeDirAll added in v0.2.0

func (s *Sandbox) MakeDirAll(ctx context.Context, path string, mode fs.FileMode) error

MakeDirAll creates a directory tree within the fence.

func (*Sandbox) NewSession

func (s *Sandbox) NewSession(_ context.Context) (sandbox.Session, error)

NewSession opens a stateful session whose shell state persists in a per-session dir under the host temp dir (unique via pid + counter). The state lives OUTSIDE the workspace fence on purpose — it is our bookkeeping, not workspace content, and a local Exec subprocess writes there directly (the fence governs ReadFile/ WriteFile, not what a spawned process touches). Close removes the dir.

func (*Sandbox) ReadFile

func (s *Sandbox) ReadFile(ctx context.Context, path string) ([]byte, error)

ReadFile reads a file within the fence.

func (*Sandbox) ReadFileLimit

func (s *Sandbox) ReadFileLimit(ctx context.Context, path string, max int64) ([]byte, bool, error)

ReadFileLimit implements sandbox.LimitedReader: a bounded read that never pulls more than max bytes into memory (Principle 4). It reads max+1 bytes — one past the limit — to detect whether the file continued, without copying the tail.

func (*Sandbox) RealRoot

func (s *Sandbox) RealRoot() string

RealRoot returns the fence root with all symlinks resolved. The docker backend bind-mounts THIS (not the lexical Root) into the container, so the mounted host path is the canonical one the symlink-safe fence checks against — they can't disagree, and a symlinked root dir can't later be swapped to redirect the mount.

func (*Sandbox) RelInRoot

func (s *Sandbox) RelInRoot(path string) (string, error)

RelInRoot fences path exactly as ReadFile/Exec do, then returns it cleaned and RELATIVE to the root ("." for the root itself). The docker backend uses this to translate a sandbox-relative working dir into an in-container path while reusing the tested fence — an escaping path is REFUSED here, not silently clamped, so the container's working dir can't be aimed outside the workspace.

func (*Sandbox) Remove added in v0.2.0

func (s *Sandbox) Remove(ctx context.Context, path string) error

Remove removes one file or empty directory within the fence. It is never recursive.

func (*Sandbox) Root

func (s *Sandbox) Root() string

Root returns the absolute, cleaned fence root. It exists so a backend that COMPOSES this one — the docker backend embeds *local.Sandbox and must bind-mount the same resolved directory into the container (so a host write and an in-container read hit the same inode) — can read the path the fence resolved to, rather than re-resolving dir itself and risking a mismatch.

func (*Sandbox) SetMountAlias

func (s *Sandbox) SetMountAlias(prefix string)

SetMountAlias declares an absolute prefix that aliases the root: the in-container bind-mount point (e.g. "/workspace") when this backend is composed under a container backend. Paths arriving with that prefix are translated to root-relative before fencing, so a model that (correctly) believes it lives at the mount point can use absolute paths without being refused (DUET-DOGFOOD F4). A trailing slash is trimmed; "" disables the alias.

func (*Sandbox) StartProcess

func (s *Sandbox) StartProcess(ctx context.Context, cmd sandbox.Command) (sandbox.Process, error)

StartProcess launches cmd as a long-lived child confined to the workspace dir (cwd fenced like Exec). Streams are wired with explicit os.Pipe and caller-owned ends so reaping the process never races an in-flight reader (the os/exec StdoutPipe/Wait hazard). The child runs in its OWN process group so Kill can take down any grandchildren it spawned. cmd.Stdin/cmd.Timeout are ignored (P: live stdin + caller-managed lifetime).

func (*Sandbox) Workdir

func (s *Sandbox) Workdir() string

Workdir reports the directory Exec commands start in, as the model should name it (sandbox.WorkdirReporter). Plain host use: the absolute root. When a mount alias is set this backend is composed under a container and the model lives at the mount point, so the alias IS the model-visible cwd.

func (*Sandbox) WriteFile

func (s *Sandbox) WriteFile(ctx context.Context, path string, data []byte, mode fs.FileMode) error

WriteFile writes a file within the fence (parent directories must already exist).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL