agentix.sandbox¶
sandbox ¶
A sandboxing tool executor for untrusted tools and model-generated code.
:class:~agentix.executors.LocalToolExecutor runs tool functions in-process —
fine for trusted tools, but it cannot contain code you don't trust (a
code-interpreter tool, a shell command the model composed). It also can't honour
AgentPolicy.network_allowlist: an in-process Python call can open any socket.
:class:SubprocessExecutor runs each tool as a separate OS process and
applies the limits the loop hands it:
- Network — when the effective allowlist is empty, egress is denied by
launching the process in a fresh network namespace (Linux
unsharewith an unprivileged user namespace, auto-detected). If isolation can't be established andrequire_network_isolationis set (the default), the call fails closed rather than running untrusted code with network access. (Per-host allowlisting — a non-empty list — is not enforced here; that needs a filtering proxy or firewall. A non-empty list is treated as "network allowed", documented as such.) - CPU / memory / file size / processes — POSIX
setrlimitin the child. - Filesystem — each call runs in a fresh temporary working directory that is removed afterwards. (This is cwd isolation, not a chroot; true FS confinement still wants a container or mount namespace.)
- Environment — the child gets a minimal env (just
PATH) plus any names you explicitly pass through, so secrets in the parent process don't leak. - Timeout — the process group is killed if it overruns
timeout_s.
Pair it with tool_schemas describing the tools to the model, exactly like any
other :class:~agentix.executors.ToolExecutor.
Command
dataclass
¶
How a tool name maps to a subprocess.
argv is either a fixed argv list or a callable that builds one from
the tool-call args (never passed through a shell). If stdin is set, the
named arg's value is fed to the process on standard input — handy for a
"run this code" tool (argv=[python, "-"], stdin="code").
SandboxPolicy
dataclass
¶
SandboxPolicy(
cpu_seconds: float | None = 5.0,
memory_bytes: int | None = 512 * 1024 * 1024,
file_size_bytes: int | None = 16 * 1024 * 1024,
max_processes: int | None = 64,
max_output_bytes: int = 64 * 1024,
env: Mapping[str, str] | None = None,
env_passthrough: Sequence[str] = (),
workdir: str | PathLike[str] | None = None,
require_network_isolation: bool = True,
isolator: Sequence[str] | None = None,
)
Resource and isolation limits applied to every sandboxed process.
SubprocessExecutor ¶
SubprocessExecutor(
commands: Mapping[
str, Command | ArgvBuilder | Sequence[str]
],
*,
sandbox: SandboxPolicy | None = None,
)
A sandboxing :class:~agentix.executors.ToolExecutor.
commands maps tool name -> :class:Command (a bare argv list or
:class:ArgvBuilder is accepted as shorthand). Example::
import sys
from agentix.sandbox import SubprocessExecutor, Command
executor = SubprocessExecutor(
{"run_python": Command(argv=[sys.executable, "-"], stdin="code")}
)
# With AgentPolicy().network_allowlist empty (the default), run_python
# executes with no network access — or refuses if it can't guarantee that.
detect_network_isolator
cached
¶
Return a working argv prefix that drops a child into an empty network
namespace, or None if none is available. Probed once and cached.
Uses unshare with an unprivileged user namespace, which needs no root on
Linux hosts that allow user namespaces. The probe actually runs it (a bare
binary on PATH isn't proof the kernel permits it).