Documentation
¶
Overview ¶
Package sandbox holds the podman-backed Sandbox implementation (internal/agent.Sandbox) — a session- or worker-scoped rootless container that RunBashTool dispatches commands into via `podman exec` instead of running them directly on the host. See roadmap/sandbox-podman.md for the design.
This package mirrors internal/lsp's role: a sibling package holding an external-process lifecycle, imported only by the two session entry points (internal/tui, internal/oneshot) and internal/agent's dispatch worker wiring — never by internal/agent itself, which only needs the Sandbox interface.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type PodmanSandbox ¶
type PodmanSandbox struct {
// contains filtered or unexported fields
}
PodmanSandbox runs commands inside a single long-lived rootless podman container (`podman run -d ... sleep infinity`), dispatching each RunBashTool call via `podman exec`. One container per session or per dispatch write-worker — never per command (see roadmap/sandbox-podman.md for why a fresh container per command is rejected).
func NewPodmanSandbox ¶
func NewPodmanSandbox(ctx context.Context, cfg config.SandboxConfig, id, mountRoot string) (*PodmanSandbox, error)
NewPodmanSandbox starts a session-scoped container per cfg, bind-mounting mountRoot at the SAME absolute path inside the container (so paths the model uses are valid on both sides). id becomes part of the container name (e.g. a session ID or "<session>-<taskID>" for a dispatch worker).
Synchronous: blocks until the container is confirmed started. On any failure (podman missing, image pull failure, rootless setup issues) it returns a wrapped, actionable error and leaves no container running — callers must treat this as fatal to session/worker startup and must NEVER fall back to HostSandbox on error, which would silently defeat the isolation the caller asked for.
func (*PodmanSandbox) Close ¶
func (s *PodmanSandbox) Close() error
Close removes the container. See removeContainer — Close just adds the caller-facing error wrap; callers at the two documented call sites (session teardown, dispatch worker cleanup) treat it as non-fatal and swallow it, consistent with this codebase's existing best-effort cleanup convention (dispatch_cleanup.go).
func (*PodmanSandbox) Command ¶
Command returns a `podman exec` into this sandbox's container. Mirrors HostSandbox.Command's contract exactly: command is a single /bin/sh -c argument, so there is no new shell-injection surface beyond what already exists for the host path today.
The command is tagged with a unique marker and cmd.Cancel is set to best-effort kill the marked process (and its direct children) inside the container when ctx is canceled or times out. Without this, the default Cancel behavior (kill the local `podman exec` client) does NOT kill the process it started inside the long-lived container — verified empirically: a canceled `sleep 30` kept running, reparented to the container's PID 1, after the local client was killed. A plain process-group kill doesn't reach it either — this container setup gives no per-exec process group (children inherit the container's own group rather than a fresh one), also verified by direct test.
Known residual limitation: only the marked process and its DIRECT children are targeted, covering the wrapper shell + whatever it directly forks — the overwhelming majority of real run_bash shapes. A grandchild the command itself spawns can still survive as an orphan inside the container until Close() removes it. Full recursive-tree cleanup would need per-exec process-group isolation this container doesn't have.
func (*PodmanSandbox) Label ¶
func (s *PodmanSandbox) Label() string
Unambiguously "-sandbox", not just "[podman]" — a bare "[podman]" tag on a run_bash card reads like a statement about the command itself (e.g. "this ran podman"), not "this ran inside a podman sandbox container".