Protect host-run files from the agent; trust registry; git audit

- read-only in the sandbox: .opencode-sandbox/, .cc-safety-net/, .git/hooks, .git/config,
  an in-project core.hooksPath, the egress rules and the safety-net home; .git is pinned
  by a bind onto itself so it cannot be swapped
- only trusted projects are loaded (init / trust): a .opencode-sandbox/ in a worktree or a
  subdirectory is no longer sourced on the host
- run (before and after the session) and doctor report planted .git entries, redirected
  worktrees and per-worktree overrides
- hide Firefox, Claude, VS Code, Cursor and Codex configs
This commit is contained in:
2026-10-30 01:31:36 -07:00
parent 5a7567849e
commit 01fc1627c1
8 changed files with 143 additions and 8 deletions
+20 -1
View File
@@ -70,7 +70,11 @@ browser opens at `http://127.0.0.1:4196/`; log in as `opencode` with the passwor
`~/.local/share/opencode-sandbox/<project>/password` (printed on the first start). `~/.local/share/opencode-sandbox/<project>/password` (printed on the first start).
Commit `.opencode-sandbox/` (except `local/`) and `.cc-safety-net/` — everyone who clones the Commit `.opencode-sandbox/` (except `local/`) and `.cc-safety-net/` — everyone who clones the
project gets the same sandbox. project gets the same sandbox. `sandbox.conf` and the hooks are bash that runs on your host, so
only trusted projects are loaded: `init` trusts the project it creates; in a clone, read
`.opencode-sandbox/` and run `opencode-sandbox trust` (the list is
`~/.config/opencode-sandbox/projects`). A `.opencode-sandbox/` in a worktree or a
subdirectory is ignored.
## Project files ## Project files
@@ -233,6 +237,14 @@ Inside the sandbox:
browsers' singleton sockets, other tools), so the agent gets its own, stored in browsers' singleton sockets, other tools), so the agent gets its own, stored in
`<state>/tmp` (files older than 7 days are removed on start). Look there for its screenshots `<state>/tmp` (files older than 7 days are removed on start). Look there for its screenshots
and logs. Docker mounts of `/tmp` paths would point at the host's `/tmp` — mount project paths. and logs. Docker mounts of `/tmp` paths would point at the host's `/tmp` — mount project paths.
- **Read-only inside the project:** what the host runs outside the sandbox — `.opencode-sandbox/`,
`.cc-safety-net/`, `.git/hooks`, `.git/config` (`core.fsmonitor` there runs on any
`git status`, a shell prompt is enough), `core.hooksPath` if it is in the project — and the
egress rules and safety-net home in the state directory. `.git` itself is a mount point, so
it cannot be renamed and replaced. Git still works; only `git config` writes fail (e.g.
`push -u` cannot record the upstream). After each session `run` reports any other `.git` in
the project and worktrees, worktrees whose `.git` points elsewhere and per-worktree
overrides; `doctor` does the same.
- **Hidden:** `OCSB_HIDE_HOME`, `OCSB_HIDE`, private keys in `~/.ssh` (`config`, `known_hosts` - **Hidden:** `OCSB_HIDE_HOME`, `OCSB_HIDE`, private keys in `~/.ssh` (`config`, `known_hosts`
and `*.pub` stay visible; the host's ssh-agent signs). and `*.pub` stay visible; the host's ssh-agent signs).
- **Network:** `HTTP(S)_PROXY` point to the filter. `GIT_SSH_COMMAND` sends ssh through it with - **Network:** `HTTP(S)_PROXY` point to the filter. `GIT_SSH_COMMAND` sends ssh through it with
@@ -269,6 +281,12 @@ place, requests to production — not to hold a determined attacker.
- **Allowed hosts are a channel.** Data can leave through any host the rules allow. - **Allowed hosts are a channel.** Data can leave through any host the rules allow.
- **The project is writable.** Mistakes inside it are real; cc-safety-net blocks the common - **The project is writable.** Mistakes inside it are real; cc-safety-net blocks the common
destructive commands, git keeps history — commit often. destructive commands, git keeps history — commit often.
- **Files you run yourself.** Hooks, configs and git metadata are protected (see above), but
the agent edits code: a changed `Makefile`, `package.json` script or `bin/` file runs with
your rights when you run it. Look at `git diff` before running project commands on the host.
- **Planted repositories are reported, not prevented.** A `.git` the agent creates in a new
directory or a new worktree is found by the check after the session; don't run git there
until you have looked.
- With `OCSB_TMP=shared` the host's `/tmp` and its sockets are reachable. - With `OCSB_TMP=shared` the host's `/tmp` and its sockets are reachable.
- With `OCSB_DISPLAY=1` the agent can open windows on your desktop and use the clipboard while - With `OCSB_DISPLAY=1` the agent can open windows on your desktop and use the clipboard while
its window is focused. its window is focused.
@@ -304,6 +322,7 @@ docker rmi opencode-sandbox-egress
``` ```
opencode-sandbox init add .opencode-sandbox/ to the current repository opencode-sandbox init add .opencode-sandbox/ to the current repository
opencode-sandbox trust [dir] trust an existing .opencode-sandbox/ (a clone)
opencode-sandbox config effective settings opencode-sandbox config effective settings
opencode-sandbox doctor dependencies and host setup opencode-sandbox doctor dependencies and host setup
opencode-sandbox run [opencode args] everything, then OpenCode opencode-sandbox run [opencode args] everything, then OpenCode
+21 -1
View File
@@ -69,7 +69,11 @@ opencode-sandbox run
`~/.local/share/opencode-sandbox/<проект>/password` (выводится при первом запуске). `~/.local/share/opencode-sandbox/<проект>/password` (выводится при первом запуске).
Закоммитьте `.opencode-sandbox/` (кроме `local/`) и `.cc-safety-net/` — каждый, кто Закоммитьте `.opencode-sandbox/` (кроме `local/`) и `.cc-safety-net/` — каждый, кто
склонирует проект, получит ту же песочницу. склонирует проект, получит ту же песочницу. `sandbox.conf` и хуки — это bash, который
выполняется на вашем хосте, поэтому загружаются только доверенные проекты: `init` доверяет
созданному проекту, а в клоне прочитайте `.opencode-sandbox/` и выполните
`opencode-sandbox trust` (список — `~/.config/opencode-sandbox/projects`).
`.opencode-sandbox/` в worktree или подкаталоге игнорируется.
## Файлы проекта ## Файлы проекта
@@ -233,6 +237,14 @@ opencode-sandbox run
(tmux, X11, сокеты браузеров, других программ), поэтому у агента свой `/tmp` — в (tmux, X11, сокеты браузеров, других программ), поэтому у агента свой `/tmp` — в
`<состояние>/tmp` (файлы старше 7 дней удаляются при запуске). Скриншоты и логи агента ищите `<состояние>/tmp` (файлы старше 7 дней удаляются при запуске). Скриншоты и логи агента ищите
там. Docker смонтирует путь из `/tmp` из `/tmp` хоста — монтируйте пути проекта. там. Docker смонтирует путь из `/tmp` из `/tmp` хоста — монтируйте пути проекта.
- **Только чтение внутри проекта:** то, что хост выполняет вне песочницы, — `.opencode-sandbox/`,
`.cc-safety-net/`, `.git/hooks`, `.git/config` (`core.fsmonitor` оттуда срабатывает при любом
`git status`, хватит prompt'а оболочки), `core.hooksPath`, если он внутри проекта, — а также
правила egress и домашний каталог safety-net в каталоге состояния. Сам `.git` — точка
монтирования, его нельзя переименовать и подменить. git работает; не проходит только запись
`git config` (например, `push -u` не запишет upstream). После каждой сессии `run` сообщает
о других `.git` в проекте и worktree, о worktree, чей `.git` указывает в другое место, и о
переопределениях для отдельных worktree; `doctor` проверяет то же.
- **Скрыто:** `OCSB_HIDE_HOME`, `OCSB_HIDE`, приватные ключи в `~/.ssh` (`config`, - **Скрыто:** `OCSB_HIDE_HOME`, `OCSB_HIDE`, приватные ключи в `~/.ssh` (`config`,
`known_hosts` и `*.pub` видны; подписывает ssh-agent хоста). `known_hosts` и `*.pub` видны; подписывает ssh-agent хоста).
- **Сеть:** `HTTP(S)_PROXY` указывают на фильтр. `GIT_SSH_COMMAND` пускает ssh через него - **Сеть:** `HTTP(S)_PROXY` указывают на фильтр. `GIT_SSH_COMMAND` пускает ssh через него
@@ -269,6 +281,13 @@ opencode-sandbox run
правила. правила.
- **Проект доступен на запись.** Ошибки внутри него настоящие; cc-safety-net блокирует - **Проект доступен на запись.** Ошибки внутри него настоящие; cc-safety-net блокирует
типичные разрушительные команды, git хранит историю — коммитьте чаще. типичные разрушительные команды, git хранит историю — коммитьте чаще.
- **Файлы, которые вы запускаете сами.** Хуки, конфиги и метаданные git защищены (см. выше),
но код агент правит: изменённый `Makefile`, скрипт в `package.json` или файл в `bin/`
выполнится с вашими правами, когда вы его запустите. Смотрите `git diff`, прежде чем
запускать команды проекта на хосте.
- **Подложенные репозитории обнаруживаются, а не предотвращаются.** `.git`, созданный агентом
в новом каталоге или новом worktree, найдёт проверка после сессии; не запускайте там git,
пока не посмотрели.
- С `OCSB_TMP=shared` доступны `/tmp` хоста и его сокеты. - С `OCSB_TMP=shared` доступны `/tmp` хоста и его сокеты.
- С `OCSB_DISPLAY=1` агент может открывать окна на вашем экране и пользоваться буфером обмена, - С `OCSB_DISPLAY=1` агент может открывать окна на вашем экране и пользоваться буфером обмена,
пока его окно в фокусе. пока его окно в фокусе.
@@ -304,6 +323,7 @@ docker rmi opencode-sandbox-egress
``` ```
opencode-sandbox init добавить .opencode-sandbox/ в текущий репозиторий opencode-sandbox init добавить .opencode-sandbox/ в текущий репозиторий
opencode-sandbox trust [каталог] доверять существующему .opencode-sandbox/ (клон)
opencode-sandbox config итоговые настройки opencode-sandbox config итоговые настройки
opencode-sandbox doctor зависимости и настройки хоста opencode-sandbox doctor зависимости и настройки хоста
opencode-sandbox run [аргументы opencode] всё сразу, затем OpenCode opencode-sandbox run [аргументы opencode] всё сразу, затем OpenCode
+4
View File
@@ -16,6 +16,7 @@ Usage: opencode-sandbox <command> [args]
Project setup Project setup
init Create .opencode-sandbox/ in the current git repository init Create .opencode-sandbox/ in the current git repository
trust [dir] Trust an existing .opencode-sandbox/ (a clone): its config runs on the host
config Print the effective configuration config Print the effective configuration
doctor Check dependencies and host setup doctor Check dependencies and host setup
@@ -46,6 +47,9 @@ case "${command_name}" in
source "${OCSB_HOME}/lib/init.sh" source "${OCSB_HOME}/lib/init.sh"
ocsb_init "$@" ocsb_init "$@"
;; ;;
trust)
ocsb_trust "$@"
;;
config) config)
ocsb_load_project ocsb_load_project
ocsb_print_config ocsb_print_config
+31 -5
View File
@@ -55,19 +55,44 @@ ocsb_defaults() {
OCSB_HIDE_HOME=(.gnupg .aws .azure .config/gcloud .kube/config .docker/config.json .netrc 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 .git-credentials .config/gh/hosts.yml .password-store .local/share/keyrings .mozilla
.config/google-chrome .config/chromium .config/BraveSoftware .claude .codex .config/google-chrome .config/chromium .config/BraveSoftware .claude .codex
.config/opencode .local/share/opencode .Xauthority) .config/opencode .local/share/opencode .Xauthority .config/mozilla .config/Claude .config/Code
.config/Cursor .config/Codex)
OCSB_SKILLS=(opencode-sandbox) # skills mounted for the agent (names of skill directories) OCSB_SKILLS=(opencode-sandbox) # skills mounted for the agent (names of skill directories)
OCSB_SKILL_DIRS=() # extra directories to look for skills in OCSB_SKILL_DIRS=() # extra directories to look for skills in
OCSB_PLUGINS=(skill-compact-resurrect/skill-compact-resurrect.v2.ts) # files in plugins/ OCSB_PLUGINS=(skill-compact-resurrect/skill-compact-resurrect.v2.ts) # files in plugins/
OCSB_ENV=() # extra KEY=VALUE for OpenCode OCSB_ENV=() # extra KEY=VALUE for OpenCode
} }
# Trusted projects (init / trust), one physical path per line. Only these are loaded:
# sandbox.conf is bash run on the host, and the agent can write anywhere in the project
# tree, so a .opencode-sandbox/ found in a worktree or a subdirectory proves nothing.
ocsb_trust_file() { echo "${XDG_CONFIG_HOME:-${HOME}/.config}/opencode-sandbox/projects"; }
ocsb_trusted() { [[ -f $(ocsb_trust_file) ]] && grep -Fxq -- "$1" "$(ocsb_trust_file)"; }
ocsb_trust() {
local dir f
dir=$(cd "${1:-.}" && pwd -P) || ocsb_die "no such directory: ${1}"
[[ -f ${dir}/.opencode-sandbox/sandbox.conf ]] || ocsb_die "no ${dir}/.opencode-sandbox/sandbox.conf"
[[ -z ${OCSB_IN_SANDBOX:-} ]] || ocsb_die "trust is a host command"
ocsb_trusted "${dir}" && { echo "already trusted: ${dir}"; return 0; }
f=$(ocsb_trust_file)
mkdir -p "$(dirname "${f}")"
echo "${dir}" >>"${f}"
echo "✓ trusted: ${dir}"
}
# Nearest trusted project at or above the directory
ocsb_find_project() { ocsb_find_project() {
local dir=${1:-$PWD} local dir untrusted=""
dir=$(cd "${1:-.}" && pwd -P)
while [[ ${dir} != / ]]; do while [[ ${dir} != / ]]; do
[[ -f ${dir}/.opencode-sandbox/sandbox.conf ]] && { echo "${dir}"; return 0; } if [[ -f ${dir}/.opencode-sandbox/sandbox.conf ]]; then
ocsb_trusted "${dir}" && { echo "${dir}"; return 0; }
untrusted=${untrusted:-${dir}}
fi
dir=$(dirname "${dir}") dir=$(dirname "${dir}")
done done
[[ -z ${untrusted} ]] ||
ocsb_warn "${untrusted} is not a trusted project: read its .opencode-sandbox/ (sandbox.conf and hooks run on the host), then: opencode-sandbox trust ${untrusted}"
return 1 return 1
} }
@@ -82,12 +107,13 @@ ocsb_load_project() {
OCSB_PROJECT_DIR="" OCSB_PROJECT_DIR=""
if [[ -n ${OCSB_PROJECT:-} ]]; then if [[ -n ${OCSB_PROJECT:-} ]]; then
OCSB_PROJECT_DIR=$(cd "${OCSB_PROJECT}" && pwd) OCSB_PROJECT_DIR=$(cd "${OCSB_PROJECT}" && pwd -P)
ocsb_trusted "${OCSB_PROJECT_DIR}" || ocsb_die "OCSB_PROJECT=${OCSB_PROJECT} is not a trusted project (opencode-sandbox trust)"
else else
OCSB_PROJECT_DIR=$(ocsb_find_project) || true OCSB_PROJECT_DIR=$(ocsb_find_project) || true
fi fi
if [[ -z ${OCSB_PROJECT_DIR} || ! -f ${OCSB_PROJECT_DIR}/.opencode-sandbox/sandbox.conf ]]; then if [[ -z ${OCSB_PROJECT_DIR} || ! -f ${OCSB_PROJECT_DIR}/.opencode-sandbox/sandbox.conf ]]; then
[[ ${optional} == 1 ]] || ocsb_die "no .opencode-sandbox/sandbox.conf here or above (run: opencode-sandbox init)" [[ ${optional} == 1 ]] || ocsb_die "no trusted .opencode-sandbox/ here or above (new project: opencode-sandbox init)"
fi fi
export OCSB_PROJECT_DIR export OCSB_PROJECT_DIR
+2
View File
@@ -32,6 +32,8 @@ ocsb_doctor() {
echo "Project: ${OCSB_PROJECT_DIR} (name ${OCSB_NAME}, state ${OCSB_STATE_DIR})" echo "Project: ${OCSB_PROJECT_DIR} (name ${OCSB_NAME}, state ${OCSB_STATE_DIR})"
source "${OCSB_HOME}/lib/egress.sh" source "${OCSB_HOME}/lib/egress.sh"
ocsb_egress status ocsb_egress status
source "${OCSB_HOME}/lib/run.sh"
if ocsb_git_audit; then echo " ✓ no planted git metadata in the project and worktrees"; else fail=1; fi
else else
echo "Project: none here (opencode-sandbox init)" echo "Project: none here (opencode-sandbox init)"
fi fi
+2
View File
@@ -17,12 +17,14 @@ ocsb_init() {
mkdir -p "${root}/.cc-safety-net" mkdir -p "${root}/.cc-safety-net"
cp "${t}/cc-safety-net-policy.json" "${root}/.cc-safety-net/policy.json" cp "${t}/cc-safety-net-policy.json" "${root}/.cc-safety-net/policy.json"
fi fi
ocsb_trust "${root}" >/dev/null
cat <<EOF cat <<EOF
✓ created ${root}/.opencode-sandbox/ ✓ created ${root}/.opencode-sandbox/
sandbox.conf settings (ports, writable paths, skills, worktrees) — commit it sandbox.conf settings (ports, writable paths, skills, worktrees) — commit it
egress.conf domains for this project — commit it egress.conf domains for this project — commit it
hooks/ worktree hooks: drop .example and chmod +x to enable hooks/ worktree hooks: drop .example and chmod +x to enable
local/ personal overrides (sandbox.conf, egress.conf), git-ignored local/ personal overrides (sandbox.conf, egress.conf), git-ignored
The project is trusted on this machine; a clone elsewhere needs: opencode-sandbox trust
Next: Next:
opencode-sandbox doctor check the host opencode-sandbox doctor check the host
+57 -1
View File
@@ -117,6 +117,59 @@ ocsb_start_dbus_proxy() {
return 1 return 1
} }
# Files the host trusts and runs outside the sandbox stay read-only for the agent: the
# sandbox config and hooks, the safety-net policies, the egress rules, git hooks and config
# (core.fsmonitor runs on any git status — a shell prompt is enough). .git is first bound
# onto itself: a mount point cannot be renamed, so it cannot be swapped for a planted one.
ocsb_protect_args() {
local d=${OCSB_PROJECT_DIR} p hooks
mkdir -p "${d}/.cc-safety-net" "${OCSB_STATE_DIR}/cc-safety-net"
for p in "${d}/.opencode-sandbox" "${d}/.cc-safety-net" "${OCSB_STATE_DIR}/cc-safety-net" "${OCSB_STATE_DIR}/egress"; do
OCSB_BWRAP+=(--ro-bind "${p}" "${p}")
done
if [[ -d ${d}/.git ]]; then
mkdir -p "${d}/.git/hooks"
OCSB_BWRAP+=(--bind "${d}/.git" "${d}/.git")
for p in "${d}"/.git/{hooks,config} "${d}"/.git/modules/*/{hooks,config}; do
[[ -e ${p} ]] && OCSB_BWRAP+=(--ro-bind "${p}" "${p}")
done
fi
# core.hooksPath inside the project (husky and the like)
hooks=$(git -C "${d}" config --get core.hooksPath 2>/dev/null) || true
if [[ -n ${hooks} ]]; then
hooks=$(cd "${d}" && realpath -m -- "${hooks/#\~/${HOME}}")
[[ ${hooks} == "${d}"/* && -e ${hooks} ]] && OCSB_BWRAP+=(--ro-bind "${hooks}" "${hooks}")
fi
}
# Git repositories planted for the host's git. The main .git is read-only for the agent, but
# it can still create a .git anywhere it writes, point a worktree's .git elsewhere, or add
# per-worktree overrides — and a repository's config can run commands. Report all of that.
ocsb_git_audit() {
local main=${OCSB_PROJECT_DIR}/.git f gitdir found=() roots=()
[[ -d ${main} ]] || return 0
roots=("${OCSB_PROJECT_DIR}")
[[ -d ${OCSB_WORKTREES_DIR} ]] && roots+=("$(realpath -- "${OCSB_WORKTREES_DIR}")")
while IFS= read -r -d '' f; do
[[ ${f} == "${main}" ]] && continue
if [[ -f ${f} && ! -L ${f} ]]; then
gitdir=$(sed -n '1s/^gitdir: //p' "${f}")
[[ ${gitdir} == "${main}/worktrees/"* && ${gitdir} != */..* && $(cat "${gitdir}/gitdir" 2>/dev/null) == "${f}" ]] && continue
fi
found+=("${f}")
done < <(find "${roots[@]}" -name .git -prune -print0 2>/dev/null)
for f in "${main}"/worktrees/*/; do
[[ -d ${f} ]] || continue
[[ $(cat "${f}commondir" 2>/dev/null) == ../.. ]] || found+=("${f}commondir")
[[ -e ${f}config.worktree ]] && found+=("${f}config.worktree")
done
[[ -e ${main}/commondir ]] && found+=("${main}/commondir")
((${#found[@]} == 0)) && return 0
ocsb_warn "unexpected git metadata — check it before running git there (a repository config can run commands):"
printf ' %s\n' "${found[@]}" >&2
return 1
}
# bubblewrap arguments for the sandbox (variant: read-only host, writable project) # bubblewrap arguments for the sandbox (variant: read-only host, writable project)
ocsb_bwrap_args() { ocsb_bwrap_args() {
local p f uid local p f uid
@@ -174,6 +227,7 @@ ocsb_bwrap_args() {
OCSB_BWRAP+=(--ro-bind /dev/null "${p}") OCSB_BWRAP+=(--ro-bind /dev/null "${p}")
fi fi
done done
ocsb_protect_args
if [[ -n ${OCSB_DBUS_PROXY_PID:-} && -S $(ocsb_dbus_sock) ]]; then if [[ -n ${OCSB_DBUS_PROXY_PID:-} && -S $(ocsb_dbus_sock) ]]; then
OCSB_BWRAP+=(--ro-bind "$(ocsb_dbus_sock)" "/run/user/${uid}/bus") OCSB_BWRAP+=(--ro-bind "$(ocsb_dbus_sock)" "/run/user/${uid}/bus")
fi fi
@@ -250,12 +304,13 @@ ocsb_run() {
ssh-add -l >/dev/null 2>&1 || ocsb_warn "ssh-agent has no keys — git over SSH will fail in the sandbox (ssh-add)" ssh-add -l >/dev/null 2>&1 || ocsb_warn "ssh-agent has no keys — git over SSH will fail in the sandbox (ssh-add)"
if ocsb_start_dbus_proxy; then if ocsb_start_dbus_proxy; then
env+=("DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus") env+=("DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus")
trap 'kill "${OCSB_DBUS_PROXY_PID}" 2>/dev/null || true' EXIT
else else
unset OCSB_DBUS_PROXY_PID unset OCSB_DBUS_PROXY_PID
env+=("DBUS_SESSION_BUS_ADDRESS=") env+=("DBUS_SESSION_BUS_ADDRESS=")
fi fi
ocsb_bwrap_args ocsb_bwrap_args
# On any exit (Ctrl-C too): stop the bus proxy, look for what the session planted
trap 'kill "${OCSB_DBUS_PROXY_PID:-}" 2>/dev/null; ocsb_git_audit || true' EXIT
cmd=(bwrap "${OCSB_BWRAP[@]}" -- "${cmd[@]}") cmd=(bwrap "${OCSB_BWRAP[@]}" -- "${cmd[@]}")
# No permission prompts in the sandbox: permission rules from all configs form one # 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. # list, the last match wins, and OPENCODE_CONFIG_CONTENT is the last document.
@@ -282,6 +337,7 @@ ocsb_run() {
for kv in "${env[@]}"; do inner+=" $(printf '%q' "${kv}")"; done for kv in "${env[@]}"; do inner+=" $(printf '%q' "${kv}")"; done
for kv in "${cmd[@]}"; do inner+=" $(printf '%q' "${kv}")"; done for kv in "${cmd[@]}"; do inner+=" $(printf '%q' "${kv}")"; done
[[ ${OCSB_SANDBOX} == 1 ]] && { ocsb_git_audit || true; }
local opened=0 line local opened=0 line
cd "${OCSB_PROJECT_DIR}" || return cd "${OCSB_PROJECT_DIR}" || return
sg "${OCSB_NETLOCK_GROUP}" -c "${inner}" 2>&1 | while IFS= read -r line; do sg "${OCSB_NETLOCK_GROUP}" -c "${inner}" 2>&1 | while IFS= read -r line; do
+6
View File
@@ -16,12 +16,18 @@ sandbox is on.
| Toolchain caches in `~` (`.cache`, `.npm`, `.asdf`, `.bundle`, `.gem`, …) | read-write | | Toolchain caches in `~` (`.cache`, `.npm`, `.asdf`, `.bundle`, `.gem`, …) | read-write |
| `/tmp` | read-write, **private** to the sandbox (on the host it is `<state>/tmp`; files older than 7 days are cleaned) | | `/tmp` | read-write, **private** to the sandbox (on the host it is `<state>/tmp`; files older than 7 days are cleaned) |
| Everything else | read-only | | Everything else | read-only |
| `.opencode-sandbox/`, `.cc-safety-net/`, `.git/hooks`, `.git/config` | read-only (the host runs them) |
| Private SSH keys, `~/.gnupg`, cloud CLI credentials, `~/.kube/config`, browser profiles, other agents' configs, `OCSB_HIDE` paths | hidden (empty) | | 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, - `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 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`. 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. - A hidden credential is not a bug either; ask the user instead of searching for it.
- `git config` writes fail (`could not lock config file`): commits, branches, worktrees, fetch
and push work; `push -u` just cannot record the upstream — use `git push origin <branch>`.
Need a sandbox, egress or safety-net rule changed? Tell the user what and why.
- Do not create git repositories (`git init`, `git clone`) inside the project or the worktrees
directory unless asked: the user is warned about every unexpected `.git` there.
## Network ## Network