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

# Run OpenCode with Tensorlake Sandboxes

> Route OpenCode's file and shell tools into a Tensorlake sandbox with a single plugin. The model edits, runs, and searches inside an isolated environment instead of on your machine.

[OpenCode](https://opencode.ai) is a terminal coding agent. The [`tensorlake-opencode`](https://www.npmjs.com/package/tensorlake-opencode) plugin redirects the agent's hands (its file and shell tools) into a Tensorlake sandbox, so the model's commands and edits run in an isolated environment you control rather than on your laptop.

## The model: brain local, hands in the sandbox

OpenCode keeps running locally: the TUI, the model loop, and your session all stay on your machine. The plugin only intercepts the **tool calls** and routes them to a sandbox:

| OpenCode tool | Runs in the sandbox as                                                |
| ------------- | --------------------------------------------------------------------- |
| `bash`        | `sandbox.run('sh', { args: ['-c', cmd] })`                            |
| `read`        | `sandbox.readFile(path)`                                              |
| `write`       | `sandbox.writeFile(path, content)`                                    |
| `edit`        | read + string replace + write                                         |
| `multiedit`   | several string replacements on one file, then one write               |
| `apply_patch` | parse the patch, then read/write each file in the sandbox             |
| `ls`          | `sandbox.listDirectory(path)`                                         |
| `glob`        | `find` in the sandbox via bash, matched against the project directory |
| `grep`        | `grep -rn …` via bash, with `include` to narrow the file set          |

`apply_patch` matters more than it looks. OpenCode's built-in `apply_patch` writes to your **local** filesystem. The plugin shadows it (plugin version 0.3.0 and later) so a patch cannot bypass the sandbox.

`webfetch` and `websearch` are **not** intercepted. They stay local, since they don't touch your filesystem.

Subagents (the `task` tool) run in their own OpenCode session but use the sandbox of the session that spawned them. See [Subagents](#subagents).

The sandbox itself runs on Tensorlake: fast boot, sub-second resume from a suspended snapshot, and state that persists across OpenCode restarts. See [Sandbox lifecycle](/sandboxes/lifecycle) for the suspend/resume and snapshot model underneath.

## Why route tool calls into a sandbox

* **Isolation.** The agent's `bash` and `write` never touch your host. A bad command, a runaway install, or an `rm` lands in a disposable environment, not your working tree.
* **Reproducible environment.** Every session gets the same image, CPU, and memory regardless of what's on the developer's machine. Pin a custom image with the right toolchain once and every session inherits it.
* **State that survives restarts.** Sandboxes are named and persisted to disk, so a session reconnects to its sandbox across OpenCode restarts; a suspended sandbox resumes with `/tmp/workspace`, installed deps, and warm caches intact.
* **Work that outlives the sandbox.** Mount a [Tensorlake filesystem](/filesystems/introduction) or attach a [hosted git repository](/git/introduction), and the agent's files survive sandbox deletion. See [Persist work across sandboxes](#persist-work-across-sandboxes).

## Prerequisites

* An [OpenCode](https://opencode.ai) installation.
* A Tensorlake **project API key**. Sign up at [cloud.tensorlake.ai](https://cloud.tensorlake.ai), open your project, and create a key under **API Keys**. Personal Access Tokens are not supported.
* A supported platform. The Tensorlake SDK ships a native binary for macOS on Apple Silicon (Intel Macs are not supported), Linux x64 and arm64 (glibc and musl), and Windows x64.

## Setup

<Steps>
  <Step title="Add the plugin to your OpenCode config">
    Add the package name to `~/.config/opencode/opencode.json` (create the file if it doesn't exist):

    ```json theme={null}
    {
      "$schema": "https://opencode.ai/config.json",
      "plugin": [
        "tensorlake-opencode"
      ]
    }
    ```

    OpenCode treats bare names as npm packages and installs them into its own cache (`~/.cache/opencode/packages/`). You don't run `npm install` yourself.
  </Step>

  <Step title="Log in">
    The plugin registers Tensorlake in OpenCode's standard auth flow (plugin version 0.2.0 and later):

    ```bash theme={null}
    opencode auth login
    ```

    Select **Tensorlake** and paste a project API key (it starts with `tl_apiKey_`). OpenCode stores the key in its credential store next to your other provider credentials. If the key is wrong, the first tool call shows an error toast that tells you to log in again — no restart needed, just retry after logging in.

    **CI / automation:** set the `TENSORLAKE_API_KEY` environment variable instead. It wins over the stored key. Use a project API key; the key itself selects the organization and project.
  </Step>

  <Step title="Start OpenCode">
    ```bash theme={null}
    opencode
    ```

    On startup the plugin loads but **no sandbox is created yet**. Confirm it loaded by tailing its log:

    ```bash theme={null}
    tail -f ~/.local/share/opencode/log/tensorlake.log
    ```

    You should see a single line: `OpenCode started with TensorLake plugin`.
  </Step>
</Steps>

## Lazy sandbox creation

<Note>
  The sandbox is created **lazily, on the first intercepted tool call** in a session, not when you launch OpenCode. If you start OpenCode and nothing appears to happen, that's expected. A session that only uses `webfetch`/`websearch` will never spin one up, because neither is intercepted.
</Note>

To trigger creation, ask the model to run something that uses a file or shell tool:

```
Run: uname -a
```

On that first `bash` call the plugin provisions the sandbox. You'll see a **"Sandbox created"** toast and new log lines:

```
[INFO] Creating new sandbox for session abc123
[INFO] Sandbox created sandbox-xyz in 2300ms
```

`uname -a` will report **Linux** (the sandbox), confirming the command ran remotely rather than on your Mac.

## Verify it's working

Ask the model to write and read a file back:

```
Write the text "Hello Tensorlake" to /tmp/workspace/test.txt, then read it back.
```

The `write` call routes to `sandbox.writeFile()` and the `read` call to `sandbox.readFile()`, both over the SDK. The agent's working directory inside the sandbox is `/tmp/workspace`.

## Subagents

When the model uses the `task` tool, OpenCode starts a subagent in a child session. The child session does not get its own sandbox. It shares the sandbox of the session that spawned it, all the way up to the root session of the tree. This mirrors how local subagents share the user's working tree: the parent sees the subagent's edits, and the subagent sees the parent's.

* **Deleting a subagent session deletes nothing.** The sandbox belongs to the root session, and is torn down when *that* session is deleted.
* **Deleting the root session waits for subagent tool calls** that are still running, so nothing is terminated mid-write.

## Persist work across sandboxes

By default a sandbox's disk is ephemeral: deleting the session deletes its files. Two optional plugin settings let the agent's work outlive the sandbox. You set them as plugin options in `opencode.json`, or as environment variables (the environment variable wins).

### Persistent filesystem

Create a [Tensorlake filesystem](/filesystems/introduction) and name it in the plugin options:

```bash theme={null}
tl fs create my-workspace
```

```json theme={null}
{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    ["tensorlake-opencode", { "filesystem": "my-workspace" }]
  ]
}
```

Every sandbox now mounts that filesystem at the working directory, `/tmp/workspace`. Files the agent writes there persist in durable storage, survive sandbox deletion, and are visible to every other sandbox or `tl fs mount` that mounts the same filesystem. Set `filesystemPath` (or `TENSORLAKE_FILESYSTEM_PATH`) to mount it somewhere else.

A misspelled filesystem name blocks tool calls with a **"Filesystem attach failed"** toast instead of silently running against ephemeral storage.

<Warning>
  To put local files into the filesystem, use `tl fs mount <name> <empty-dir>`, not `tl fs push`. Mount is two-way and continuous. Push is one-way and destructive: it makes the filesystem match your local directory exactly, so a second push **deletes every file the agent created**. Use push once to seed a filesystem, never while a session is running. See [Mount filesystems](/sandboxes/mount-filesystems) and [Concurrent writes](/filesystems/concurrent-writes).
</Warning>

### Hosted git repository

To let the agent persist work through git, name a [Tensorlake git repository](/git/introduction). The repository is hosted in your Tensorlake project, not on GitHub. Only people with an API key for the project can see it or push to it.

```json theme={null}
{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    ["tensorlake-opencode", { "gitRepo": "my-repo" }]
  ]
}
```

The plugin creates the repository if it does not exist. In each sandbox it configures a Tensorlake credential scoped to that one repository and a fallback git identity, and refreshes the credential in long sessions. The model is told the clone URL, so "clone the repo, make the change, push" works out of the box.

Your own git credentials and remotes never enter the sandbox. To publish the code elsewhere, clone the Tensorlake repository to your machine and push it to GitHub yourself.

You can use `filesystem` and `gitRepo` together. The agent then clones the repository onto the mounted filesystem, so the working copy survives sandbox deletion and the pushed commits live in the repository. If you also mount that filesystem on your machine, run git commands from one side only: concurrent writes to the same `.git` files lose data silently.

## Configure the sandbox

The plugin has two kinds of settings:

* **Plugin options** in `opencode.json` choose what the sandbox attaches to: a filesystem and a git repository. Each also has an environment variable, and the variable wins.
* **Environment variables** decide what the sandbox looks like: its image, CPUs, memory, and disk. There is no config-file form for these; you set them in the shell, then launch OpenCode from that same shell.

The plugin reads both **at sandbox-creation time**.

<Note>
  The variables are read **once, when the sandbox is created** (the first intercepted tool call of a session). Set them *before* you run `opencode`. Changing a variable in another terminal, or after the sandbox already exists, has no effect on the running session. Start a new session to pick up new values.
</Note>

### How it fits together

```bash theme={null}
# 1. Authenticate once (stored by OpenCode, no env var needed)
opencode auth login   # select Tensorlake, paste a project API key

# 2. Size the sandbox VM
export TENSORLAKE_CPUS=4
export TENSORLAKE_MEMORY_MB=8192
export TENSORLAKE_DISK_MB=20480

# 3. Choose the toolchain image (optional, omit for the platform default)
export TENSORLAKE_IMAGE=my-custom-image

# 4. Launch: the next sandbox this session creates uses all of the above
opencode
```

Every value above describes the sandbox the plugin spins up for that OpenCode session, not OpenCode itself, and not your local machine.

### Plugin options

Set these in `opencode.json` as the second element of the plugin entry: `["tensorlake-opencode", { ... }]`.

| Option           | Env var                      | What it controls                                                                                                 |
| ---------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `filesystem`     | `TENSORLAKE_FILESYSTEM`      | Tensorlake filesystem to mount into every sandbox. See [Persistent filesystem](#persistent-filesystem)           |
| `filesystemPath` | `TENSORLAKE_FILESYSTEM_PATH` | Mount path for that filesystem. Default: `/tmp/workspace`                                                        |
| `gitRepo`        | `TENSORLAKE_GIT_REPO`        | Tensorlake-hosted git repository to set up in every sandbox. See [Hosted git repository](#hosted-git-repository) |

### All variables

| Variable                       | Default            | What it controls                                                                                |
| ------------------------------ | ------------------ | ----------------------------------------------------------------------------------------------- |
| `TENSORLAKE_API_KEY`           | (optional)         | Overrides the key stored by `opencode auth login`. For CI/automation                            |
| `TENSORLAKE_IMAGE`             | (platform default) | Registered image the sandbox boots from. Bake your runtimes, build tools, and repo deps in here |
| `TENSORLAKE_CPUS`              | `2`                | vCPUs allocated to the sandbox                                                                  |
| `TENSORLAKE_MEMORY_MB`         | `4096`             | RAM allocated to the sandbox, in MB                                                             |
| `TENSORLAKE_DISK_MB`           | `10240`            | Ephemeral disk allocated to the sandbox, in MB                                                  |
| `TENSORLAKE_SHUTDOWN_DRAIN_MS` | `15000`            | How long OpenCode exit waits for in-flight sandbox work before suspending the sandbox           |

### Making the settings persistent

Your API key persists on its own — `opencode auth login` stores it once. The filesystem and git settings persist in `opencode.json`. The sandbox-sizing variables are shell exports, so to apply the same config every time, add them to your shell profile (`~/.zshrc` or `~/.bashrc`):

```bash theme={null}
echo 'export TENSORLAKE_IMAGE=my-custom-image' >> ~/.zshrc
echo 'export TENSORLAKE_CPUS=4' >> ~/.zshrc
```

Open a new terminal (or `source ~/.zshrc`) and every `opencode` session inherits them.

### Use a custom image

`TENSORLAKE_IMAGE` is the most impactful setting for real work: it lets every OpenCode session start from an environment that already has your language runtimes, system packages, and project dependencies, so the agent isn't reinstalling them on each session. Register an image, then point the variable at its name:

```bash theme={null}
tl sbx image create Dockerfile --registered-name my-custom-image
export TENSORLAKE_IMAGE=my-custom-image
```

See [Build and Import Images](/sandboxes/images) for building and managing images.

## Troubleshooting

**No sandbox starts when I launch OpenCode.** Expected. The sandbox is created on the first tool call, not at launch. Ask the model to run a command (for example, `Run: uname -a`).

**"Tensorlake login required" toast.** Run `opencode auth login`, select Tensorlake, and paste a project API key. No restart is needed; retry the tool call.

**Auth error (401/403) in the log.** The stored key was revoked. Re-run `opencode auth login` with a fresh project API key.

**"Filesystem attach failed" toast.** The name in `filesystem` / `TENSORLAKE_FILESYSTEM` does not exist in this project. Create it with `tl fs create <name>` or fix the name.

**`tl fs unmount` fails with `volume_busy`.** A process still holds the mount, usually a shell whose working directory is inside it. Leave the directory (`cd ~`), close editors pointed at the path, then unmount.

**`Missing native binding for <platform>`.** Your platform is not supported by the Tensorlake SDK. See [Prerequisites](#prerequisites).

**Anything else.** Check `~/.local/share/opencode/log/tensorlake.log`. If the file does not exist, the plugin never loaded.

## Next steps

<CardGroup cols={2}>
  <Card title="Plugin source" icon="github" href="https://github.com/tensorlakeai/opencode-tensorlake-plugin">
    The full plugin: tool interceptors, session manager, and lifecycle handling.
  </Card>

  <Card title="Sandbox lifecycle" icon="arrows-rotate" href="/sandboxes/lifecycle">
    The suspend/resume and snapshot model that persists session state.
  </Card>

  <Card title="Build and Import Images" icon="layer-group" href="/sandboxes/images">
    Build a custom image so every OpenCode session gets the same toolchain.
  </Card>

  <Card title="Filesystems" icon="hard-drive" href="/filesystems/introduction">
    Durable storage the agent's files survive in after the sandbox is gone.
  </Card>

  <Card title="Git repositories" icon="code-branch" href="/git/introduction">
    Hosted repositories the agent can clone, commit, and push to.
  </Card>

  <Card title="Tool calls" icon="robot" href="/sandboxes/tool-calls">
    The general pattern: expose sandboxes as tools to any LLM agent.
  </Card>
</CardGroup>
