Skip to main content
Sandbox provides secure, isolated environments for executing code generated by AI agents, protecting your system from potentially harmful operations.

Quick Start

1

Simple Usage

Enable sandbox with a single line - the easiest way to get started.
from praisonaiagents import Agent

agent = Agent(
    name="Coder",
    instructions="Write and execute Python code safely.",
    sandbox=True  # one-line enable
)

agent.start("Calculate fibonacci(10) and print the result")
2

With Configuration

Use factory methods for specific sandbox types.
from praisonaiagents import Agent, SandboxConfig

agent = Agent(
    name="DataAnalyst",
    instructions="Analyze data with Python.",
    sandbox=SandboxConfig.docker("python:3.11-slim")
)

agent.start("Read CSV data and create a summary")
3

Full Configuration

Complete control over sandbox settings.
from praisonaiagents import Agent, SandboxConfig, ResourceLimits, SecurityPolicy

agent = Agent(
    name="SecureAgent",
    instructions="Execute code with strict security.",
    sandbox=SandboxConfig(
        sandbox_type="e2b",
        resource_limits=ResourceLimits(memory_mb=512, timeout_seconds=60),
        security_policy=SecurityPolicy.standard(),
    )
)

agent.start("Process sensitive data securely")

Agent Sandbox API

When sandbox is configured, agents gain powerful execution capabilities:
import asyncio
from praisonaiagents import Agent

agent = Agent(
    name="DataAnalyst",
    instructions="Analyze data with Python.",
    sandbox=True,
)

# Async pattern
async def main():
    result = await agent.execute_code("""
import statistics
data = [1, 2, 3, 4, 5, 100]
print(f"Mean: {statistics.mean(data)}")
print(f"Median: {statistics.median(data)}")
""")
    print(result.stdout)
    print(f"Status: {result.status}, Exit: {result.exit_code}")

asyncio.run(main())

# Sync pattern (for non-async code)
result = agent.execute_code_sync("print(2 + 2)")
print(result.stdout)

SandboxMixin API

MethodSignatureDescription
has_sandboxproperty -> boolWhether sandbox is configured
execute_codeasync (code, language="python", check_security=True, **kwargs) -> SandboxResultAsync code execution with optional security pre-check
execute_code_sync(code, language="python", check_security=True, **kwargs) -> SandboxResultSync wrapper around execute_code
run_shell_commandasync (command: str | list, check_security=True, **kwargs) -> SandboxResultRun shell command in sandbox
get_sandbox_status() -> Dict[str, Any]Returns {"configured", "config", "available_types", "current_type"}
sandbox_cleanupasync () -> NoneForce cleanup of sandbox resources
get_sandbox_manager() -> SandboxManager | NoneGet or lazily create the manager

Auto-Generated Agent Tools

When sandbox is set on an Agent, two tools are automatically available:
from praisonaiagents import Agent

agent = Agent(
    name="ShellAgent",
    instructions="Use execute_python_code or execute_shell_command to answer questions.",
    sandbox=True,
)

agent.start("What's the current working directory and the first 5 files in it?")
# The agent will autonomously call execute_shell_command("pwd && ls | head -n 5")
Available auto-generated tools:
  • execute_python_code(code: str) -> str — runs Python in the sandbox, returns stdout or error
  • execute_shell_command(command: str) -> str — runs shell command in the sandbox, returns output or error

Sandbox Backends

Choose the right backend for your security and performance needs:
Fastest, minimal isolation - development only
from praisonaiagents import SandboxConfig

config = SandboxConfig.subprocess()
agent = Agent(sandbox=config)
RequirementsNone (built-in)
Use caseDevelopment, trusted code only
Isolation⚠️ Limited - NOT for untrusted code
Performance✅ Fastest
Subprocess sandbox provides minimal isolation. Never use with untrusted code.
All backend imports are lazy-loaded. Importing the top-level package will not trigger heavy backend imports.

Security Pre-checks

Static code analysis warns about potentially dangerous patterns before execution:
from praisonaiagents.sandbox import check_code_safety, format_warnings

code = """
import os
os.system("rm -rf /tmp/test")
"""

warnings = check_code_safety(code, language="python")
print(format_warnings(warnings))
# Security analysis found 1 potential issue(s):
# HIGH RISK:
#   - Direct system command execution (line 3)
#     Context: os.system("rm -rf /tmp/test")
# Note: These are warnings only. The sandbox provides real isolation.

Security API

from praisonaiagents.sandbox import (
    check_code_safety,
    format_warnings,
    get_security_summary,
    SecurityWarning,
)
FunctionSignatureDescription
check_code_safety(code: str, language: str = "python") -> List[SecurityWarning]Runs regex + AST analysis. Supports python, bash, generic fallback
format_warnings(warnings: List[SecurityWarning]) -> strPretty-print warnings grouped by severity
get_security_summary(warnings) -> DictReturns {"total_warnings", "by_severity", "max_severity", "is_safe", "has_critical"}
SecurityWarning fields:
FieldTypeDescription
patternstrRegex pattern or AST node that triggered
messagestrHuman-readable warning
severitystr"low", "medium", "high", "critical"
line_numberOptional[int]Line number in code
contextOptional[str]Source line text
Security pre-checks do not block execution - they provide warnings only. The sandbox provides the real isolation.

Agent Integration

Agents automatically run security checks unless disabled:
result = await agent.execute_code(
    code="import os; os.system('ls')",
    check_security=True,  # default
)
# Warnings are stored in result.metadata["security_warnings"]

SandboxManager

Factory and async context manager for sandbox backends:
from praisonaiagents.sandbox import SandboxManager, SandboxConfig

