Expand description
Instance identity must not be inheritable by the processes a pane spawns.
§The defect this closes
Both pane-spawn paths build their environment by inheriting srv’s, and srv’s
environment is where the launcher injects the whole instance identity set
(DataPaths::to_env_vars — data dir, config dir, cache dir, channel, runtime
mode) and where srv adds more of its own (AGENTMUX_DATA_HOME,
AGENTMUX_CONFIG_HOME, AGENTMUX_LOCAL_URL).
- terminal panes:
portable_pty::CommandBuilderseeds fromstd::env::vars_os(), so.env()calls are overrides on a full copy. - agent panes:
tokio::process::Commandinherits by default.
So every AGENTMUX_* variable reached every pane, and therefore every
process launched from one — including another AgentMux build, which then
adopted the launching instance’s identity instead of the one baked into it.
Confirmed live: a 0.56.3 build launched from a pane wrote into a 0.56.2
instance’s channel. That is invariant I6 breached through a channel I1–I6
never named, because they enumerate OS objects and directories and treat the
threat as “a new launch must not crash a running one” — this is neither.
Full analysis, including the five prior documents that identified this and
left it open:
docs/retro/retro-env-inheritance-instance-isolation-breach-2026-09-17.md.
§The rule
An AGENTMUX_* variable crosses into a pane only if it is on PANE_ENV_KEEP
— an allowlist, not a denylist, so a variable added later is excluded by
default rather than leaking until someone remembers to deny it. That
direction is the whole point: the previous state of the world was an
implicit allow-everything.
This strips only what would arrive by inheritance. Callers apply their own
explicit .env() values afterwards, which is why the terminal path can strip
nearly everything — it re-sets what it needs a few lines later.
Constants§
- NESTING_
SENTINEL_ KEY - The nesting sentinel. Presence tells a launcher started from a pane to
ignore ambient
AGENTMUX_*and re-derive from its own exe path (crates/launcher/src/data_dir.rs). - PANE_
ENV_ KEEP AGENTMUX_*variables permitted to reach a pane by inheritance.
Functions§
- all_
agentmux_ 🔒keys - Every
AGENTMUX*key in this process’s environment except the sentinel. - build_
info - Which AgentMux a pane runs in, set on every process a pane starts so an agent can say so without reading it out of a log path:
- keys_
to_ strip - Keys in this process’s environment that must not cross into a pane.
- sanitize_
external_ command - Strip every
AGENTMUX_*variable, including the in-pane helper keep-set. - sanitize_
external_ pty_ command sanitize_external_commandfor a PTY command.- sanitize_
external_ std_ command sanitize_external_commandfor a synchronousstd::process::Command.- sanitize_
process_ command sanitize_pty_commandfor atokio::process::Command— agent subprocesses, and the/api/v1/shell/createrunner behind the MCPShelltool, which is how CLAUDE.md tells agents to launchtask dev/task package. That path was missed entirely by the first revision of this fix (ReAgent P0), which is notable because it is the single most likely way for an agent to start a second instance.- sanitize_
pty_ command - Apply the policy to a PTY command: strip inherited identity, then mark the child as running inside AgentMux.
- should_
strip - Is
keyan AgentMux variable that must not be inherited by a pane?