opencode-sandbox: OpenCode 2 in a sandbox for any project

Runs OpenCode 2 with full permissions while containing its mistakes:

- egress filter: squid in a container per project; squid.conf is rendered
  from layered egress.conf files (package, user, project, local) with
  allow/block/connect rules and expect-pass/expect-block self-tests
- netlock: nftables rejects direct egress for the opencode-sandbox group
  (installed once with sudo as a systemd unit)
- bubblewrap: read-only host, writable project, worktrees and state;
  credentials hidden; ssh keys via the host's ssh-agent; session bus
  replaced by an xdg-dbus-proxy limited to notifications; no permission
  prompts (sudo and systemd-run denied); refuses to run unsandboxed
- per-project state in ~/.local/share/opencode-sandbox/<name>
- worktrees: `wt add|rm|list` with project hooks
- skills mounted per project; the opencode-sandbox skill explains the
  environment to the agent
- plugins as submodules: context-panel, skill-compact-resurrect;
  global config adds opencode-notifier and cc-safety-net
- README in English and Russian
This commit is contained in:
2026-10-30 01:00:02 -07:00
commit 64c222a084
29 changed files with 1945 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
---
name: opencode-sandbox
description: "How the opencode-sandbox environment works: what is writable, how network access is filtered (proxy, 403, connect rules, git over SSH), hidden credentials, Docker, worktrees (opencode-sandbox wt add/rm). Use when a command fails with 'Read-only file system', 'Permission denied', a proxy 403 or a connection error, before touching git remotes, Docker or files outside the project, and before creating or removing a worktree (worktrees are only for changing code on a new branch — not for questions, search, analysis or review)."
---
# Working inside opencode-sandbox
The project directory is `$OCSB_PROJECT`. `$OCSB_IN_SANDBOX` is `1` when the filesystem
sandbox is on.
## Files
| Path | Access |
|---|---|
| The project, its worktrees directory, paths from `OCSB_RW` in `.opencode-sandbox/sandbox.conf` | read-write, real disk |
| Toolchain caches in `~` (`.cache`, `.npm`, `.asdf`, `.bundle`, `.gem`, …) | read-write |
| `/tmp` | read-write, shared with the host |
| Everything else | read-only |
| Private SSH keys, `~/.gnupg`, cloud CLI credentials, `~/.kube/config`, browser profiles, other agents' configs, `OCSB_HIDE` paths | hidden (empty) |
- `Read-only file system` / `EROFS` means the path is outside the writable set. Do not retry,
do not look for another way to write there. If the task really needs it, tell the user
which path and why — they can add it to `OCSB_RW`.
- A hidden credential is not a bug either; ask the user instead of searching for it.
## Network
- All HTTP(S) goes through the egress proxy (`HTTPS_PROXY`). Direct connections (ping, raw
TCP, IP literals, SSH without the proxy) are rejected by the kernel.
- A `403` from the proxy, or `CONNECT tunnel failed, response 403`, is policy. Tell the user
the exact host (and port) you need and why. Rules live in `.opencode-sandbox/egress.conf`
(`allow`, `block`, `connect host:port`); do not edit them yourself unless asked.
- Private networks, names without a dot and ports other than 80/443 are blocked unless the
project allows them with `connect`.
- git over SSH works through the proxy (`GIT_SSH_COMMAND` is set) when the host and port are
allowed with `connect`; the key is in the host's ssh-agent, not on disk.
- Containers get the proxy through `DOCKER_CONFIG` (builds and `docker run`).
## Things you cannot do
- Open a browser (`xdg-open` is disabled): print the link for the user.
- `sudo`, `systemd-run`: denied.
- Start processes outside the sandbox through the session bus: only desktop notifications
are reachable.
## Docker
The Docker socket is available, so Docker can see the whole host. Mount only paths inside
the project or its worktrees; never mount `/`, `~` or system directories.
## Worktrees
A worktree is for **changing code on a new branch** — and only that. Do not create one for
questions, search, reading code, analysis, review or planning: work in the current tree
read-only. Do not create one "just in case" before you know changes are needed, and do not
create a second one if your session already has a worktree for the task.
When you do change code: one session — one worktree. Create and remove them with the
package's command; the project's hooks start and stop its stack:
```shell
opencode-sandbox wt add feature-PROJ-42-login --seed # extra args go to the project hook
opencode-sandbox wt rm 42 # refuses if there are uncommitted changes
opencode-sandbox wt rm 42 --force # discards them
opencode-sandbox wt list
```
Worktrees live in the worktrees directory next to the project (writable). Remove yours
after the merge, and right away if it turned out no changes were needed; `wt list` shows
leftovers.