> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# praisonai-sandbox Package

> Standalone sandbox backends for PraisonAI — install only what you need

Use `praisonai-sandbox` when you only need sandboxed execution — no gateway, no CLI wrapper — or when you want the sandbox backends on a slim install. It's a standalone Tier‑2 package extracted from the `praisonai` wrapper, so agents keep the same `sandbox=True` behaviour with a much smaller dependency tree.

<Note>
  Files an agent writes inside a remote sandbox reach the user through the outbound media path — see [Remote-sandbox media](/docs/features/outbound-media-delivery#remote-sandbox-media-modal-e2b-daytona-fly-io-ssh).
</Note>

<Info>
  Standalone `praisonai-sandbox` still guards built-in provider names against pip-plugin shadowing — the vendored `PluginRegistry` fallback carries the same check since [PR #4184](https://github.com/MervinPraison/PraisonAI/pull/4184). See [Plugin Precedence](/docs/features/plugin-precedence#every-registry-is-guarded).
</Info>

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="Coder",
    instructions="Run code safely",
    sandbox=True,  # subprocess backend, no extras needed
)
agent.start("Print the current directory tree")
```

The user asks the agent to run code; the sandbox package executes it in an isolated backend and returns the result.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    A[🤖 Agent] --> P[📦 praisonai-sandbox]
    P --> S1[subprocess]
    P --> S2[docker]
    P --> S3[e2b]
    P --> S4[modal]
    P --> S5[ssh]
    P --> S6[sandlock]
    P --> S7[daytona]
    P --> S8[novita]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef pkg fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef backend fill:#10B981,stroke:#7C90A0,color:#fff

    class A agent
    class P pkg
    class S1,S2,S3,S4,S5,S6,S7,S8 backend
```

## Quick Start

<Steps>
  <Step title="Run an agent with the default backend">
    The `subprocess` backend ships in the base install — nothing else to configure:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent

    agent = Agent(
        name="Coder",
        instructions="Run code safely",
        sandbox=True,
    )
    agent.start("Print the current directory tree")
    ```
  </Step>

  <Step title="Pick a specific backend">
    Choose a backend with `SandboxConfig`:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent, SandboxConfig

    agent = Agent(
        name="DataAnalyst",
        instructions="Analyze data with Python.",
        sandbox=SandboxConfig.docker("python:3.12-slim"),
    )
    agent.start("Read CSV data and create a summary")
    ```
  </Step>

  <Step title="Install the slim package and optional backends">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    pip install praisonai-sandbox                 # slim install, subprocess backend
    pip install "praisonai-sandbox[docker]"
    pip install "praisonai-sandbox[e2b]"
    pip install "praisonai-sandbox[modal]"
    pip install "praisonai-sandbox[ssh]"
    pip install "praisonai-sandbox[sandlock]"
    pip install "praisonai-sandbox[daytona]"      # pulls daytona-sdk; set DAYTONA_API_KEY
    pip install "praisonai-sandbox[novita]"       # pulls novita-sandbox; set NOVITA_API_KEY
    pip install "praisonai-sandbox[all]"          # every backend at once
    ```
  </Step>

  <Step title="List available backends">
    ```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    praisonai-sandbox backends
    ```

    ```
    daytona: unavailable
    docker: unavailable
    e2b: unavailable
    modal: unavailable
    novita: unavailable
    sandlock: unavailable
    ssh: unavailable
    subprocess: available
    ```

    Each backend reports `available` or `unavailable` based on whether its optional dependency is installed.
  </Step>
</Steps>

<Note>
  The standalone `praisonai-sandbox` console script currently exposes `--help` and `backends`. To **run** code or open an interactive **shell**, use the wrapper CLI — `praisonai sandbox run` / `praisonai sandbox shell` (see [Sandbox CLI](/docs/cli/sandbox)).
</Note>

<Info>
  The slim install is not less safe: its vendored `PluginRegistry` fallback enforces the same built-in guard, so a pip package publishing `docker` cannot replace the built-in ([PR #4184](https://github.com/MervinPraison/PraisonAI/pull/4184)). See [Plugin Precedence](/docs/features/plugin-precedence#every-registry-is-guarded).
</Info>

## Do I Need to Install It Directly?

Most users never install `praisonai-sandbox` on its own — the full wrapper already bundles it.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{Do I need praisonai-sandbox directly?}:::question
    Q -->|Using the full stack| A[No — pip install praisonai<br/>bundles every backend]:::ok
    Q -->|Want a minimal footprint| B[Yes — pip install<br/>praisonai-sandbox extras]:::pick

    classDef question fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
    classDef pick fill:#189AB4,stroke:#7C90A0,color:#fff
```

***

## Backends Included

Every backend is selectable by name through `praisonaiagents.sandbox.SandboxManager` — the same registry the wrapper uses.

| Name | Class | Typical use |
| - | - | - |
| `subprocess` | `SubprocessSandbox` | Fast local development (base install) |
| `sandlock` | `SandlockSandbox` | Hardened local sandbox (`[sandlock]`) |
| `docker` | `DockerSandbox` | Container isolation for production (`[docker]`) |
| `ssh` | `SSHSandbox` | Remote server execution (`[ssh]`) |
| `modal` | `ModalSandbox` | Modal cloud sandboxes (`[modal]`) — **stateless: no file persistence via `write_file`/`read_file`; use `execute()` with inline source** |
| `e2b` | `E2BSandbox` | E2B cloud code interpreter (`[e2b]`) |
| `daytona` | `DaytonaSandbox` | Daytona cloud sandboxes (`[daytona]` + `DAYTONA_API_KEY`) |
| `novita` | `NovitaSandbox` | Novita cloud sandboxes (`[novita]` + `NOVITA_API_KEY`) |

<Note>
  Only `subprocess` ships in the base `praisonai-sandbox` install. Every other backend — including `sandlock` — requires its matching extra. This differs from the full `praisonai` wrapper, where `subprocess` **and** `sandlock` are both built in.
</Note>

***

## Install Matrix

Base install pulls the core dependencies; each backend is an optional extra.

| Extra | Command | Pulls |
| - | - | - |
| *(base)* | `pip install praisonai-sandbox` | `praisonaiagents`, `rich`, `typer`, `click` |
| `docker` | `pip install "praisonai-sandbox[docker]"` | `docker>=7.0.0` |
| `e2b` | `pip install "praisonai-sandbox[e2b]"` | `e2b-code-interpreter>=1.0.0` |
| `sandlock` | `pip install "praisonai-sandbox[sandlock]"` | `sandlock>=0.1.0` |
| `ssh` | `pip install "praisonai-sandbox[ssh]"` | `asyncssh>=2.14.0` |
| `modal` | `pip install "praisonai-sandbox[modal]"` | `modal>=0.64.0` |
| `daytona` | `pip install "praisonai-sandbox[daytona]"` | `daytona-sdk` (set `DAYTONA_API_KEY`) |
| `novita` | `pip install "praisonai-sandbox[novita]"` | `novita-sandbox>=2.0.0` (set `NOVITA_API_KEY`) |
| `all` | `pip install "praisonai-sandbox[all]"` | all of the above |

***

## Compute providers live here too

As of [PR #4092](https://github.com/MervinPraison/PraisonAI/pull/4092), the seven vendor **compute providers** that `tools_run_on=` / `run_in=` / `run_on=` reach also live in this package — next to the sandbox backends they share a vendor with.

You never import them by hand. Name the vendor on the agent and the provider resolves from `praisonai_sandbox.compute`:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent

agent = Agent(
    name="Runner",
    instructions="Run code in an E2B cloud sandbox.",
    tools_run_on="e2b",
)
agent.start("Print the Python version")
```

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
pip install "praisonai-sandbox[e2b]"   # enough to reach tools_run_on="e2b"
```

That single install is all you need — no full `praisonai` wrapper.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    subgraph "praisonai-sandbox"
        subgraph "SandboxProtocol"
            SB1[DockerSandbox]
            SB2[E2BSandbox]
            SB3[SubprocessSandbox]
        end
        subgraph "ComputeProviderProtocol"
            CP1[DockerCompute]
            CP2[E2BCompute]
            CP3[LocalCompute]
        end
    end

    Agent[🤖 Agent] -->|sandbox=| SB1
    Agent -->|tools_run_on= / run_in=| CP2

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef sandbox fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef compute fill:#6366F1,stroke:#7C90A0,color:#fff

    class Agent agent
    class SB1,SB2,SB3 sandbox
    class CP1,CP2,CP3 compute
```

The two protocol stacks stay **separate** — a sandbox backend and a compute provider for the same vendor (docker, e2b, modal, daytona) are still two different classes. PR #4092 only co-locates them; it does not merge them.

### Which install do I actually need?

Reaching a vendor used to pull vastly different dependency trees. `run_in="e2b"` resolved **58** packages via `praisonai-sandbox[e2b]`; `tools_run_on="e2b"` resolved **143** through the `praisonai` wrapper. After PR #4092 both spellings reach the same provider through the same slim package.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{What do I need?}:::question
    Q -->|Just run code in a sandbox| S["pip install<br/>praisonai-sandbox[e2b]"]:::pick
    Q -->|tools_run_on= / run_in=| S
    Q -->|Full stack: gateway, CLI, bots| W["pip install praisonai"]:::ok

    classDef question fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef pick fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff
```

### Import paths

Old `praisonai.integrations.compute.*` imports still work through `sys.modules` **module aliases** — the same module object is reachable from both paths.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# New (canonical)
from praisonai_sandbox.compute import DockerCompute

# Backward-compatible alias (still works)
from praisonai.integrations.compute import DockerCompute
```

| Provider | Canonical location | Backward-compatible alias |
| - | - | - |
| `LocalCompute` | `praisonai_sandbox.compute.local` | `praisonai.integrations.compute.local` |
| `DockerCompute` | `praisonai_sandbox.compute.docker` | `praisonai.integrations.compute.docker` |
| `E2BCompute` | `praisonai_sandbox.compute.e2b` | `praisonai.integrations.compute.e2b` |
| `ModalCompute` | `praisonai_sandbox.compute.modal_compute` | `praisonai.integrations.compute.modal_compute` |
| `DaytonaCompute` | `praisonai_sandbox.compute.daytona` | `praisonai.integrations.compute.daytona` |
| `FlyioCompute` | `praisonai_sandbox.compute.flyio` | `praisonai.integrations.compute.flyio` |
| `TenkiCompute` | `praisonai_sandbox.compute.tenki` | `praisonai.integrations.compute.tenki` |

<Note>
  The shims are `sys.modules` module aliases, not `from … import X` re-exports. Both paths point at the **same module object**, so monkeypatching a module-level name through either path lands on the real module — tests that patch via the old import keep working unchanged.
</Note>

***

## Use the Backends Directly

Import a backend class straight from the package when you want the isolated runtime without an `Agent`:

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
import asyncio
from praisonaiagents.sandbox import SandboxConfig, SandboxManager

async def main():
    manager = SandboxManager(SandboxConfig.subprocess())
    result = await manager.run_code("print('hello from the sandbox')")
    print(result.stdout)

asyncio.run(main())
```

## Platform support

`praisonai-sandbox` runs on Linux, macOS, and Windows — the subprocess backend enforces different isolation on each, and the cloud backends (Docker, E2B, Modal, Daytona, Novita, SSH) behave identically everywhere.

| Backend | Linux / macOS | Windows |
| - | - | - |
| `subprocess` | Full `setrlimit`, process-group kill, symlink escape rejection | No `setrlimit`, leader-only kill; symlink protection only when Developer Mode / `SeCreateSymbolicLinkPrivilege` is present |
| `docker` | ✅ Full container isolation | ✅ Full container isolation (Docker Desktop) |
| `e2b`, `daytona`, `novita` | ✅ Cloud isolation | ✅ Cloud isolation |
| `modal` | ✅ Cloud isolation — **stateless, no file storage** | ✅ Cloud isolation — **stateless, no file storage** |
| `ssh` | ✅ Isolation is provided by the remote host | ✅ Isolation is provided by the remote host |
| `sandlock` | ✅ Landlock / seatbelt | ❌ Not available |

<Warning>
  On Windows, the built-in `subprocess` backend enforces fewer platform limits than on POSIX. Use `docker` or a cloud backend when running untrusted code. See the [main sandbox page](/docs/docs/features/sandbox#resource-limits-in-practice) for the full capability matrix and [PR #3224](https://github.com/MervinPraison/PraisonAI/pull/3224) for background.
</Warning>

<Note>
  **Modal is stateless.** `write_file()` returns `False`, `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](/docs/docs/features/sandbox-backends#modal-caveats).
</Note>

***

Every backend is lazy-loaded — importing one never imports the others.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonai_sandbox import (
    DockerSandbox,
    SubprocessSandbox,
    SandlockSandbox,
    SSHSandbox,
    ModalSandbox,
    DaytonaSandbox,
    NovitaSandbox,
    E2BSandbox,
    NovitaSandbox,
)
```

***

## Backward Compatibility

The old `praisonai.sandbox` import path still works through shims.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
# New (recommended)
from praisonai_sandbox import SubprocessSandbox

# Backward-compatible shim (still works)
from praisonai.sandbox import SubprocessSandbox
```

Use `praisonai_sandbox` in new code; the shim keeps existing scripts running unchanged.

***

## How It Fits with SandboxManager

`SandboxManager` stays the entry point — agents reach backends through the registry, never by importing them directly.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    Agent[🤖 Agent] --> Manager[⚙️ SandboxManager]
    Manager --> Registry[🔍 registry]
    Registry --> Backend[📦 praisonai_sandbox.Backend]
    Backend --> Result[✅ SandboxResult]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef manager fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef registry fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef backend fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef ok fill:#10B981,stroke:#7C90A0,color:#fff

    class Agent agent
    class Manager manager
    class Registry registry
    class Backend backend
    class Result ok
```

The agents layer uses `praisonaiagents._sandbox_bridge` for registry-only dispatch, so it never imports sandbox backends at module load.

***

## Console Script

The package ships a `praisonai-sandbox` console script for listing available backends.

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
$ praisonai-sandbox backends
daytona: unavailable
docker: unavailable
e2b: unavailable
modal: unavailable
novita: unavailable
sandlock: unavailable
ssh: unavailable
subprocess: available
```

The user-facing CLI remains `praisonai sandbox …` — this console script lists backends and points you at the Python API and the wrapper command.

***

## Validating your install

Run the single-command smoke suite from a repo checkout to confirm every backend you have installed is wired up correctly.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "E2E Runner"
        R[▶️ run_e2e.py] --> C1[✅ subprocess]
        R --> C2[✅ SandboxManager]
        R --> C3[✅ shim]
        R --> C4{🐳 Docker?}
        R --> C5[✅ CLI]
        C4 -->|available| C4a[✅ execute]
        C4 -->|unavailable| C4b[⏭️ SKIP]
    end

    classDef runner fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef check fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef pass fill:#10B981,stroke:#7C90A0,color:#fff
    classDef skip fill:#8B0000,stroke:#7C90A0,color:#fff

    class R runner
    class C1,C2,C3,C5,C4a pass
    class C4 decision
    class C4b skip
```

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
cd src/praisonai-sandbox
python e2e-validation/run_e2e.py
```

The runner prints a PASS / FAIL / SKIP table and exits `0` when every required check passes. No API keys are needed for the subprocess path; Docker is optional and is skipped gracefully when the daemon or base image is unavailable.

<Note>
  The `e2e-validation/` bundle ships with the source repository, **not** the published wheel. Run these commands from a checkout of [`MervinPraison/PraisonAI`](https://github.com/MervinPraison/PraisonAI) under `src/praisonai-sandbox/`, not from a `pip install`ed environment.
</Note>

| Check | Required | Notes |
| - | - | - |
| Subprocess execute | ✅ | No dependencies |
| SandboxManager subprocess | ✅ | Via `praisonaiagents.sandbox` |
| Backward-compat shim | ✅ | `praisonai.sandbox` → `praisonai_sandbox` (skipped if the `praisonai` wrapper isn't installed) |
| Docker execute | ⬜ optional | Skipped when Docker daemon/image is unavailable |
| CLI backends smoke | ✅ | `praisonai-sandbox backends` |

Set `PRAISONAI_SANDBOX_E2E_DOCKER_TIMEOUT` (seconds, default `180`) to raise the bound on the optional Docker check for slow networks:

```bash theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
PRAISONAI_SANDBOX_E2E_DOCKER_TIMEOUT=300 python e2e-validation/run_e2e.py
```

See [`MANUAL-E2E-GUIDE.md`](https://github.com/MervinPraison/PraisonAI/blob/main/src/praisonai-sandbox/e2e-validation/MANUAL-E2E-GUIDE.md) for step-by-step manual validation of each backend path.

***

## Best Practices

<AccordionGroup>
  <Accordion title="Install only the backends you use">
    The base package is intentionally slim. Add `[docker]`, `[e2b]`, `[modal]`, `[ssh]`, `[sandlock]`, `[daytona]`, or `[novita]` extras individually instead of `[all]` to keep the dependency tree small.
  </Accordion>

  <Accordion title="Set cloud credentials before selecting cloud backends">
    `daytona` needs `DAYTONA_API_KEY` (and optional `DAYTONA_API_URL`); `novita` needs `NOVITA_API_KEY`; `e2b` and `modal` need their own provider credentials. Selecting a backend without its credentials fails fast.
  </Accordion>

  <Accordion title="Prefer the canonical import">
    Import from `praisonai_sandbox` in new code. The `praisonai.sandbox` shim stays for existing scripts.
  </Accordion>

  <Accordion title="Let the wrapper install it for you">
    `pip install praisonai` bundles `praisonai-sandbox` — install the standalone package only for minimal, sandbox-only environments.
  </Accordion>

  <Accordion title="Interact through SandboxManager">
    Keep using `SandboxManager` and `sandbox=True` on agents. Backends resolve through the registry, so where they live is an internal detail.
  </Accordion>

  <Accordion title="Check availability in CI">
    Run `praisonai-sandbox backends` in CI to confirm the expected backend is installed before a pipeline depends on it.
  </Accordion>

  <Accordion title="Smoke every backend before shipping">
    Run `python e2e-validation/run_e2e.py` from `src/praisonai-sandbox/` to smoke every backend you've installed. It's the fastest way to catch a broken Docker daemon or a missing extra before an agent hits it in production. See the [Validating your install](#validating-your-install) section above.
  </Accordion>

  <Accordion title="Prefer the wrapper CLI for run / shell">
    The standalone script lists backends; the `praisonai sandbox run` / `shell` commands execute code and open REPLs with full flag support.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Sandbox Backends" icon="shield" href="/docs/features/sandbox-backends">
    All eight backends, the registry, and backend selection guide
  </Card>

  <Card title="Sandbox CLI" icon="terminal" href="/docs/cli/sandbox">
    `praisonai sandbox run`, `shell`, and `backends` commands
  </Card>

  <Card title="Sandboxed Agent" icon="shield-check" href="/docs/docs/features/sandboxed-agent">
    Local agent loop with tool sandboxing
  </Card>

  <Card title="praisonai-sandbox SDK" icon="box" href="/docs/docs/sdk/praisonai-sandbox/index">
    Package overview and public API surface
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.