> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.modbox.run/llms.txt.
> For full documentation content, see https://docs.modbox.run/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.modbox.run/_mcp/server.

# The modbox/sandbox Image

The `modbox/sandbox` image is a full-featured, browser-capable sandbox. It runs a complete Ubuntu desktop environment inside a container and exposes everything — CDP, VNC, a web terminal, a file/exec API, and MCP endpoints — behind a single HTTPS URL.

Use this image when your workload needs a **real browser**, visual interaction, or a persistent remote desktop. For headless code execution without a browser, use `modbox/sandbox-lite` instead.

## What is inside

```
Container (Ubuntu 22.04)
|
+-- Xvfb          :99    virtual X11 display (1920x1080)
+-- x11vnc               VNC server connected to the display
+-- noVNC         :6080  browser-based VNC client (WebSocket)
+-- Chromium      :9222  browser with Chrome DevTools Protocol
+-- cdp-mcp-gateway      CDP exposed as an MCP server
+-- supergateway  :6060  bash shell exposed as an MCP server
+-- ttyd          :3000  web terminal
+-- sandbox-server :3334 REST API for exec and file upload/download
+-- nginx         :8080  main entry point (all routes below)
```

All processes are managed by `supervisord` and restart automatically.

## Provisioning

```typescript
import { ModboxClient } from "modbox-sdk";

const modbox = new ModboxClient({ token: process.env.MODBOX_API_TOKEN });

const sandboxApiKey = crypto.randomUUID(); // you generate this

await modbox.provisionSandbox({
  taskId: "my-browser-sandbox",
  imageId: process.env.MODBOX_SANDBOX_IMAGE_ID, // modbox/sandbox image ID
  ttlSeconds: 1800,
  envVars: {
    SANDBOX_API_KEY: sandboxApiKey, // required to authenticate the REST API
  },
});

await modbox.waitForSandbox({ taskId: "my-browser-sandbox", timeout: 120 });
const sandbox = await modbox.getSandbox("my-browser-sandbox");
// sandbox.sandboxUrl → https://my-browser-sandbox.sandbox.modbox.run
```

```python
import os, uuid
from modbox import ModboxClient

modbox = ModboxClient(token=os.environ["MODBOX_API_TOKEN"])

sandbox_api_key = str(uuid.uuid4())

modbox.provision_sandbox(
    task_id="my-browser-sandbox",
    image_id=os.environ["MODBOX_SANDBOX_IMAGE_ID"],
    ttl_seconds=1800,
    env_vars={"SANDBOX_API_KEY": sandbox_api_key},
)
modbox.wait_for_sandbox(task_id="my-browser-sandbox", timeout=120)
sandbox = modbox.get_sandbox("my-browser-sandbox")
# sandbox.sandbox_url → https://my-browser-sandbox.sandbox.modbox.run
```

The `SANDBOX_API_KEY` is a secret you generate. It is injected into the container via `env_vars` and required to authenticate every call to the sandbox REST API (`/modbox/api/*`). Store it alongside `sandboxUrl` in your session.

## Routes

All routes are served through nginx at `https://{sandbox_url}` (port 8080).

| Path                | Description                                                      |
| ------------------- | ---------------------------------------------------------------- |
| `/`                 | Redirects to `/vnc/` — opens the desktop in the browser          |
| `/vnc/`             | noVNC — view and control the desktop from a browser tab          |
| `/terminal/`        | ttyd — web-based bash terminal (WebSocket)                       |
| `/cdp/`             | Chrome DevTools Protocol HTTP endpoint (JSON/version, JSON/list) |
| `/devtools/`        | CDP WebSocket endpoint (used by Playwright, Puppeteer, etc.)     |
| `/mcp/terminal/`    | MCP server for bash shell (SSE / Streamable HTTP)                |
| `/mcp/cdp/`         | MCP server for browser control via CDP (SSE / Streamable HTTP)   |
| `/modbox/api/exec`  | REST — execute shell commands                                    |
| `/modbox/api/files` | REST — upload, list, and download files                          |

## Executing commands

Use `POST /modbox/api/exec` to run a shell command inside the sandbox. The endpoint executes arbitrary bash and returns stdout, stderr, exit code and duration.

```bash
POST https://{sandbox_url}/modbox/api/exec
Authorization: Bearer {SANDBOX_API_KEY}
Content-Type: application/json

{
  "command": "python3 -c \"print('hello')\"",
  "timeout": 10000,
  "cwd": "/workspace"
}
```

Response:

```json
{
  "exitCode": 0,
  "stdout": "hello\n",
  "stderr": "",
  "killed": false,
  "signal": null,
  "durationMs": 38,
  "truncated": false
}
```

Fields:

| Field     | Type    | Description                                            |
| --------- | ------- | ------------------------------------------------------ |
| `command` | string  | Shell command (required)                               |
| `timeout` | integer | Timeout in milliseconds — default and max is 30 000 ms |
| `cwd`     | string  | Working directory — default is `/`                     |

```typescript
const result = await fetch(`${sandbox.sandboxUrl}/modbox/api/exec`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${sandboxApiKey}`,
  },
  body: JSON.stringify({
    command: "python3 script.py",
    cwd: "/workspace",
    timeout: 15000,
  }),
}).then((r) => r.json());

console.log(result.stdout);  // → output of the script
console.log(result.exitCode); // → 0 if successful
```

```python
import httpx

