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
+39
View File
@@ -0,0 +1,39 @@
# shellcheck shell=bash
# opencode-sandbox doctor: dependencies and host setup
ocsb_doctor() {
local fail=0 c
ocsb_load_project --optional
echo "Required:"
for c in docker bwrap sg curl jq ncat git; do
if command -v "${c}" >/dev/null; then echo " ✓ ${c}"; else echo " ✗ ${c}"; fail=1; fi
done
local bin
bin=$(ocsb_opencode_bin)
if [[ -x ${bin} ]]; then
echo " ✓ opencode: ${bin} ($("${bin}" --version 2>/dev/null | head -1))"
elif [[ -z ${OCSB_PROJECT_DIR} ]]; then
echo " - opencode: not in PATH (fine if projects set OCSB_OPENCODE_BIN; run doctor inside a project to check)"
else
echo " ✗ opencode: ${bin:-not in PATH} (set OCSB_OPENCODE_BIN in sandbox.conf or put opencode in PATH)"
fail=1
fi
echo "Optional:"
command -v xdg-dbus-proxy >/dev/null && echo " ✓ xdg-dbus-proxy (notifications)" || echo " - xdg-dbus-proxy: no desktop notifications in the sandbox"
if ssh-add -l >/dev/null 2>&1; then echo " ✓ ssh-agent has keys"; else echo " - ssh-agent has no keys: git over SSH fails in the sandbox (ssh-add)"; fi
[[ -d ${OCSB_HOME}/plugins/context-panel/.git || -f ${OCSB_HOME}/plugins/context-panel/.git ]] &&
echo " ✓ plugins checked out" || echo " - plugins missing: git -C ${OCSB_HOME} submodule update --init"
echo "Host:"
if bwrap --ro-bind / / --dev /dev true 2>/dev/null; then echo " ✓ unprivileged bubblewrap works"
else echo " ✗ bubblewrap cannot create a sandbox (user namespaces disabled?)"; fail=1; fi
source "${OCSB_HOME}/lib/netlock.sh"
ocsb_netlock status
if [[ -n ${OCSB_PROJECT_DIR} ]]; then
echo "Project: ${OCSB_PROJECT_DIR} (name ${OCSB_NAME}, state ${OCSB_STATE_DIR})"
source "${OCSB_HOME}/lib/egress.sh"
ocsb_egress status
else
echo "Project: none here (opencode-sandbox init)"
fi
return ${fail}
}