Files an agent writes inside a remote sandbox reach the user through the outbound media path — see Remote-sandbox media.
Sandbox backends now ship in a dedicated
praisonai-sandbox package. If you use the agent-level API (sandbox=True, SandboxConfig), the existing from praisonai.sandbox import ... imports and praisonai extras continue to work — the standalone package is installed for you. Install praisonai-sandbox directly only when you want the sandbox stack without the full praisonai wrapper (e.g. embedding a sandbox in a small script or another project).Package layout. Sandbox backends and the vendor compute providers behind
tools_run_on= / run_in= (LocalCompute, DockerCompute, E2BCompute, ModalCompute, DaytonaCompute, FlyioCompute, TenkiCompute) both live in the standalone praisonai-sandbox package as of PR #4092. pip install praisonai pulls it in transitively; you can also install it alone with pip install praisonai-sandbox when you don’t need the rest of the framework. All praisonai.sandbox.* and praisonai.integrations.compute.* imports keep working via a compatibility shim, so existing agent code does not need to change. See The praisonai-sandbox Package for details.Quick Start
1
Simple Usage
Enable sandbox on the agent — subprocess backend is the default:
2
With Configuration
Pick a specific backend via
SandboxConfig or the CLI --sandbox-type flag:Default Docker image: the docker sandbox backend defaults to
python:3.12-slim. Both Agent(tools_run_on="docker") and Agent(sandbox=SandboxConfig.docker()) use the same image, so run_in="docker" and tools_run_on="docker" hand you the same Python runtime. Pass an explicit image= argument to override.How It Works
Docker Shared Workspace
DockerSandbox mounts a per-sandbox host directory at /sandbox and runs every command in it. That means:
write_file("data.txt", "…")places the file at/sandbox/data.txtinside the container.- The next
run_command("cat data.txt")sees it. execute(...)writes and reads the same directory — the script itself lands at/sandbox/code_<id>.py.- Each command is still
--rm; persistence comes from the mount, not from a reused container.
The
/sandbox mount is per-sandbox and persists for the sandbox’s lifetime, not per command. The SANDBOX_ROOT constant (value /sandbox) is importable from praisonai_sandbox.docker.Docker labels: two lifecycles
PraisonAI tags Docker containers with two different labels, one per lifecycle.praisonai=managed is the sole input to DockerCompute.list_instances() — the label praisonai managed ps filters on.
The two labels also carry different name shapes: managed instances are
praisonai_<id>, ephemeral execution containers are praisonai-<uuid> (named as of PR #4109 so a timed-out execution can be killed by name). managed ps deliberately does not list sandbox-exec containers — tagging --rm containers as managed would surface a name that managed stop could never reclaim. To see them directly:
Both spellings mean the same file
Pass either the container path (/sandbox/report.txt, the path you see inside the container) or the bare relative name (report.txt) to write_file / read_file — both resolve to the same file:
_sandbox_relative() (in praisonai_sandbox.docker) strips the exact /sandbox / /sandbox/… prefix before joining onto the sandbox root. A genuine subdirectory literally named sandbox still works — only the exact mount prefix is stripped.
Modal caveats
ModalSandbox functions are stateless, so it has no file storage — the opposite of Docker’s /sandbox mount above.
False return from write_file() on Modal is expected — callers that branch on the result (if not await sandbox.write_file(...)) can now detect the failed write instead of trusting a false True.
Docker SecurityPolicy enforcement
DockerSandbox enforces command-level SecurityPolicy clauses on every run_command() — the same enforcement SubprocessSandbox applies, run on the host before Docker is invoked (PR #4303, closing issue #4302). A refused command returns SandboxResult(status=FAILED, error=…) and Docker is never contacted.
allow_subprocess is deliberately not enforced on Docker. run_command() always dispatches through sh -c inside the container by design, so refusing shell binaries would refuse every command and defeat the backend, not harden it. The container is the boundary that clause approximates on the host.Which Backend Should I Use?
Pick a backend based on where the code runs and how much you trust it.All Built-in Sandbox Backends
PR #2003 exposes all built-in sandboxes throughSandboxRegistry — selectable by string name from the CLI or Python.
Plugin-registered backends (installed separately, resolved via the
praisonai.sandbox entry-point group):
Two different destination sets
Sandbox backends and compute providers are two separate sets — a name valid for one is not automatically valid for the other.Agent(sandbox=…) sandbox backends (via SandboxManager / SandboxRegistry) run explicit execute_code() calls. They include the local-only boundaries:
run_on= / compute= providers (via the compute bridge) place a whole flow’s tools or one LocalAgent’s tools. They are compute providers, not SandboxManager backends:
flyio and tenki are compute providers (run_on= / compute=) only — they are not Agent(sandbox=…) backends. sandlock, subprocess and ssh are sandbox backends only — they are not run_on= providers.As of PR #4071, novita is also accepted by run_on= (whole-loop hosting) and tools_run_on=, not just as a sandbox backend — so it no longer belongs on the “sandbox backends only” list. See Placement.Select by Name
- CLI
- Python
Using the Novita backend
Run agent code in a Novita cloud sandbox by selecting thenovita backend.
Novita requires
pip install "praisonai-sandbox[novita]" (or pip install "praisonai[novita]") and a NOVITA_API_KEY environment variable.Using the Daytona backend
Run agent code in a Daytona cloud sandbox by selecting thedaytona backend.
Daytona requires
pip install "praisonai[daytona]" (or pip install "praisonai-sandbox[daytona]") and a DAYTONA_API_KEY environment variable. Optional DAYTONA_API_URL and DAYTONA_TARGET env vars override the API endpoint and region.Using the Novita backend
Run agent code in a Novita cloud sandbox by selecting thenovita backend.
Novita requires
pip install "praisonai-sandbox[novita]" (or pip install "praisonai[novita]") and a NOVITA_API_KEY environment variable. The sandbox reads credentials from the environment at startup and fails loudly with RuntimeError if the key is missing. The backend follows the same lazy-load + async lifecycle pattern as the E2B and Daytona backends.Installing Optional Backends
Onlysubprocess and sandlock ship in the base install — every other backend requires an optional extra. The standalone praisonai-sandbox package is the preferred install; the legacy praisonai[...] extras still work as compatibility shims.
As of PR #4073 the
praisonai[e2b], praisonai[docker], and praisonai[daytona] extras are real and delegate to the sandbox package — earlier releases printed those install commands but the extras installed nothing.The
daytona backend is now a real cloud provider (DaytonaSandbox) backed by daytona-sdk, mirroring the Modal / E2B compute-provider pattern. It lives in the standalone praisonai-sandbox package — install it via praisonai-sandbox[daytona]. Set DAYTONA_API_KEY (and optional DAYTONA_API_URL) before selecting it.You’ll never get an unexpected backend — if
--sandbox-type X isn’t available, the CLI tells you exactly what to install.capsule), SandboxManager resolves the name through SandboxRegistry and raises a clearer error when the plugin is missing. When praisonai is installed but the plugin is not registered:
praisonai itself is not installed (the registry import fails):
Third-Party Sandbox Plugins
Register custom sandboxes via thepraisonai.sandbox entry-point group. Discovery is now driven from praisonai_sandbox._plugin_registry; the entry-point group name is unchanged.
pip install, the new name appears alongside the built-ins when you call registry.list_names().
Example: Capsule (from praisonai-plugins)
capsule backend is registered by praisonai-plugins under the praisonai.sandbox entry-point group. SandboxManager resolves the name via SandboxRegistry the first time the sandbox starts.
Using the standalone package
The built-in backends live in a dedicated package so you can use them without pulling in the fullpraisonai wrapper.
- Agent (default)
- Direct import
- CLI
- Legacy shim
Nothing to change — the standalone package is installed alongside
praisonai:Which Sandbox Should I Pick?
Configuration Options
Shell Parameter Control
- shell=False (Default)
- shell=True (Opt-in)
shlex.split().Decision Guide
Common Patterns
Backend Selection
- Development
- Production
- Remote
Safe Data Processing
Resource Limits
Best Practices
Always use shell=False for untrusted input
Always use shell=False for untrusted input
Model-generated commands or user input should never use
shell=True to prevent injection attacks. The default shell=False provides automatic protection.Quote arguments when building shell commands
Quote arguments when building shell commands
If you must use
shell=True, quote all dynamic arguments with shlex.quote():Prefer list form for complex commands
Prefer list form for complex commands
Using argument lists avoids shell parsing entirely:
Use appropriate backend for your security needs
Use appropriate backend for your security needs
Choose the sandbox backend based on your isolation requirements:
- Development:
SubprocessSandboxfor speed and convenience (no longer inherits host environment) - Production:
DockerSandboxfor container-level isolation - Remote:
SSHSandboxfor network-isolated execution - High Security: Always use Docker or SSH backends with
shell=False
Handle missing backends explicitly in scripts and CI
Handle missing backends explicitly in scripts and CI
Catch exit code 2 from In CI pipelines, install the required extra before running:
praisonai sandbox run --type <X> and either install the extra or fall back to --sandbox-type subprocess:Related
Where Does It Run
Ask any agent where its thinking and tools actually execute
Sandbox
Agent-level sandbox=True and SandboxConfig
praisonai-sandbox Package
Standalone backends and the praisonai_sandbox import path
Sandbox CLI
CLI reference for praisonai sandbox run and praisonai sandbox shell

