Expand description
Cleared-environment construction for every prompt-injectable child the
engine spawns (ticket agent-env-clear, P1 of the 2026-07-28
hostile-workload review).
Before this module, agent CLI sessions spawned with .envs(&spec.env)
overlaid on the FULL ambient environment (backend_claude.rs), and contract
command assertions ran with clear_env = false (command_exec.rs) — so
ambient server secrets (Slack tokens, GH_TOKEN, cloud credentials,
remote-workspace tokens) reached every prompt-injectable child. Now:
- Agent CLI sessions (claude/codex/droid/kimi/cursor backends) spawn
with
env_clear+sanitized_child_env: PATH, a scratch HOME, locale vars, and nothing else — plus backend-specific auth injected explicitly (agent_session_env), never the ambient set. - Contract/gate commands (validation round, final gate, approval-time
contract lint) run with
env_clear+contract_command_env: the sanitized base plusKRANZ_BASE_SHA, a cache-only Cargo home, the non-credential toolchain locations, and at most the operator’scontractEnvPassthroughnames.
The ENGINE process itself keeps its ambient environment — the clearing
applies to child processes only. Merge gates keep their own pre-existing
command_exec::sanitized_gate_env allowlist (it intentionally retains
ambient HOME/CI/temp dirs for the operator’s toolchain; not a clean
swap for this module’s scratch-HOME shape, so both lists stay, each
documented at its site) — with ONE exception: the gate env never carries
the ambient CARGO_HOME, which run_bounded_gate_command replaces with
a fresh [cache_only_cargo_home] exactly like the contract env. Under
worker.sandbox.enforce != off the merge gate additionally runs WRAPPED
in the resolved sandbox profile
(command_exec::run_bounded_gate_command_sandboxed): the ambient HOME
pass-through stays (git identity needs ~/.gitconfig), and the profile
makes it read-only — containment by the sandbox, not by env rewrite.
Secret hygiene: only variable NAMES are ever logged here (the injected auth key’s name, the passthrough names applied/skipped) — never values.
Functions§
- agent_
session_ env - The cleared env for one agent CLI session, uniform across the spawning backends (claude/codex/droid/kimi/cursor).
- contract_
command_ env - The cleared env for one contract/gate command execution (validation
round, final gate, approval-time lint — design decision 3 of the
ticket):
sanitized_child_envover the per-mission writablemission_scratchhome, plus - sanitized_
child_ env - Build a cleared child environment from scratch: EXACTLY
PATH(from ambient — binaries must resolve),HOME = base_home(the scratch dir the session/command already gets, never the operator’s real home),TMPDIR = base_home/tmp, the ambient locale vars when present, the non-credential toolchain locations plus the cache-only Cargo home ([CONTRACT_TOOLCHAIN_VARS] / [cache_only_cargo_home]; without cache seeding every agent session re-downloads the registry into scratch, which filled the disk and killed mission m-533143), and on Windows the process-required passthroughs (AMBIENT_WINDOWS_VARS) plusUSERPROFILE = base_home,TEMP/TMP = base_home/tmp, andAPPDATA/LOCALAPPDATA = base_home/AppData/{Roaming,Local}. Thenextrais applied verbatim, in order — that is whereKRANZ_BASE_SHA, proxy wiring, git identity, and backend-specific auth go. NOTHING else crosses from ambient. - session_
env_ with_ home agent_session_envwith an explicitbase_home— the claude backend uses this after seeding a fresh scratch home (OAuth credentials copy) for a spec that carried no relocated HOME, so the seeded dir is the HOME the child actually gets.- session_
scratch_ home - The per-session scratch
HOMEused when a session spec carries no relocatedHOMEof its own: thehomedir under the same per-session scratch root worker relocation uses (crate::backend_claude::scratch_home_root), so sandboxed sessions get a HOME inside their writable TMPDIR allowlist either way.