Skip to main content
Each isolation surface in PraisonAI guarantees something different — this page states exactly what, so you never mistake a restriction flag for a real boundary.

Quick Start

1

Isolate an explicit execute_code() call

Agent(sandbox=…) gives you the caller-invoked execute_code() API. It adds no tools and does not isolate tools= callables.
2

Isolate everything the model runs

AgentFlow(run_on="docker") puts every step’s shell and file tools inside one shared container boundary, so model-driven tools route through the shared sandbox.

Guarantee Matrix

Each surface below isolates a different thing — read the row before you rely on it.
With the docker backend, cross-command filesystem state is provided: write_file(), run_command(), and execute() share a /sandbox bind mount, so a file written by one is visible to the next. That mount does not expose the host filesystem (ls /Users fails), and code inside the container cannot swap a symlink into the mount to redirect the host’s write_file / read_file — both open descriptor-relative with O_NOFOLLOW at every path component. list_files() never returns host paths — even on platforms where TMPDIR sits behind a symlink (macOS /var → /private/var).

File persistence per backend

A True return from write_file() means the bytes are on disk; Modal is honest about writing nothing and returns False.
Modal has no file persistence. write_file() returns False and writes nothing, read_file() returns None, and execute_file() fails with a “no file storage” error. Pass code inline via execute(code=...), or use docker / subprocess / ssh when the workflow needs files. See Modal caveats.

Why sandbox= is not a capability grant

Agent(sandbox=…) is a restriction flag, not a way to hand the model an execution tool.
Agent(sandbox=…) does not add execute_python_code / execute_shell_command to agent.tools — that auto-injection was reverted in PR #3976. Giving the model a sandboxed execution tool must be a deliberate act by the caller, the way MCP() is. No peer framework grants execution capability from a config flag.
As of PR #4875, the subprocess backend’s execute() path enforces blocked_imports on Python code. The check parses the source with ast (no substring false positives from strings, comments, or longer identifiers) and resolves aliases back to their real dotted names. See the sandbox warning for the end-to-end example.
blocked_imports is now enforced on the subprocess execute() path. The rest of SecurityPolicy is still not enforced there (this warning is scoped to that backend and that path):
  • allow_network=False does not block outbound HTTPS — code importing a module that isn’t on the blocked list can still open a socket.
  • blocked_paths=['~/.ssh', ...] does not stop reading an SSH private key.
  • Command-level clauses (blocked_commands, allowed_commands, blocked_paths, allowed_paths) apply on run_command() only, not execute().
  • A separate process is not a security boundary.
For real containment, use a real container backend (docker / e2b), sandlock for a kernel-enforced local boundary (Landlock + seccomp-bpf), or AgentFlow(run_on=…).
DockerSandbox is different — as of PR #4303 it does enforce the command-level parts of SecurityPolicy on the host before Docker is invoked.
  • Enforced on docker: blocked_commands, allowed_commands, blocked_paths, allowed_paths on every run_command(); max_output_size on both run_command() and execute().
  • Not enforced on docker: allow_subprocess — the container itself is the boundary that clause approximates on the host, so blocking sh inside the container would refuse every command rather than harden it.
  • Not a substitute for the container: the container still bounds where code runs; policy enforcement is additional, not a replacement for the isolation boundary.
See Docker SecurityPolicy enforcement.
praisonai-sandbox must be installed to run any sandbox backend — pip install praisonaiagents alone is not enough. Install a backend, e.g. pip install "praisonai-sandbox[docker]".

Reliability: no leaked containers

Two guarantees close the container-leak story end to end — a timed-out execution and an exited run_on= script both leave zero running containers. Verified before/after: See Reclaim Stray Sandboxes for the full mechanism and Placement for how the leaks fit the placement story.

Common Patterns


Best Practices

Agent(sandbox=…) configures the explicit execute_code() API. It never hands the model a tool — add one yourself only when you intend the model to run code.
The default subprocess backend is for trusted development only. For untrusted or model-driven execution, use AgentFlow(run_on="docker"), LocalAgent(compute="docker"), or sandlock for a kernel-enforced local boundary.
With autonomy=True, the agent still carries the host execute_command tool. sandbox= does not protect that path — isolate the whole workflow with AgentFlow(run_on=…) instead.
pip install praisonaiagents cannot execute any sandbox. Install praisonai-sandbox with the backend you need before relying on isolation.

Sandbox

Configure the explicit execute_code() API and choose a backend

Shared Sandbox

Share one container across every agent with run_on=