Skip to main content
Sandbox backends provide isolated command execution environments with explicit shell control to prevent injection attacks while enabling shell features when needed.
from praisonaiagents import Agent

agent = Agent(
    name="Coder",
    instructions="Run shell commands safely",
    sandbox=True,
)
agent.start("List Python files in the current directory")
The user runs commands through the agent; the sandbox backend isolates execution with explicit shell control.

Quick Start

1

Simple Usage

Enable sandbox on the agent — subprocess backend is the default:
from praisonaiagents import Agent

agent = Agent(
    name="System Agent",
    instructions="Execute system commands safely",
    sandbox=True,
)
agent.start("List files in the current directory")
2

With Configuration

Pick a specific backend via SandboxConfig or the CLI --sandbox-type flag:
from praisonaiagents import Agent, SandboxConfig

agent = Agent(
    name="Data Agent",
    instructions="Process data in an isolated container",
    sandbox=SandboxConfig.docker("python:3.11-slim"),
)
agent.start("Run pip list and summarise installed packages")
praisonai sandbox run --code "print('hello')" --type docker

How It Works

BackendUse CaseSecurity Level
SubprocessSandboxLocal development, scriptsMedium (OS-level isolation, POSIX only)
DockerSandboxProduction, untrusted codeHigh (container isolation)
SSHSandboxRemote executionHigh (network isolation)

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 seven sandboxes through SandboxRegistry — selectable by string name from the CLI or Python.
NameClassTypical use
dockerDockerSandboxContainer isolation for production
subprocessSubprocessSandboxFast local development (default)
sandlockSandlockSandboxHardened local sandbox
sshSSHSandboxRemote server execution
modalModalSandboxModal cloud sandboxes
daytonaDaytonaSandboxNot implemented — use subprocess/docker/e2b
e2bE2BSandboxE2B cloud code interpreter
Plugin-registered backends (installed separately, resolved via the praisonai.sandbox entry-point group):
NameSourceTypical use
capsulePlugin (via praisonai.sandbox)Plugin-provided secure sandbox — install via praisonai-plugins[capsule]

Select by Name

# Run code in any registered backend
praisonai sandbox run --code "print('hello')" --type subprocess
praisonai sandbox run --code "print('hello')" --type e2b
praisonai sandbox run --file script.py --type modal
praisonai sandbox run --code "ls" --type docker --image python:3.11-slim

Installing Optional Backends