# Convenience: one-shot run
config = SandboxConfig.docker("python:3.11-slim")
manager = SandboxManager(config)
result = await manager.run_code("print('Hello, World!')")

# Context manager: reuse one sandbox for multiple executions
async with SandboxManager(config) as sandbox:
    r1 = await sandbox.execute("x = 5")
    r2 = await sandbox.execute("print(x * 2)")  # persistence depends on backend

SandboxManager API

MethodDescription
__init__(config: SandboxConfig | None)Defaults to SandboxConfig.subprocess()
run_code(code, language="python", **kwargs) -> SandboxResultOne-shot: open, run, cleanup
__aenter__ / __aexit__Async context manager yielding SandboxProtocol
get_available_types() -> Dict[str, Dict]Returns availability info per backend

Configuration Options

Progressive disclosure from simple to advanced:
from praisonaiagents import Agent, SandboxConfig, ResourceLimits, SecurityPolicy

# Level 1: Bool (simplest)
agent = Agent(sandbox=True)

# Level 2: Factory shortcut
agent = Agent(sandbox=SandboxConfig.docker("python:3.11-slim"))

# Level 3: Full config
agent = Agent(
    sandbox=SandboxConfig(
        sandbox_type="e2b",
        resource_limits=ResourceLimits(memory_mb=512, timeout_seconds=60),
        security_policy=SecurityPolicy.standard(),
    )
)

SandboxConfig Options

OptionTypeDefaultDescription
sandbox_typestr"subprocess"Backend: subprocess, docker, e2b, native, sandlock, ssh, modal, daytona
imagestr"python:3.11-slim"Docker image (docker backend only)
working_dirstr"/workspace"Working directory within sandbox
envDict[str, str]{}Environment variables
resource_limitsResourceLimitsResourceLimits()CPU, memory, timeout limits
security_policySecurityPolicySecurityPolicy()File and network access rules
auto_cleanupboolTrueAuto-cleanup after execution
persist_filesboolFalseKeep files between runs
mount_pathsList[str][]Paths to mount (host:container format)
metadataDict[str, Any]{}Additional configuration

ResourceLimits Presets

from praisonaiagents import ResourceLimits

# Minimal limits for untrusted code
limits = ResourceLimits.minimal()  # 128MB, 30s, no network

# Standard limits  
limits = ResourceLimits.standard()  # 512MB, 60s

# Generous limits for trusted code
limits = ResourceLimits.generous()  # 2GB, 300s, network allowed
LimitMinimalStandardGenerous
memory_mb1285122048
timeout_seconds3060300
cpu_percent50100100
network_enabled

SecurityPolicy Presets

from praisonaiagents import SecurityPolicy

# Strict policy for untrusted code
policy = SecurityPolicy.strict()

# Standard policy
policy = SecurityPolicy.standard()

# Permissive policy (trusted code only)
policy = SecurityPolicy.permissive()

# Custom policy
policy = SecurityPolicy(
    allow_network=False,
    allow_file_write=True,
    allow_subprocess=False,
    blocked_paths=["/etc", "~/.ssh"],
    blocked_imports=["subprocess", "os.system"]
)

Common Patterns

from praisonaiagents import Agent, SandboxConfig, ResourceLimits

agent = Agent(
    name="DataScientist",
    instructions="Analyze data and create visualizations.",
    sandbox=SandboxConfig(
        sandbox_type="docker",
        image="python:3.11-slim",
        resource_limits=ResourceLimits.generous(),  # Need memory for data
        mount_paths=["./data:/workspace/data:ro"],   # Mount data read-only
    )
)

agent.start("Load the CSV file and show descriptive statistics")

Timeout Behavior & Resource Cleanup

Understanding how different backends handle timeouts and resource cleanup ensures your resource limits are properly enforced.
BackendLocal cleanup on timeoutRemote cleanup on timeout
subprocessProcess killedN/A
dockerClient process killed and docker kill <container> issued on the named container (praisonai-<execution_id>)Container stopped — resource limits enforced
sshLocal SSH client killedRemote process terminated via timeout N sh -c ... wrapper; remote temp file cleaned up via finally (even on error)
e2b, modal, native, sandlock, daytonaBackend-managedBackend-managed

Docker Timeout Handling

Docker containers get deterministic names and are properly killed on timeout: Why containers are no longer orphaned: Every docker run is launched with --name praisonai-<execution_id>. On timeout, the sandbox issues docker kill <name> to stop the actual container — not just detach the client. Your memory_mb and cpu_percent limits are now enforced through the entire execution lifecycle.

SSH Timeout Handling

SSH backend prevents both remote process leaks and temp file accumulation: Remote process cleanup: Commands are wrapped with timeout N sh -c ... to ensure remote processes terminate even if the SSH connection drops. Temp file cleanup: File cleanup (rm -f) is now in a finally block. Even if execution raises (timeout, network blip), the remote temp file is removed. Cleanup errors are swallowed so they never mask the real execution result.

Best Practices

Always use Docker sandbox when executing code from untrusted sources. Subprocess isolation is not sufficient for security-critical applications.
Keep check_security=True (default) when calling execute_code(). Review warnings in result.metadata["security_warnings"] for insights.
Configure memory and timeout limits based on expected workload. Start with minimal limits and increase as needed.
Keep allow_network=False unless code specifically needs network access. This prevents data exfiltration.
For multiple operations on the same sandbox, use async with SandboxManager(config) as sandbox: to reuse the environment efficiently.

Sandboxed Agent

Complete agent with built-in sandbox

Tools

Extend agent capabilities