13. Deliver the integrity guard globally via managed-settings#
Date: 2026-06-14
Status#
Accepted
Context#
A guard must assert “Claude is actually inside the shadow” in every folder —
even ones with no project .claude/. Two earlier designs failed: per-repo
project .claude/ hooks left un-promoted folders unguarded; an intermediate
user-scope ~/.claude/settings.json design was silently removable by editing
that shared, cross-container file.
Decision#
Deliver the guard through Claude Code’s managed-settings layer
(/etc/claude-code/managed-settings.json) — the highest-precedence tier, which
a user cannot override or remove from user-scope — with two hooks whose scripts
live in /usr/libexec/claude-sandbox/ (root-owned, off-PATH, ro inside the
sandbox):
SessionStart → sandbox-verify.sh: advisory full integrity battery, warns loudly when unwrapped (SessionStart cannot block).UserPromptSubmit → sandbox-gate.sh: fail-closed,exit 2unlessIS_SANDBOX=1. Escape hatchCLAUDE_SANDBOX_ALLOW_UNWRAPPED=1; both skip onCLAUDE_CODE_REMOTE=true.
This is the same “security inputs live outside the rw workspace” discipline as
12. Treat the read-write workspace as untrusted: default to $PWD, source config from /etc: under ~/.claude the scripts would be rw-bound
and a compromised session could rewrite the gate to exit 0.
Consequences#
Editing
~/.claude/settings.jsoncannot disable the guard — onlyrootediting/etcor a deliberate./installcan.We deliberately do not set
allowManagedHooksOnly(managed hooks are additive — the owner’s own hooks still run).wire_user_statuslineprunes any guard hooks an earlier user-scope install left behind, so the guard has one authoritative home.The same managed layer also hard-disables the in-container auto-updater (
DISABLE_AUTOUPDATER=1+autoUpdates:false; first shipped in this work, commitf4928d4). This is root-cause removal of a PATH-bypass re-arm: the updater otherwise re-creates~/.local/bin/claudeunwrapped on a version bump (see 9. Relocate the real Claude binary off PATH so the shadow always wins), so updates become a deliberate./installthat re-relocates the binary and re-asserts the shadow.autoUpdatesChannel:"stable"only slows updates and would not fix this.Refuse: moving the guard back to per-repo/user-scope; putting the scripts in the sandbox rw set; setting
allowManagedHooksOnly; or hard-failing install over a non-JSON settings file we don’t exclusively own (warn-and-skip instead). Fleet-wide enforcement against accidental unwrapped hostclaude(issue #41) extends this layer.