Private /tmp, no display, git over SSH and netlock check fixes

- /tmp: the agent gets its own (<state>/tmp, files older than 7 days are
  removed on start) — the host's /tmp holds sockets that lead out of the
  sandbox (tmux, X11, browsers' singleton sockets, other tools).
  OCSB_TMP=shared keeps the old behaviour. /tmp is mounted before the
  project and state binds so they stay visible if they live under /tmp
- DISPLAY, WAYLAND_DISPLAY, XAUTHORITY are empty in the sandbox and
  ~/.Xauthority is hidden; browsers must run headless
- GIT_SSH_COMMAND uses -F ~/.ssh/config: in the user namespace root-owned
  system ssh configs look owned by nobody and ssh refuses them
- netlock self-test checks that the proxy opens the tunnel, not that a
  remote site answers; failed self-tests now say so instead of exiting
  silently
- doctor: no false error for opencode outside a project
- skill and READMEs: worktrees only for code changes, private /tmp,
  headless browsers, troubleshooting entries
This commit is contained in:
2026-10-30 01:09:53 -07:00
parent 5193dbe573
commit 6dec88641a
5 changed files with 48 additions and 14 deletions
+3 -2
View File
@@ -19,7 +19,7 @@ ocsb_info() { echo "→ $*"; }
OCSB_SCALARS=(
OCSB_NAME OCSB_WEB_PORT OCSB_EGRESS_PORT OCSB_NETLOCK_GROUP OCSB_SANDBOX OCSB_PANEL
OCSB_NOTIFICATIONS OCSB_OPENCODE_BIN OCSB_ENV_FILE OCSB_KUBECONFIG OCSB_PASSWORD_FILE
OCSB_NOTIFICATIONS OCSB_TMP OCSB_OPENCODE_BIN OCSB_ENV_FILE OCSB_KUBECONFIG OCSB_PASSWORD_FILE
OCSB_WORKTREES_DIR OCSB_WT_PREFIX OCSB_WT_BASE OCSB_WT_SLUG_REGEX OCSB_DOCKER_GW
OCSB_STATE_DIR OCSB_OPENCODE_CONFIG OCSB_EGRESS_BASE
)
@@ -33,6 +33,7 @@ ocsb_defaults() {
OCSB_SANDBOX=1 # bubblewrap; 0 = egress filter and netlock only
OCSB_PANEL=1 # "Tokens" panel in the web UI (plugins/context-panel)
OCSB_NOTIFICATIONS=1 # desktop notifications through a filtered D-Bus proxy
OCSB_TMP=private # private: own /tmp in the state dir; shared: the host's /tmp
OCSB_OPENCODE_BIN="" # default: `opencode` from PATH
OCSB_ENV_FILE="" # KEY=VALUE file with tokens, exported to OpenCode
OCSB_KUBECONFIG="" # kubeconfig for the agent (~/.kube/config stays hidden)
@@ -53,7 +54,7 @@ ocsb_defaults() {
OCSB_HIDE_HOME=(.gnupg .aws .azure .config/gcloud .kube/config .docker/config.json .netrc
.git-credentials .config/gh/hosts.yml .password-store .local/share/keyrings .mozilla
.config/google-chrome .config/chromium .config/BraveSoftware .claude .codex
.config/opencode .local/share/opencode)
.config/opencode .local/share/opencode .Xauthority)
OCSB_SKILLS=(opencode-sandbox) # skills mounted for the agent (names of skill directories)
OCSB_SKILL_DIRS=() # extra directories to look for skills in
OCSB_PLUGINS=(skill-compact-resurrect/skill-compact-resurrect.v2.ts) # files in plugins/
+16 -2
View File
@@ -124,11 +124,23 @@ ocsb_bwrap_args() {
OCSB_BWRAP=(
--die-with-parent --new-session --unshare-pid --unshare-ipc --unshare-uts
--ro-bind / / --dev /dev --proc /proc
--bind /tmp /tmp
--tmpfs "/run/user/${uid}"
)
# /tmp: the host's holds sockets that lead out of the sandbox (tmux, X11, browsers,
# other agents) — by default the agent gets its own, kept in the state directory.
# Mounted before the project and the state, so they stay visible if they live under /tmp
if [[ ${OCSB_TMP} == shared ]]; then
OCSB_BWRAP+=(--bind /tmp /tmp)
else
mkdir -p "${OCSB_STATE_DIR}/tmp"
chmod 700 "${OCSB_STATE_DIR}/tmp"
find "${OCSB_STATE_DIR}/tmp" -mindepth 1 -mtime +7 -delete 2>/dev/null || true
OCSB_BWRAP+=(--bind "${OCSB_STATE_DIR}/tmp" /tmp)
fi
OCSB_BWRAP+=(
--bind "${OCSB_PROJECT_DIR}" "${OCSB_PROJECT_DIR}"
--bind "${OCSB_WORKTREES_DIR}" "${OCSB_WORKTREES_DIR}"
--bind "${OCSB_STATE_DIR}" "${OCSB_STATE_DIR}"
--tmpfs "/run/user/${uid}"
)
for p in "${OCSB_RW[@]}"; do
p=$(ocsb_abs "${p}")
@@ -243,6 +255,8 @@ ocsb_run() {
# No permission prompts in the sandbox: permission rules from all configs form one
# list, the last match wins, and OPENCODE_CONFIG_CONTENT is the last document.
# Denies are repeated after "*" so they still win.
# No display: GUI programs fail fast; browsers must run headless
env+=("DISPLAY=" "WAYLAND_DISPLAY=" "XAUTHORITY=")
env+=('OPENCODE_CONFIG_CONTENT={"permission":{"bash":{"*":"allow","systemd-run *":"deny","sudo *":"deny"},"external_directory":{"*":"allow"}}}')
echo " sandbox: writable ${OCSB_PROJECT_DIR}, ${OCSB_WORKTREES_DIR}${OCSB_RW[*]:+, ${OCSB_RW[*]}}; the rest of the system is read-only"
fi