<!-- https://ouijit.com/docs/sandbox/ · rendered as Markdown for agents and LLMs -->

# Sandbox

Run any task's terminals and hooks inside a sandbox. Two backends: a Lima VM that mounts only the task's work, or nono's kernel-level access limits in place on the host.

## Choose a backend

Sandboxing is a per-terminal choice. Right-click a task and use **Open in → Lima VM sandbox** or **Open in → nono sandbox**; an entry appears for each backend installed on your system. Plain **Open in → Terminal** runs on the host, unsandboxed. Every terminal and [hook](https://ouijit.com/docs/hooks/) opened for that task then runs under the chosen backend. A sandboxed terminal carries an outlined status dot and a backend suffix on its label.

Each backend has its own configuration under **Project Settings → Sandbox**.

## Lima VM

Lima-sandboxed terminals run inside a Linux VM, on a parallel git branch, in a worktree that contains only tracked files.

### What it does

For a Lima-sandboxed task, Ouijit creates a second git worktree at `~/Ouijit/sandbox-views/<project>/T-N-sandbox` on a child branch `s/<your-branch>`. Because it is a plain `git worktree add`, only tracked files exist there: no `.env`, `node_modules`, build caches, or anything else gitignored. That sandbox-view directory is what gets mounted into a [Lima](https://lima-vm.io) VM as the agent's working directory. Your real worktree is never mounted.

When the agent commits inside the VM, the commit lands on `s/<your-branch>`. A host-side watcher fast-forwards your branch to match. If you committed in parallel on the host, the fast-forward fails and Ouijit surfaces the divergence.

### How it works

1.  **Open a Lima sandbox terminal** from a task's **Open in** menu. Every terminal and hook for that task then runs in the VM.
2.  **First terminal**. Ouijit lazily creates an Ubuntu 24.04 VM via Lima on the first sandboxed spawn for a project. One VM per project, reused across that project's sandboxed tasks.
3.  **Spawn progress**. The dialog shows each step: checking VM status, creating or starting, SSH connection, provisioning, launching the shell. First boot takes 1-2 minutes; later spawns reuse the running VM.
4.  **Shell sessions** run inside the VM via `limactl shell`, with the working directory set to the sandbox-view path.
5.  **VM lifecycle and resources**. Manage the VM from **Project Settings → Sandbox → Lima VM**: see status, create, start, stop, recreate, open a console, and edit the merged Lima YAML config (memory, CPU, disk, mounts). Resource changes prompt a recreate.

### Mount surface

Only two host paths are exposed to the VM:

| Host path | In guest | Why |
| --- | --- | --- |
| `~/Ouijit/sandbox-views/<proj>/` | Read/write | The agent's tracked-files-only worktree. |
| `<project>/.git/` | Read-only base, with read/write overlays at `objects/`, `refs/`, `logs/`, `worktrees/` | Commits need to write packs, refs, and reflogs. `hooks/`, `config`, and `packed-refs` stay read-only. |

Your home directory, source tree, system binaries, and any other host paths are not mounted at all.

### Isolation properties

-   **Gitignored content is not in the mount**. `.env`, `.npmrc`, `secrets/`, `node_modules`, and build artifacts never leave the host.
-   **Host toolchains stay separate**. darwin-arm64 `node_modules`, Rust `target/`, and Python `.venv` don't collide with the Linux guest.
-   **Parallel sandboxed tasks** on the same project share the VM but work in separate sandbox-view directories.
-   **`.git/hooks/` and `.git/config` are read-only** in the mount, so writes from the VM that would land there fail with `EROFS`.
-   **No `ouijit` CLI in the VM.** The binary isn't installed; the `/api/*` routes return 403 for sandbox-scoped tokens. Only the `/hook` endpoint accepts them.

#### Caveats

-   **Committed files are always visible.** The sandbox view is populated from the index, so anything you committed lives there regardless of `.gitignore`. To remove: `git rm --cached <file>` and rotate the secret.
-   **Commits round-trip through fast-forward.** Parallel commits on the host make the fast-forward fail; Ouijit surfaces the divergence.
-   **The VM has no git credentials.** Pushes happen from the host. Commits made inside the VM ride the next host-side push.
-   **The VM's home directory is out of scope.** The isolation above applies to mounts. Files copied into the VM's home (`~/.netrc`, `~/.git-credentials`) aren't covered.

### Customize the VM

VMs start from a clean Ubuntu 24.04 image with `bash`, `git`, `curl`, `wget`, `nodejs`, `npm`, `python3`, and `build-essential` pre-installed. Defaults are 4 GiB memory, 2 CPUs, 10 GiB disk.

Resource limits, extra packages, additional mounts, and provisioning scripts are controlled by the Lima YAML config in **Project Settings → Sandbox → Lima VM**. The editor shows the merged effective config and lets you write overrides; Ouijit prompts you to recreate the VM when changes require it. See the [Lima configuration reference](https://lima-vm.io/docs/config/) for the available keys.

### Requirements

Ouijit bundles `limactl`; there is nothing extra to install.

-   **macOS**. Apple's Virtualization.framework (vz) with vzNAT networking.
-   **Linux**. QEMU with user-mode networking.

### Limitations

-   First boot downloads an Ubuntu cloud image and provisions packages. It needs internet and takes 1-2 minutes.
-   GPU passthrough is not supported.
-   macOS-specific tooling (Xcode, Swift) is not available inside the Linux VM.
-   Docker-in-VM requires additional Lima configuration.
-   The VM home directory is ephemeral. Anything outside the mounted sandbox-views and `.git` overlays is lost on recreate.

## nono

[nono](https://github.com/nolabs-ai/nono) runs a task's commands in place on the host worktree under kernel-level access limits (Seatbelt on macOS, Landlock on Linux). There is no VM and no file sync, so it starts instantly. Compared to the Lima VM it is a lighter boundary: a kernel access policy on a host process rather than a separate machine.

nono is experimental. Turn it on under **Project Settings → Experimental**; the **nono sandbox** entry then appears in a task's **Open in** menu.

### What it does

Terminals and hooks run on the real worktree, wrapped by `nono`. Filesystem access is limited to the worktree and the repository's git data, with the same read/write overlays at `objects/`, `refs/`, `logs/`, `worktrees/`, so commits land while `hooks/` and `config` stay read-only. Ouijit derives these grants from the task's worktree path at spawn time. Network is allowed by default; the local hook-server port stays reachable so agent status still reports back.

### Configuration

Under **Project Settings → Sandbox → nono**:

-   **Additional folders**. Extra paths granted read/write beyond the worktree and git data, e.g. a shared cache or a monorepo sibling.
-   **Block outbound network**. Deny outbound connections. The hook-server port stays open so agent status keeps working.
-   **Allowed ports**. Extra localhost ports to keep reachable, e.g. a dev server on `3000`.

### Requirements

Ouijit bundles the `nono` binary and the agent packs it uses; there is nothing extra to install.

-   **macOS**. Seatbelt (`sandbox-exec`).
-   **Linux**. Landlock, which needs kernel 5.13 or newer.
