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.
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

