/tmp-block
Fail-CLOSED PreToolUse guard denying durable-content writes to temp paths (/tmp, /private/tmp, /var/folders, $TMPDIR) across Write/Edit and Bash writes. Triggers: tmp guard, TMP-BLOCK deny, bypass ledger. NOT for storage policy.
$ golems-cli skills install tmp-blockUpdated 2 weeks ago
Hook-carried skill. The enforcing artifact is hooks/tmp-block-pretooluse.py
(PreToolUse on Write|Edit|NotebookEdit|Bash); this page is its contract and
audit guide.
Why it exists (S04, weave 2026-06-07 Phase-2 Fix-3)
A worker ran Write(/tmp/orqi-tts-answer-msg.md); the era's /tmp guard
half-fired (validated then allowed) — a hook validation error let the
durable write through, and Etan caught it live ("Wait, why are we writing
those things in temp?"). Class evidence: ≥24 /tmp anti-pattern findings,
4 proven costs in 24h (reboot-wiped leak worksheet, a harness fix stranded in
a prunable /tmp worktree, Etan's own terminal burned by a /tmp-worktree-held
branch). Adversary verdict: KEEP, with the deny extended to the
canonicalized path-CLASS and to Bash writes, "else it's one directory
away from useless."
The contract
| Surface | Behavior |
|---|---|
Write/Edit/NotebookEdit into the class | DENY + redirect to the repo / docs.local/ |
Bash output redirect (>, >>, &>), incl. heredoc+redirect | DENY |
Bash tee / tee -a into the class | DENY |
git worktree add into the class | DENY + redirect to <repo>/.worktrees/ |
Reads/deletes (ls, cat, grep, rm, worktree list) | NEVER denied |
| Any hook/validation error (incl. unwritable ledger) | DENY — fail CLOSED (the S04 half-fire class) |
CLAUDE_WORKER | does NOT exempt (S04's violator was a worker) |
Path-CLASS (canonicalized via realpath): /tmp, /private/tmp,
/var/folders, /private/var/folders, the live $TMPDIR value, and literal
$TMPDIR tokens in Bash commands. The macOS /tmp → /private/tmp symlink is
not a route-around.
Escape hatch = bypass-detector seed
Over-broad guards INDUCE route-arounds (adversary Attack 5.4), so genuinely-ephemeral writes have a sanctioned path:
WEAVE_ALLOW_TMP=1 <command> # inline, or exported for the sessionEvery use is logged to the durable ledger
~/.claude/logs/tmp-block-ledger.jsonl (override: TMP_BLOCK_LEDGER). If the
ledger cannot be written, the bypass is DENIED — an unlogged bypass must not
proceed. Audit the ledger during weaves: entries that look durable
(.md notes, worksheets, worktrees) are route-arounds to flag.
Full SKILL.md source — includes LLM directives, anti-patterns, and technical instructions stripped from the Overview tab.
Hook-carried skill. The enforcing artifact is hooks/tmp-block-pretooluse.py
(PreToolUse on Write|Edit|NotebookEdit|Bash); this page is its contract and
audit guide.
Why it exists (S04, weave 2026-06-07 Phase-2 Fix-3)
A worker ran Write(/tmp/orqi-tts-answer-msg.md); the era's /tmp guard
half-fired (validated then allowed) — a hook validation error let the
durable write through, and Etan caught it live ("Wait, why are we writing
those things in temp?"). Class evidence: ≥24 /tmp anti-pattern findings,
4 proven costs in 24h (reboot-wiped leak worksheet, a harness fix stranded in
a prunable /tmp worktree, Etan's own terminal burned by a /tmp-worktree-held
branch). Adversary verdict: KEEP, with the deny extended to the
canonicalized path-CLASS and to Bash writes, "else it's one directory
away from useless."
The contract
| Surface | Behavior |
|---|---|
Write/Edit/NotebookEdit into the class | DENY + redirect to the repo / docs.local/ |
Bash output redirect (>, >>, &>), incl. heredoc+redirect | DENY |
Bash tee / tee -a into the class | DENY |
git worktree add into the class | DENY + redirect to <repo>/.worktrees/ |
Reads/deletes (ls, cat, grep, rm, worktree list) | NEVER denied |
| Any hook/validation error (incl. unwritable ledger) | DENY — fail CLOSED (the S04 half-fire class) |
CLAUDE_WORKER | does NOT exempt (S04's violator was a worker) |
Path-CLASS (canonicalized via realpath): /tmp, /private/tmp,
/var/folders, /private/var/folders, the live $TMPDIR value, and literal
$TMPDIR tokens in Bash commands. The macOS /tmp → /private/tmp symlink is
not a route-around.
Escape hatch = bypass-detector seed
Over-broad guards INDUCE route-arounds (adversary Attack 5.4), so genuinely-ephemeral writes have a sanctioned path:
WEAVE_ALLOW_TMP=1 <command> # inline, or exported for the sessionEvery use is logged to the durable ledger
~/.claude/logs/tmp-block-ledger.jsonl (override: TMP_BLOCK_LEDGER). If the
ledger cannot be written, the bypass is DENIED — an unlogged bypass must not
proceed. Audit the ledger during weaves: entries that look durable
(.md notes, worksheets, worktrees) are route-arounds to flag.
Known limits (the detection frontier)
Statically uncaught: inline-interpreter writes (python3 -c "open('/tmp/…')"),
cp/mv/rsync into the class, relative paths from a temp cwd. The ledger +
a periodic temp-dir scan for agent-shaped artifacts are the detectors for that
frontier — detectors trail the evasion frontier by construction.
Install & evals
- Install:
hooks/INSTALL.md(symlink + settings.json PreToolUse entry). - Evals:
hooks/tests/test_tmp_block.py— 17 cases incl. the verbatim S04 replay; RED against the prior guard stack (15/17 pass-through), GREEN 17/17. Run:python3 -m pytest hooks/tests/ -v.
Changelog entries are derived from eval runs and skill version updates. Full cascading changelog (Phase 4D) coming soon.
- +Initial release to Golems skill library