Skip to content

Sandbox: cover all agent side effects, at any scope including per-run #326

Open

Description

@GEMISIS

Goal

The OS sandbox should be able to cover everything an agent can do with side effects, and be designatable at any scope: a specific run, an agent, a stage, or the entire world. Today it covers a subset, and one scope is missing.

What the sandbox covers today (verified in code)

  • Shell command execution: BuiltinTools routes shell through the ShellExecutor seam into the container/namespace (crates/leviath-tools/src/context.rs, exec.rs).
  • Seed commands: run inside the entry stage's sandbox when the agent declares one (crates/leviath-cli/src/daemon/seed_command.rs).
  • Rhai script shell(): routed through the agent's per-stage sandbox (crates/leviath-cli/src/daemon/script_host.rs).

Scopes: global default, per agent, per stage, cascading through resolve_sandbox (crates/leviath-core/src/sandbox.rs), with the right property that a manifest can strengthen but never weaken the user's setting.

Gaps

  1. Network tools bypass the sandbox entirely. web_fetch / web_search execute in the daemon process on the host, and Rhai fetch() does host networking through RealScriptIo. A sandbox with network = false still leaves the agent full host network access through these tools. This is the sharpest inconsistency: the config key reads like a network boundary and is not one.
  2. MCP servers run on the host, outside any sandbox. The shared MCP pool (crates/leviath-cli/src/daemon/mcp_pool.rs) spawns server processes directly, and they are shared across agents, so one sandboxed agent's MCP tool calls execute host-side with no containment and no per-agent separation.
  3. File tools are host-side. They are workdir-confined via canonicalized path containment (a real fence, and the sandbox bind-mounts the same workdir), but they do not execute inside the sandbox, so kernel attack surface and symlink/TOCTOU exposure remain host-level even for a fully sandboxed agent.
  4. No per-run designation. lev run has no --sandbox (or similar) override; designation is blueprint/config-level only. "Run this one untrusted task in a container" currently requires editing config or the blueprint.

Suggested end state

  • network = false denies web_fetch/web_search/Rhai fetch() for that agent (or routes them through the sandbox's network namespace), so the key means what it says.
  • An option to run an agent's MCP servers inside the agent's sandbox (accepting the loss of cross-agent pooling for sandboxed agents), or at minimum per-agent server instances for sandboxed agents.
  • File tools optionally executed via the sandbox when one is active, since the workdir is already bind-mounted.
  • A lev run --sandbox <kind> / --sandbox-image <img> spawn-time override, clamped by the same may-strengthen-never-weaken rule as resolve_sandbox.

Until then, the docs (security.md, comparison.md, integrations.md) describe the current scope truthfully: shell, seeds, and script shell() only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions