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

# E2B

> Run captured programs in an E2B cloud sandbox you created.

The `e2b` machine provider reaches a running [E2B](https://e2b.dev)
sandbox, by ID, and the `machine` runtime runs captured programs in it. You create the sandbox from a template; Mirage only runs programs in
it. Python SDK, Node and browsers.

## Installation

> [tab] Python
>
> ```bash theme={null}
> pip install 'mirage-ai[e2b,ssh]'
> ```

> [tab] TypeScript
>
> ```bash theme={null}
> pnpm add e2b ssh2
> ```

The `ssh` extra and `ssh2` serve the workspace into the sandbox with
`mount_type: ssh`. Create the sandbox from a template that has what
[connecting the workspace](#connecting-the-workspace) needs. Set `api_key`
in the config, as a value or a [credential pointer](https://docs.mirage.strukto.ai/home/authentication.md#pointer-fields),
or `E2B_API_KEY`.

## Usage

`captures` defaults to [`@external`](https://docs.mirage.strukto.ai/home/setup/runtime/overview#captures):
every program Mirage does not have itself. Name `@python` to run
Python in the sandbox too.

> [tab] Python
>
> ```python theme={null}
> from mirage import RAMVFS, MountMode, Workspace
> from mirage.runtime.machine import MachineRuntime
>
> ws = Workspace(
>     {"/data": RAMVFS()},
>     mode=MountMode.EXEC,
>     machines={
>         "box": {
>             "provider": "e2b",
>             "sandbox_id": "sandbox-id",
>             "mount_type": "ssh",
>         }
>     },
>     runtimes=[MachineRuntime(["@python", "@external"], machine="box")],
> )
> await ws.shell("python3 -c 'print(1 + 1)'")
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { MountMode, MachineRuntime, RAMVFS, Workspace } from '@struktoai/mirage-node'
>
> const ws = new Workspace(
>   { '/data': new RAMVFS() },
>   {
>     mode: MountMode.EXEC,
>     machines: { box: { provider: 'e2b', sandboxId: 'sandbox-id', mountType: 'ssh' } },
>     runtimes: [new MachineRuntime({ captures: ['@python', '@external'], machine: 'box' })],
>   },
> )
> await ws.shell("python3 -c 'print(1 + 1)'")
> ```
>
> In a browser, import `@struktoai/mirage-core/machine/e2b/handle` once
> to register the provider (`pnpm add @struktoai/mirage-core`), pass
> `apiKey`, and use `mountType: 'fuse'` or leave it unset.

> [tab] YAML
>
> ```yaml theme={null}
> mode: exec
> machines:
>   box:
>     provider: e2b
>     sandbox_id: ${E2B_SANDBOX_ID}
>     mount_type: ssh
> runtimes:
>   - name: machine
>     captures: ["@python", "@external"]
>     machine: box
> mounts:
>   /data:
>     vfs: ram
> ```

## Connecting the workspace

A program in the sandbox opens workspace files at the same paths as in
Mirage, so the sandbox must see the mounts there. `mount_type` picks how:

| `mount_type` | How the files get there | Rules that apply |
| - | - | - |
| `ssh` | Mirage serves every mount over E2B's command channel; the sandbox mounts each with `sshfs` | This workspace's mount modes, hides, permissions and policies |
| `fuse` | A Mirage inside the sandbox mounts your backends with FUSE | That Mirage's own config |
| `both` | The sandbox's own FUSE mounts where it has them, `ssh` for the rest | Each side's own |
| unset | Nothing; you put the files there yourself | The sandbox's |

### ssh

* The template needs only `sshfs` ([below](#a-template-for-ssh)). No
  Mirage, keys or open port in the sandbox.
* `ssh` and `both` do not work in browsers.
* Each file call is a round trip to the workspace, about 150 ms for a small
  file from a laptop; with `both`, keep bulk data on the sandbox's own
  mounts.
* The sandbox shows one session's view, the first that runs a line there.
  Give each agent its own sandbox.
* An sshfs still serving a path Mirage serves, another workspace's or one
  of your own, is left alone and the line exits 1. Give each workspace its
  own sandbox.
* If the sandbox restarts, Mirage mounts the paths again. A line caught
  mid-restart exits 126 without running.
* The root mount and `/dev` are not served.

#### A template for ssh

Build a template with `sshfs` and pass its sandbox ID as `sandbox_id`:

> [tab] Python
>
> ```python theme={null}
> from e2b import AsyncSandbox, Template
>
> Template.build(Template().from_base_image().apt_install("sshfs"), "mirage-sshfs")
> sandbox = await AsyncSandbox.create("mirage-sshfs")
> print(sandbox.sandbox_id)
> ```

> [tab] TypeScript
>
> ```typescript theme={null}
> import { Sandbox, Template } from 'e2b'
>
> await Template.build(Template().fromBaseImage().aptInstall('sshfs'), 'mirage-sshfs')
> const sandbox = await Sandbox.create('mirage-sshfs')
> console.log(sandbox.sandboxId)
> ```

Keep the base image's user, which has `sudo` for mountpoints such as
`/data`. On the default template, run `sudo apt-get install -y sshfs`
instead.

### fuse

Base the template on [the machine image](https://docs.mirage.strukto.ai/home/setup/machine/overview.md#the-machine-image),
which carries Mirage and `fuse3`. Write the same mounts at the same paths
into `sandbox.yaml`, and start Mirage with it in the sandbox:

```yaml theme={null}
mounts:
  /data:
    vfs: s3
    config:
      bucket: my-bucket
    backend: fuse
    mountpoint: /data
```

```bash theme={null}
mirage workspace create /tmp/sandbox.yaml
```

* This workspace's rules do not reach those files, so a line is refused
  while the session's permissions hide or narrow anything under them.
* If a path the sandbox mounted goes away, as on a restart, lines exit
  126 until it is mounted again. Start Mirage from the image's entrypoint
  so a restart mounts it again.
* Keep the sandbox's paths in step with the host's mounts yourself.
  `sandbox.yaml` can differ where it should, such as narrower credentials.

## Runtime behavior

| Capability | In the sandbox |
| - | - |
| Command | The captured program and its words, as typed |
| Piped input | stdin |
| Environment | The session's, over the config `env` |
| Working directory | The shell's, unchanged |
| Workspace files | By `mount_type`, above |
| Disk and network | The sandbox's own |

## Notes

* Closing the workspace leaves the sandbox running.
* Output comes back as the program wrote it, binary included: the
  sandbox base64-encodes it on the way out, so the image needs `dd` and
  `base64` (Debian, Ubuntu and Alpine images have both), and output
  takes about a third more to transfer. Without them a line runs
  nothing, prints why and exits 127.
* Commands run concurrently, each with its own input and output. Without
  piped input, stdin starts closed.
* Templates, sizing, networking and lifecycle stay E2B settings.
* A Mirage [timeout](https://docs.mirage.strukto.ai/home/setup/machine/overview.md#timeouts) tries to kill the command; its child
  processes may survive.


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