Only subprocess and sandlock ship in the base install — every other backend requires an optional extra.
BackendInstall command
subprocessBuilt in — no extra required
sandlockBuilt in — no extra required
dockerpip install "praisonai[docker]"
sshpip install "praisonai[ssh]"
modalpip install "praisonai[modal]"
daytonapip install "praisonai[daytona]"
e2bpip install "praisonai[e2b]"
capsulepip install "praisonai-plugins[capsule]"
Selecting an uninstalled backend exits with code 2 and prints a fix-it hint — no silent downgrade to subprocess:
$ praisonai sandbox run --code "print('hi')" --type modal
Error: sandbox 'modal' is unavailable: <reason>
Available: ['subprocess', 'sandlock']
To install the optional backend:  pip install "praisonai[modal]"
Or explicitly choose another sandbox:  --sandbox-type subprocess
$ echo $?
2
You’ll never get an unexpected backend — if --sandbox-type X isn’t available, the CLI tells you exactly what to install.
For plugin-registered backends (like 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:
ValueError: Unknown sandbox type: 'capsule'. Available: ['docker', 'subprocess', 'sandlock', 'ssh', 'modal', 'daytona', 'e2b']
When praisonai itself is not installed (the registry import fails):
ValueError: Unknown sandbox type: 'capsule'. Supported built-ins: 'docker', 'subprocess', 'e2b', 'sandlock', 'ssh', 'modal', 'daytona'. Install a plugin package that registers 'capsule' under the 'praisonai.sandbox' entry-point group (e.g. pip install praisonai-plugins[capsule]).

Third-Party Sandbox Plugins

Register custom sandboxes via the praisonai.sandbox entry-point group:
# pyproject.toml
[project.entry-points."praisonai.sandbox"]
my-sandbox = "my_pkg.sandbox:MySandbox"
After pip install, the new name appears alongside the built-ins when you call registry.list_names().

Example: Capsule (from praisonai-plugins)

pip install "praisonai-plugins[capsule]"
from praisonaiagents import Agent, SandboxConfig

agent = Agent(
    name="SecureRunner",
    instructions="Execute code inside the Capsule sandbox.",
    sandbox=SandboxConfig.capsule(),   # strict security policy applied automatically
)

agent.start("Run a Python script")
The 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.

Which Sandbox Should I Pick?


Configuration Options

Shell Parameter Control

# String commands are parsed safely
result = await sandbox.run_command("python script.py --arg value")

# List commands are executed directly
result = await sandbox.run_command(["python", "script.py", "--arg", "value"])
Security: No shell injection possible. String commands are parsed with shlex.split().
Set shell=True only when you need shell features (pipes, &&, globbing). With untrusted input always keep shell=False.

Decision Guide

Use CaseRecommended shell Value
Running a single executable with argumentsFalse
Pipelines (grep | sort)True
Globs and env-var expansionTrue
Untrusted / model-generated commandsFalse

Common Patterns

Backend Selection

from praisonai.sandbox import SubprocessSandbox

# Local development with subprocess
sandbox = SubprocessSandbox()
result = await sandbox.run_command("python test.py")

Safe Data Processing

from praisonai.sandbox import DockerSandbox

sandbox = DockerSandbox()

# Process user data safely
async def process_file(filename):
    # Safe: no shell injection possible
    result = await sandbox.run_command([
        "python", "process.py", "--input", filename
    ], shell=False)
    return result.stdout

# Process with shell features when controlled
async def count_errors(log_file):
    import shlex
    # Trusted input, need shell features  
    result = await sandbox.run_command(
        f"grep 'ERROR' {shlex.quote(log_file)} | wc -l",
        shell=True
    )
    return int(result.stdout.strip())

Resource Limits

from praisonai.sandbox import ResourceLimits, SubprocessSandbox

limits = ResourceLimits(
    timeout_seconds=30,
    memory_mb=512
)

sandbox = SubprocessSandbox()
result = await sandbox.run_command(
    "python heavy_task.py",
    limits=limits,
    shell=False
)

Best Practices

Model-generated commands or user input should never use shell=True to prevent injection attacks. The default shell=False provides automatic protection.
# ✅ Safe with any user input
user_script = request.get("script")
result = await sandbox.run_command(f"python {user_script}", shell=False)

# ❌ Vulnerable to injection
result = await sandbox.run_command(f"python {user_script}", shell=True)
If you must use shell=True, quote all dynamic arguments with shlex.quote():
import shlex

filename = user_input  # Could contain special characters
command = f"process.py --file {shlex.quote(filename)}"
result = await sandbox.run_command(command, shell=True)
Using argument lists avoids shell parsing entirely:
# ✅ Clear and injection-safe
result = await sandbox.run_command([
    "python", "script.py", 
    "--input", input_file,
    "--output", output_file
], shell=False)

# ❌ Requires careful quoting
import shlex
command = f"python script.py --input {shlex.quote(input_file)} --output {shlex.quote(output_file)}"
result = await sandbox.run_command(command, shell=True)
Choose the sandbox backend based on your isolation requirements:
  • Development: SubprocessSandbox for speed and convenience (no longer inherits host environment)
  • Production: DockerSandbox for container-level isolation
  • Remote: SSHSandbox for network-isolated execution
  • High Security: Always use Docker or SSH backends with shell=False
Catch exit code 2 from praisonai sandbox run --type <X> and either install the extra or fall back to --sandbox-type subprocess:
praisonai sandbox run --code "print('hello')" --type docker
if [ $? -eq 2 ]; then
  echo "Docker not available, falling back to subprocess"
  praisonai sandbox run --code "print('hello')" --type subprocess
fi
In CI pipelines, install the required extra before running:
pip install "praisonai[docker]"
praisonai sandbox run --code "print('hello')" --type docker

Sandbox

Agent-level sandbox=True and SandboxConfig

Sandbox CLI

CLI reference for praisonai sandbox run and praisonai sandbox shell