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

# Sandbox

> Cloud sandboxes that run the user app while the agent edits it

Every build session runs against an isolated cloud microVM sandbox. The sandbox is where files are written, dependencies are installed, the dev server runs, and the preview URL is served from. The agent never touches the host filesystem.

<Frame>
  <img src="https://cdn.vibely.sh/doc/v1/engine-sandbox.webp" alt="Sandbox" width="1200" height="675" />
</Frame>

## Lifecycle

```
session start
  └─ WorkspaceManager.getOrCreate(projectId)
       ├─ resume paused sandbox if one exists for this project
       ├─ otherwise spawn from template (E2B_TEMPLATE_WEB / E2B_TEMPLATE_MOBILE)
       └─ restore project files from Supabase (vibe_files)

session active
  ├─ orchestrator starts dev server EARLY (before agent runs)
  ├─ preview_url emitted to client
  └─ agent loop reads/writes files via tools

session idle
  └─ auto-pauses after E2B_AUTO_PAUSE_MIN minutes (default 30)

session resume
  └─ next request hydrates the same sandbox (state preserved)
```

## Templates

| Project type | Template (env) | Default name | Stack |
| - | - | - | - |
| `web` | `E2B_TEMPLATE_WEB` | `vibely-web` | Vite + React + Tailwind |
| `mobile` | `E2B_TEMPLATE_MOBILE` | `vibely-expo` | Expo + React Native |

Templates are baked via `bun run bake:e2b` (see `scripts/bake-e2b-templates.ts`). Verify a baked template with `bun run verify:e2b`. Source templates are pulled from GitHub via `git clone --depth 1` — never bundled into the server image.

## Dev server start

`PreviewManager.startDevServer()` runs in the orchestrator **before** the agent's first turn:

1. Patch `vite.config.ts` so HMR works through the Cloudflare proxy.
2. Run `npm install` if `node_modules` is stale.
3. Spawn the dev command in the background.
4. Poll for the port (`detectPort`, preferred `8080`).
5. Construct the branded URL — `https://<shortId>.vibelyagent.com`.
6. Emit `preview_url` over SSE/WS so the iframe mounts before the agent has done anything.

This early start is why the system prompt can claim "the dev server is already running" — by the time the model reads it, that's true. The `bash` tool short-circuits any `npm run dev` the model tries, because issuing it again would steal the port and break HMR.

## Preview proxy

The `<shortId>.vibelyagent.com` URL is served by `cloudflare/worker.js`, which:

* Proxies the request to the sandbox over its private hostname.
* Strips `X-Frame-Options` and `Content-Security-Policy: frame-ancestors` so the preview can be embedded in the Vibely app's iframe.
* Forwards WebSocket upgrades for HMR.

If the iframe loads blank, the worker is the first place to look.

## File persistence

Files written through `write` or `edit` are mirrored to Supabase (`vibe_files`) per turn. `loopResult.writtenFiles` is the canonical source per-turn — only files the loop saw written are saved, which is fast. As a fallback for bash-only turns (e.g. `npm install` produced new files), `getAllFiles(sandbox)` does a full tree scan. Full scans are slow (2–10 s) so they only run when the incremental list is empty.

This is the contract that lets a paused sandbox resume cleanly: every file that matters is in Postgres, not just on the sandbox disk.

## 404 auto-recovery

The sandbox provider occasionally returns `404` for a sandbox we know exists — usually during snapshot rotation or metadata replication. The tool registry handles this transparently:

```
tool call fails with 404 → wait 1.5 s → recreate sandbox → retry tool call once
```

After 3 recoveries on the same project the agent bails with a hard error so a real fix (sandbox rebuild, project bounce) can happen instead of an infinite retry storm. See [Tools](/reference/engine/tools#sandbox-404-auto-recovery).

## Sandbox pool

A small pool of pre-warmed sandboxes can be kept ready so new sessions don't pay the cold-start cost. The switch is `SANDBOX_POOL_TARGET`, which defaults to **0** — pooling is off unless you ask for it, because an idle sandbox bills continuously. (`SANDBOX_POOL_ENABLED` is on by default and only gates the feature; the target is what decides whether anything is warmed.) With pooling off, the first request to a new project pays \~5–15 s for sandbox spawn + template hydration; subsequent requests for the same project resume the existing sandbox in \<1 s.

## Self-hosted sandboxes

Set `E2B_DOMAIN=https://your-control-plane` to point at your own sandbox control plane instead of the managed cloud. Leave it empty to use the managed one. Every other sandbox env var works the same way against a self-hosted control plane.


## Related topics

- [Error Format](/api-reference/errors.md)
- [Tools](/reference/engine/tools.md)
- [Preview and test your app](/features/projects/preview.md)
- [Mobile app templates](/features/mobile-apps/templates.md)
- [Web app templates](/features/web-apps/templates.md)


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