result = httpx.post(
    f"{sandbox.sandbox_url}/modbox/api/exec",
    headers={"Authorization": f"Bearer {sandbox_api_key}"},
    json={
        "command": "python3 script.py",
        "cwd": "/workspace",
        "timeout": 15000,
    },
).json()

print(result["stdout"])
print(result["exitCode"])
```

## Uploading and downloading files

### Upload

`POST /modbox/api/files/upload` — multipart/form-data, field name `file`. Max 50 MB.

```bash
curl -X POST https://{sandbox_url}/modbox/api/files/upload \
  -H "Authorization: Bearer {SANDBOX_API_KEY}" \
  -F "file=@/local/path/script.py"
```

Response:

```json
{
  "filename": "1748000000000-script.py",
  "originalName": "script.py",
  "size": 1024,
  "mimetype": "text/x-python",
  "uploadedAt": "2026-05-22T10:00:00.000Z"
}
```

After uploading, the file is stored in `/workspace/uploads/{filename}` inside the container. Execute it with the exec endpoint.

### List files

```bash
GET https://{sandbox_url}/modbox/api/files
Authorization: Bearer {SANDBOX_API_KEY}

# Optional: filter by name
GET https://{sandbox_url}/modbox/api/files?search=script
```

### Download

```bash
GET https://{sandbox_url}/modbox/api/files/download/{filename}
Authorization: Bearer {SANDBOX_API_KEY}
```

### Delete

```bash
DELETE https://{sandbox_url}/modbox/api/files/{filename}
Authorization: Bearer {SANDBOX_API_KEY}
```

## Browser automation (CDP)

The sandbox runs a Chromium instance with Chrome DevTools Protocol enabled. Connect Playwright, Puppeteer, or any CDP client using the `/cdp/` HTTP endpoint. Nginx rewrites the WebSocket URLs in CDP responses so they resolve correctly through the proxy.

```typescript
import { chromium } from "playwright";

// connectOverCDP expects an HTTP endpoint, not WebSocket
const browser = await chromium.connectOverCDP(
  `https://${new URL(sandbox.sandboxUrl).host}/cdp`
);

const context = browser.contexts()[0];
const page = context.pages()[0];

await page.goto("https://example.com");
const title = await page.title();

await browser.close();
```

```python
from playwright.async_api import async_playwright
from urllib.parse import urlparse

async with async_playwright() as p:
    cdp_url = f"https://{urlparse(sandbox.sandbox_url).netloc}/cdp"
    browser = await p.chromium.connect_over_cdp(cdp_url)

    context = browser.contexts[0]
    page = context.pages[0]

    await page.goto("https://example.com")
    title = await page.title()

    await browser.close()
```

Pass the **HTTP endpoint** (`https://.../cdp`) to `connectOverCDP`, not a WebSocket URL. Playwright fetches `{endpoint}/json/version` to discover the actual WebSocket debugger URL, which nginx rewrites to route correctly through the proxy.

## Visual desktop (noVNC)

Open `{sandbox_url}/vnc/` in a browser to access a full graphical desktop and watch the browser interact in real time. Useful for debugging automations, recording sessions, or running GUI applications.

No credentials required to access the VNC viewer — access is controlled by the sandbox URL itself, which is only known to the provisioning party.

## Web terminal

Open `{sandbox_url}/terminal/` in a browser to access a bash terminal running as the `sandbox` user inside the container. The terminal streams over WebSocket via ttyd.

## MCP integration

The sandbox exposes two MCP servers, both available from the sandbox URL:

| MCP server      | Path             | Capability                                |
| --------------- | ---------------- | ----------------------------------------- |
| Terminal (bash) | `/mcp/terminal/` | Run shell commands, read/write files      |
| CDP browser     | `/mcp/cdp/`      | Control the browser via DevTools Protocol |

Connect any MCP-compatible client (Claude Desktop, VS Code, custom agent) by pointing it at `https://{sandbox_url}/mcp/terminal/` or `https://{sandbox_url}/mcp/cdp/`.

```json
{
  "mcpServers": {
    "sandbox-terminal": {
      "url": "https://my-browser-sandbox.sandbox.modbox.run/mcp/terminal/"
    },
    "sandbox-browser": {
      "url": "https://my-browser-sandbox.sandbox.modbox.run/mcp/cdp/"
    }
  }
}
```

## Proxy support

If your sandbox needs to route traffic through a corporate proxy, pass the proxy settings via `env_vars`. The entrypoint propagates them automatically to `apt`, `pip`, `git`, and `npm`.

```json
{
  "envVars": {
    "HTTP_PROXY":  "http://proxy.example.com:3128",
    "HTTPS_PROXY": "http://proxy.example.com:3128",
    "NO_PROXY":    "localhost,127.0.0.1"
  }
}
```

For SOCKS5 proxies, replace the value with `socks5://proxy.example.com:1080`.

## sandbox vs sandbox-lite

| Feature                       | modbox/sandbox | modbox/sandbox-lite |
| ----------------------------- | -------------- | ------------------- |
| Desktop (Xvfb + VNC)          | Yes            | No                  |
| Chromium + CDP                | Yes            | No                  |
| noVNC browser viewer          | Yes            | No                  |
| Web terminal (ttyd)           | Yes            | No                  |
| sandbox-server (exec + files) | Yes            | Yes                 |
| MCP shell server              | Yes            | Yes                 |
| nginx                         | Yes            | Yes                 |
| Typical memory                | 1.5–2 GB       | 128–256 MB          |

Use `modbox/sandbox` when the task needs a real browser or visual interaction. Use `modbox/sandbox-lite` for code execution, scripts, and automation without a GUI.