Warning
Work in progress. This plugin is unfinished and unproven. It is not published, its behavior is not stable, and parts of it have never run against a real workload. Do not depend on it for isolation you actually need. Expect bugs and breaking changes.
An OpenCode V2 plugin that runs the agent's shell tool inside a per-project
microsandbox microVM instead of on
the host.
The built-in shell tool runs with the host user's filesystem, process, and network
authority. Permission rules can ask or deny, but an allowed command still runs on the
host. This plugin replaces that tool with one that executes in a microVM:
- One persistent VM per project (
oc-sbx-<projectID>), so installed tools survive across sessions. - The project directory is mounted at its identical host path, so absolute paths work unchanged.
- Egress is denied by default. Only allowlisted hosts are reachable, and the metadata, private, and loopback ranges are blocked.
- Secrets stay on the host. The guest sees a placeholder, substituted for allowlisted hosts only.
- Fail-closed. If the runtime is missing, the VM cannot start, or an input cannot be
sandboxed (
background: true), the tool call fails. It never falls back to the host.
- OpenCode V2 (
@opencode/plugin2.0.8). - The
msb/ microsandbox runtime on the host. - Linux with KVM, or macOS on Apple Silicon.
V2 resolves a local plugin directory by path: <dir>/index for the server and
<dir>/tui for the TUI. The root index.ts and tui.ts exist for that reason.
| Option | Default | Notes |
|---|---|---|
image |
oc-sandbox-toolchain:latest |
OCI image or local rootfs; applied at create. |
memory |
1024 |
MiB. |
cpus |
2 |
vCPUs. |
shell |
/bin/sh |
Shell used to run commands. |
user |
none | User inside the VM. |
network |
{ "mode": "allowlist", "allow": [] } |
mode: none, allowlist, or all. allow entries are domains or destination-group keywords. |
secrets |
{} |
NAME -> { env: "VAR", hosts: [...] }. env is the host variable to read; it defaults to NAME. Literal values are rejected. Optional requireTlsIdentity (default true); set false only for a plain-HTTP target. (Do not use {env:VAR} in opencode.json(c): OpenCode resolves it before the plugin sees its options.) |
volumes |
[] |
Extra bind, named, or tmpfs mounts. |
maxDurationSeconds, idleTimeoutSeconds |
none | VM lifetime and idle auto-stop. |
prewarm |
true |
Start the VM when the plugin loads. |
recreate |
false |
Recreate the VM on ensure. |
The legacy switches disableNetwork, allowAllNetwork, and allowedHosts still map
onto network.
Each network.allow entry is either a domain (api.github.com) or one of the
destination-group keywords public, private, loopback, link-local,
metadata, multicast, host. Use host to let the sandbox reach a service on
the host's localhost — inside the guest that is host.microsandbox.internal, not
localhost (which is the guest's own loopback). In allowlist mode the plugin
adds the gateway DNS rule whenever the allowlist is non-empty; a custom
deny-by-default policy otherwise denies every lookup.
// reach a dev server on the host and one public registry
"network": { "mode": "allowlist", "allow": ["host", "registry.npmjs.org"] }For a secret to be substituted on a request to that host, its hosts must list
the destination the guest actually uses, e.g. "host.microsandbox.internal".
/sandbox show settings and VM status
/sandbox image [ref] show or set the OCI image
/sandbox memory [MiB] show or set VM memory
/sandbox cpus [count] show or set vCPUs
/sandbox shell [path] show or set the command shell
/sandbox network [mode] show, or set none | allowlist | all
/sandbox network allow <host...>
/sandbox network deny <host...>
/sandbox secret list list secret names and hosts (never values)
/sandbox secret add <ENV> <host...>
/sandbox secret remove <ENV>
/sandbox reset restore the configured defaults
/sandbox apply recreate the VM with current settings
/sandbox log print the plugin log path
/sandbox runs in the TUI and calls the server plugin over RPC, so it needs no model
turn. Settings changes apply on the next create; /sandbox apply applies them now.
The model gets a read-only sandbox tool (status, help, log). Every
mutating command — image, resources, network policy, and secret add/remove/list
— is human-only and reachable only through the TUI /sandbox command. This keeps
the model from binding a host secret to a host it controls and exfiltrating it
through the substitution proxy. Secret values are never read or returned.
Accepts command, workdir, timeout, and background. background is rejected.
Output is returned as text with a trailing [exit code: N].
- Fail closed on a missing runtime, failed VM setup, or unsupported input.
- Never mount
~/.ssh,~/.aws,~/.config, or token directories. - Secrets are placeholder-only in the guest; real values stay on the host.
- Egress denied by default; dangerous ranges always blocked.
- Project mounted at its identical host path.
- No secret values in logs or tool output.
- Only
shellis overridden.read,write,edit,patch,glob, andgrepstill touch the host filesystem. Full isolation needs the file-tool overrides sketched insrc/file-tools.ts. backgroundexecution is unsupported.- The plugin itself is trusted host code. It sandboxes agent commands, not itself.
pnpm install
pnpm typecheckTypechecking needs the Linux native TypeScript compiler, so run it inside the guest
toolchain rather than on macOS. The plugin logs to
<project>/.opencode/microsandbox.log.
{ "plugins": [ { "package": "/absolute/path/to/sandbox-plugin", "options": { "network": { "mode": "allowlist", "allow": ["registry.npmjs.org"] }, "secrets": { "GITHUB_TOKEN": { "env": "GITHUB_TOKEN", "hosts": ["api.github.com"] } } } } ] }