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

# AI Agent Integration

> Production patterns for BoxLite as an AI agent sandbox — configuration, security presets, concurrency, timeout handling, and terminal resizing.

This guide covers production patterns for deploying BoxLite as a sandboxed execution environment for AI agents. It assumes you've already worked through the [Tutorials](/tutorials/index) and know how to create boxes, run code, and transfer files.

<Note>
  **Using BoxRun?** If your agent communicates via HTTP, [BoxRun's REST API](/boxrun/rest-api) or [Python SDK](/boxrun/python-sdk) may be a simpler integration path. BoxRun handles sandbox lifecycle, file upload/download, and SSE streaming out of the box. See the [AI agent patterns](/boxrun/python-sdk#ai-agent-patterns) section in the BoxRun SDK docs.
</Note>

## Recommended Configuration

### Workload-Type Reference

| Workload         | Image            | CPUs | Memory   | Disk  | Notes                        |
| ---------------- | ---------------- | ---- | -------- | ----- | ---------------------------- |
| Code execution   | `python:slim`    | 1    | 512 MiB  | None  | Ephemeral, fast startup      |
| Data analysis    | `python:slim`    | 2    | 2048 MiB | None  | More memory for pandas/numpy |
| Web browsing     | Use `BrowserBox` | 2    | 2048 MiB | None  | Chromium needs resources     |
| Multi-tool agent | `python:slim`    | 2    | 1024 MiB | None  | Balance cost vs. capability  |
| Persistent env   | `python:slim`    | 1    | 512 MiB  | 10 GB | State survives restarts      |

### Starter Configuration

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import boxlite
    from boxlite.boxlite import SecurityOptions

    options = boxlite.BoxOptions(
        image="python:slim",
        cpus=2,
        memory_mib=1024,
        working_dir="/workspace",
        security=SecurityOptions.maximum(),
    )
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { JsBoxlite } from '@boxlite-ai/boxlite';

    const runtime = JsBoxlite.withDefaultConfig();
    const box = await runtime.create({
      image: 'python:slim',
      cpus: 2,
      memoryMib: 1024,
      workingDir: '/workspace',
    });
    ```
  </Tab>
</Tabs>

### Security Presets

`SecurityOptions` has three presets:

| Preset          | Jailer | Seccomp    | Resource Limits                                                  | Use Case                 |
| --------------- | ------ | ---------- | ---------------------------------------------------------------- | ------------------------ |
| `development()` | Off    | Off        | None                                                             | Debugging sandbox issues |
| `standard()`    | On     | On (Linux) | None                                                             | General workloads        |
| `maximum()`     | On     | On (Linux) | `max_open_files=1024`, `max_file_size=1GiB`, `max_processes=100` | Untrusted AI code        |

For AI agents running untrusted code, use `SecurityOptions.maximum()`:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from boxlite.boxlite import SecurityOptions

    security = SecurityOptions.maximum()

    # Customize if needed
    security.max_open_files = 2048
    security.network_enabled = False  # Disable network for strict isolation
    ```
  </Tab>

  <Tab title="Node.js">
    The Node.js SDK enables jailer and seccomp by default. You configure resource limits directly in the box creation options:

    ```typescript theme={null}
    import { JsBoxlite } from '@boxlite-ai/boxlite';

    const runtime = JsBoxlite.withDefaultConfig();
    const box = await runtime.create({
      image: 'python:slim',
      cpus: 2,
      memoryMib: 1024,
      maxOpenFiles: 2048,
      // Omit ports to prevent incoming connections
    });
    ```
  </Tab>
</Tabs>

***

## Concurrency Model

### One Box, Multiple Executions (Recommended)

A single box can run many `exec()` calls. Each call spawns a new process inside the same VM. This avoids repeated VM boot overhead and is safe because the VM provides hardware isolation from the host.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import asyncio
    import boxlite

    async def main():
        runtime = boxlite.Boxlite.default()
        from boxlite.boxlite import SecurityOptions
        box = await runtime.create(boxlite.BoxOptions(
            image="python:slim",
            cpus=2,
            memory_mib=1024,
            security=SecurityOptions.maximum(),
        ))

        try:
            # Run agent tools concurrently in the same box
            results = await asyncio.gather(
                box.exec("python", ["-c", "print('task A')"]),
                box.exec("python", ["-c", "print('task B')"]),
                box.exec("python", ["-c", "print('task C')"]),
            )

            for execution in results:
                result = await execution.wait()
                print(f"Exit code: {result.exit_code}")
        finally:
            await box.stop()
            await runtime.remove(box.id)
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { JsBoxlite } from '@boxlite-ai/boxlite';

    const runtime = JsBoxlite.withDefaultConfig();
    const box = await runtime.create({
      image: 'python:slim',
      cpus: 2,
      memoryMib: 1024,
    });

    try {
      // Run agent tools concurrently in the same box
      const [execA, execB, execC] = await Promise.all([
        box.exec('python', ['-c', "print('task A')"]),
        box.exec('python', ['-c', "print('task B')"]),
        box.exec('python', ['-c', "print('task C')"]),
      ]);

      for (const execution of [execA, execB, execC]) {
        const result = await execution.wait();
        console.log(`Exit code: ${result.exitCode}`);
      }
    } finally {
      await box.stop();
      await runtime.remove(box.id);
    }
    ```
  </Tab>
</Tabs>

**When to use:** Most AI agent scenarios. Keeps VM boot cost to one-time.

### One Box Per Agent

Use separate boxes when you need strict isolation between agents, different images, or independent resource limits.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    async def run_isolated_agent(code: str, image: str = "python:slim"):
        """Each agent gets its own box."""
        async with boxlite.SimpleBox(image=image, memory_mib=512) as box:
            result = await box.exec("python", "-c", code)
            return result.stdout

    async def main():
        agents = [
            run_isolated_agent("print('agent 1')"),
            run_isolated_agent("print('agent 2')", image="node:alpine"),
            run_isolated_agent("print('agent 3')"),
        ]
        results = await asyncio.gather(*agents)
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { JsBoxlite } from '@boxlite-ai/boxlite';

    async function runIsolatedAgent(
      code: string,
      image: string = 'python:slim'
    ): Promise<string> {
      const runtime = JsBoxlite.withDefaultConfig();
      const box = await runtime.create({ image, memoryMib: 512 });

      try {
        const execution = await box.exec('python', ['-c', code]);
        const result = await execution.wait();
        return result.stdout;
      } finally {
        await box.stop();
        await runtime.remove(box.id);
      }
    }

    const results = await Promise.all([
      runIsolatedAgent("print('agent 1')"),
      runIsolatedAgent("print('agent 2')", 'node:alpine'),
      runIsolatedAgent("print('agent 3')"),
    ]);
    ```
  </Tab>
</Tabs>

**When to use:** Multi-tenant isolation, different language runtimes, or strict resource separation.

***

## Timeout Handling and Zombie Prevention

### The Problem

`asyncio.wait_for()` cancels the Python coroutine but does **not** kill the guest process. Without explicit cleanup, the process continues running inside the VM indefinitely.

<Warning>
  The following pattern leaves a zombie process running inside the box:

  ```python theme={null}
  # BAD: process keeps running inside the box after timeout
  try:
      execution = await box.exec("python", ["-c", "import time; time.sleep(9999)"])
      result = await asyncio.wait_for(execution.wait(), timeout=5)
  except asyncio.TimeoutError:
      print("Timed out")  # Process is still running in the VM!
  ```
</Warning>

### Correct Pattern

Always kill the execution in the timeout handler:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    async def exec_with_timeout(box, cmd, args=None, timeout=30):
        """Execute a command with proper timeout and cleanup."""
        execution = await box.exec(cmd, args or [])
        try:
            result = await asyncio.wait_for(execution.wait(), timeout=timeout)
            return result
        except asyncio.TimeoutError:
            await execution.kill()
            raise
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    async function execWithTimeout(box, cmd, args = [], timeoutMs = 30000) {
      const execution = await box.exec(cmd, args);
      let timer;
      try {
        const result = await Promise.race([
          execution.wait(),
          new Promise((_, reject) => {
            timer = setTimeout(
              () => reject(new Error('Execution timed out')),
              timeoutMs
            );
          }),
        ]);
        clearTimeout(timer);
        return result;
      } catch (error) {
        clearTimeout(timer);
        await execution.kill();
        throw error;
      }
    }
    ```
  </Tab>
</Tabs>

### Defensive Helper

For maximum safety, combine timeout handling with a try/finally block:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    async def safe_exec(box, cmd, args=None, timeout=30):
        """Execute with timeout, guaranteed process cleanup."""
        execution = await box.exec(cmd, args or [])
        try:
            result = await asyncio.wait_for(execution.wait(), timeout=timeout)
            return result
        except asyncio.TimeoutError:
            try:
                await execution.kill()
            except Exception:
                pass  # Best-effort kill
            raise
        except Exception:
            try:
                await execution.kill()
            except Exception:
                pass  # Best-effort kill on any failure
            raise
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    async function safeExec(box, cmd, args = [], timeoutMs = 30000) {
      const execution = await box.exec(cmd, args);
      let timer;
      try {
        const result = await Promise.race([
          execution.wait(),
          new Promise((_, reject) => {
            timer = setTimeout(
              () => reject(new Error('Execution timed out')),
              timeoutMs
            );
          }),
        ]);
        clearTimeout(timer);
        return result;
      } catch (error) {
        clearTimeout(timer);
        try {
          await execution.kill();
        } catch {
          // Best-effort kill
        }
        throw error;
      }
    }
    ```
  </Tab>
</Tabs>

***

## Security Boundaries

### SecurityOptions Fields

| Field             | Type          | Description                                                |
| ----------------- | ------------- | ---------------------------------------------------------- |
| `jailer_enabled`  | `bool`        | OS-level sandbox (seccomp on Linux, sandbox-exec on macOS) |
| `seccomp_enabled` | `bool`        | Syscall filtering (Linux only)                             |
| `max_open_files`  | `int \| None` | Limit open file descriptors                                |
| `max_file_size`   | `int \| None` | Maximum file size in bytes                                 |
| `max_processes`   | `int \| None` | Maximum number of processes                                |
| `max_memory`      | `int \| None` | Maximum virtual memory in bytes                            |
| `max_cpu_time`    | `int \| None` | Maximum CPU time in seconds                                |
| `network_enabled` | `bool`        | Allow network access from sandbox (macOS only)             |
| `close_fds`       | `bool`        | Close inherited file descriptors                           |

### Network Isolation

To prevent an agent from accessing the network:

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    from boxlite.boxlite import SecurityOptions

    security = SecurityOptions.maximum()
    security.network_enabled = False

    options = boxlite.BoxOptions(
        image="python:slim",
        security=security,
        # No ports= means no incoming connections either
    )
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { JsBoxlite } from '@boxlite-ai/boxlite';

    const runtime = JsBoxlite.withDefaultConfig();
    const box = await runtime.create({
      image: 'python:slim',
      // Do not pass ports — no incoming connections
      // On Linux, the VM runs in an isolated network namespace by default
    });

    // Verify: this should fail if network is isolated
    const execution = await box.exec('ping', ['-c', '1', '8.8.8.8']);
    const result = await execution.wait();
    console.log(`Network test exit code: ${result.exitCode}`);
    ```

    <Note>
      The Node.js SDK does not expose a `networkEnabled` toggle. On Linux, network isolation is enforced by the runtime's network namespace. On macOS, use the Python SDK's `SecurityOptions.network_enabled` for explicit control.
    </Note>
  </Tab>
</Tabs>

<Note>
  In the Python bindings, `network_enabled` is currently a macOS-only control. On Linux and other platforms, network isolation is typically enforced by the container/runtime networking configuration (for example, running in an isolated network namespace and not publishing ports), and `network_enabled` may not itself hard-disable all outbound connectivity.
</Note>

### Resource Limits as Security Boundaries

Resource limits prevent a rogue agent from consuming all host resources:

```python theme={null}
options = boxlite.BoxOptions(
    image="python:slim",
    cpus=1,             # Cap CPU usage
    memory_mib=512,     # Hard memory limit
    security=SecurityOptions.maximum(),
)
```

### Memory Limits and OOM

The `memory_mib` setting is a **hard limit** enforced by the hypervisor. When a guest process exceeds this limit, the Linux OOM killer terminates the offending process inside the VM — but the box itself stays running. This means you can detect OOM and retry or report the failure.

How to detect OOM:

* The process exit code will be **137** (128 + SIGKILL)
* `stderr` may contain `Killed` or `Out of memory`

```python theme={null}
result = await box.exec("python", ["-c", "x = bytearray(2**30)"])
completed = await result.wait()

if completed.exit_code == 137:
    print("Process was OOM-killed")
    print(f"stderr: {completed.stderr}")
```

<Warning>
  OOM kills the guest **process**, not the box. The box remains running and can accept new `exec()` calls. If your agent needs to detect and handle OOM, check for exit code 137 after each execution.
</Warning>

***

## Terminal Resizing

When running interactive TTY sessions (e.g., an AI agent controlling a shell), use `resize_tty()` to set the terminal dimensions. This ensures proper line wrapping and avoids garbled output from programs that query terminal size.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    runtime = boxlite.Boxlite.default()
    box = await runtime.create(boxlite.BoxOptions(image="alpine:latest"))

    # Start a shell with TTY
    execution = await box.exec("sh", tty=True)

    # Set terminal size to 40 rows x 120 columns
    await execution.resize_tty(40, 120)

    # Send commands via stdin
    stdin = execution.stdin()
    await stdin.send_input(b"ls -la\n")

    # Read output
    stdout = execution.stdout()
    async for line in stdout:
        print(line)
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { JsBoxlite } from '@boxlite-ai/boxlite';

    const runtime = JsBoxlite.withDefaultConfig();
    const box = await runtime.create({ image: 'alpine:latest' });

    // Start a shell with TTY
    const execution = await box.exec('sh', [], { tty: true });

    // Set terminal size to 40 rows x 120 columns
    await execution.resizeTty(40, 120);

    // Send commands via stdin
    const stdin = execution.stdin();
    await stdin.sendInput(Buffer.from('ls -la\n'));

    // Read output
    const stdout = execution.stdout();
    for await (const line of stdout) {
      console.log(line);
    }
    ```
  </Tab>
</Tabs>

<Note>
  `resize_tty()` / `resizeTty()` only works on executions started with `tty=True` / `{ tty: true }`. Calling it on a non-TTY execution returns an error.
</Note>

***

## Complete Example

Putting it all together: security configuration, concurrent execution with timeouts, and cleanup.

<Tabs>
  <Tab title="Python">
    ```python theme={null}
    import asyncio
    import boxlite
    from boxlite.boxlite import SecurityOptions


    async def safe_exec(box, cmd, args=None, timeout=30):
        """Execute with timeout and guaranteed process cleanup."""
        execution = await box.exec(cmd, args or [])
        try:
            result = await asyncio.wait_for(execution.wait(), timeout=timeout)
            return result
        except asyncio.TimeoutError:
            try:
                await execution.kill()
            except Exception:
                pass
            raise


    async def main():
        runtime = boxlite.Boxlite.default()

        # Configure box with security and resource limits
        box = await runtime.create(boxlite.BoxOptions(
            image="python:slim",
            cpus=2,
            memory_mib=1024,
            working_dir="/workspace",
            security=SecurityOptions.maximum(),
        ))

        try:
            # Run with timeout protection
            result = await safe_exec(
                box,
                "python",
                ["-c", "print('hello from secure sandbox')"],
                timeout=60,
            )
            print(f"Exit code: {result.exit_code}")

            # Run concurrent tasks safely
            tasks = [
                safe_exec(box, "python", ["-c", "print('task 1')"], timeout=10),
                safe_exec(box, "python", ["-c", "print('task 2')"], timeout=10),
            ]
            results = await asyncio.gather(*tasks, return_exceptions=True)

            for i, r in enumerate(results):
                if isinstance(r, Exception):
                    print(f"Task {i} failed: {r}")
                else:
                    print(f"Task {i} exit code: {r.exit_code}")

        finally:
            await box.stop()
            await runtime.remove(box.id)


    asyncio.run(main())
    ```
  </Tab>

  <Tab title="Node.js">
    ```typescript theme={null}
    import { JsBoxlite } from '@boxlite-ai/boxlite';

    async function safeExec(box, cmd, args = [], timeoutMs = 30000) {
      const execution = await box.exec(cmd, args);
      let timer;
      try {
        const result = await Promise.race([
          execution.wait(),
          new Promise((_, reject) => {
            timer = setTimeout(
              () => reject(new Error('Execution timed out')),
              timeoutMs
            );
          }),
        ]);
        clearTimeout(timer);
        return result;
      } catch (error) {
        clearTimeout(timer);
        try { await execution.kill(); } catch { /* best-effort */ }
        throw error;
      }
    }

    const runtime = JsBoxlite.withDefaultConfig();
    const box = await runtime.create({
      image: 'python:slim',
      cpus: 2,
      memoryMib: 1024,
      workingDir: '/workspace',
    });

    try {
      // Run with timeout protection
      const result = await safeExec(
        box,
        'python',
        ['-c', "print('hello from secure sandbox')"],
        60000
      );
      console.log(`Exit code: ${result.exitCode}`);

      // Run concurrent tasks safely
      const results = await Promise.allSettled([
        safeExec(box, 'python', ['-c', "print('task 1')"], 10000),
        safeExec(box, 'python', ['-c', "print('task 2')"], 10000),
      ]);

      results.forEach((r, i) => {
        if (r.status === 'rejected') {
          console.log(`Task ${i} failed: ${r.reason}`);
        } else {
          console.log(`Task ${i} exit code: ${r.value.exitCode}`);
        }
      });
    } finally {
      await box.stop();
      await runtime.remove(box.id);
    }
    ```
  </Tab>
</Tabs>

***

## See also

* [Tutorials](/tutorials/index) — Step-by-step walkthroughs for code execution, file transfer, LLM integration, and browser automation
* [BoxRun Python SDK — AI agent patterns](/boxrun/python-sdk#ai-agent-patterns) — Agent integration via REST API
* [BoxRun REST API](/boxrun/rest-api) — HTTP endpoints for sandbox management
* [SDK Reference](/reference/index) — Full API reference for all BoxLite SDKs
* [Architecture](/architecture/index) — How BoxLite isolation works
