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
+91
View File
@@ -0,0 +1,91 @@
# shellcheck shell=bash
# Worktrees for parallel agent sessions. Generic part here; project specifics (ports, .env,
# starting a stack, seeding) go into hooks:
# .opencode-sandbox/hooks/wt-post-add after `git worktree add`, cwd = the new worktree
# .opencode-sandbox/hooks/wt-pre-rm before removal (stop the stack), cwd = the worktree
# Hooks get WT_DIR, WT_BRANCH, WT_SLUG, MAIN_ROOT in the environment and the extra
# arguments of `wt add` / `wt rm` as their arguments. A failing hook aborts the command.
ocsb_wt_main_root() {
git -C "${OCSB_PROJECT_DIR}" rev-parse --path-format=absolute --git-common-dir | sed 's|/\.git$||'
}
ocsb_wt_slug() {
local branch=$1 slug=""
if [[ -n ${OCSB_WT_SLUG_REGEX} && ${branch} =~ ${OCSB_WT_SLUG_REGEX} ]]; then
slug=${BASH_REMATCH[1]}
fi
[[ -n ${slug} ]] || slug=$(printf '%s' "${branch}" | tr '/' '-' | tr -c 'a-zA-Z0-9._-' '-')
echo "${slug}"
}
ocsb_wt_hook() {
local name=$1 dir=$2
shift 2
local hook=${OCSB_PROJECT_DIR}/.opencode-sandbox/hooks/${name}
[[ -x ${hook} ]] || return 0
ocsb_info "hook ${name}"
(cd "${dir}" && "${hook}" "$@")
}
ocsb_wt_add() {
[[ $# -ge 1 ]] || ocsb_die "usage: opencode-sandbox wt add <branch> [hook args]"
local branch=$1 main slug dir base
shift
git check-ref-format --branch "${branch}" >/dev/null 2>&1 || ocsb_die "invalid branch name: ${branch}"
main=$(ocsb_wt_main_root)
slug=$(ocsb_wt_slug "${branch}")
dir=${OCSB_WORKTREES_DIR}/${OCSB_WT_PREFIX}${slug}
[[ -e ${dir} ]] && ocsb_die "already exists: ${dir}"
base=${OCSB_WT_BASE:-HEAD}
if [[ ${base} != HEAD ]] && git -C "${main}" rev-parse -q --verify "origin/${base}" >/dev/null; then
echo " ${base} is $(git -C "${main}" rev-list --count "${base}..origin/${base}") commit(s) behind origin/${base} (git fetch if needed)"
fi
mkdir -p "${OCSB_WORKTREES_DIR}"
git -C "${main}" worktree add -b "${branch}" "${dir}" "${base}"
export WT_DIR=${dir} WT_BRANCH=${branch} WT_SLUG=${slug} MAIN_ROOT=${main}
ocsb_wt_hook wt-post-add "${dir}" "$@"
echo "✓ worktree ${dir} (branch ${branch}); remove: opencode-sandbox wt rm ${slug}"
}
ocsb_wt_rm() {
[[ $# -ge 1 ]] || ocsb_die "usage: opencode-sandbox wt rm <name|path> [--force]"
local name=$1 force=0 main dir branch status a extra=()
shift
for a in "$@"; do
if [[ ${a} == --force ]]; then force=1; else extra+=("${a}"); fi
done
main=$(ocsb_wt_main_root)
case "${name}" in
/*) dir=${name} ;;
*) dir=${OCSB_WORKTREES_DIR}/${OCSB_WT_PREFIX}${name} ;;
esac
[[ -d ${dir} ]] || ocsb_die "no such worktree: ${dir}"
[[ $(realpath "${dir}") != "$(realpath "${main}")" ]] || ocsb_die "refusing to remove the main worktree"
branch=$(git -C "${dir}" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
export WT_DIR=${dir} WT_BRANCH=${branch} WT_SLUG=${name##*/} MAIN_ROOT=${main}
ocsb_wt_hook wt-pre-rm "${dir}" "${extra[@]}"
status=$(git -C "${dir}" status --porcelain)
if [[ -n ${status} && ${force} -ne 1 ]]; then
echo "${status}" >&2
ocsb_die "worktree has uncommitted changes — commit them or pass --force (they will be lost)"
fi
if [[ ${force} -eq 1 ]]; then
git -C "${main}" worktree remove --force "${dir}"
else
git -C "${main}" worktree remove "${dir}"
fi
git -C "${main}" worktree prune
echo "✓ removed ${dir} (branch ${branch:-?} kept)"
}
ocsb_wt() {
local sub=${1:-list}
[[ $# -gt 0 ]] && shift
case "${sub}" in
add) ocsb_wt_add "$@" ;;
rm) ocsb_wt_rm "$@" ;;
list) git -C "${OCSB_PROJECT_DIR}" worktree list ;;
*) ocsb_die "wt: unknown subcommand ${sub} (add|rm|list)" ;;
esac
}