fix(acpmux): delivered machine secrets reach agents started after delivery
What
A secret delivered to a Cloud machine with cmux cloud deliver-secrets (it writes ~/.config/cmux/machine-secrets.env, mode 600) now reaches agents that acpmux starts on that machine afterwards.
Why it didn't
#27487 already re-reads the login shell before a spawn once the secrets file's metadata changes, so the delivered value got into acpmux's imported login environment. After that, login_env::allowed_with let a child have only PATH, proxies, version managers, ANTHROPIC_*/CLAUDE_*/DISABLE_* (Claude), OPENAI_*/CODEX_* (Codex) and loginEnvAllow, and storable dropped every other name from memory. So a delivered DEMO_KEY, or any name outside those prefixes, never reached an agent. A fresh login shell on the machine had it, but /proc/PID/environ of the cmux-claude-real processes did not. The /usr/local/bin/claude wrapper never sources the secrets file.
Change (cmux-tui/crates/acpmux/src/login_env.rs)
- When acpmux reads the login shell, it now also reads the names (only the names) exported in
~/.config/cmux/machine-secrets.env, in the receiver'sexport NAME='ā¦'format. Like the applier, it reads the file only if it is a regular file owned by this user and at most 4 MiB, opened with O_NOFOLLOW|O_NONBLOCK. The buffer is zeroed when dropped, and the values still come from the shell. - Those names pass the allow-list for local children.
- A delivered name always counts as secret, so
login-env.jsonkeeps its name and never its value, even when the name doesn't look secret. - The remote-origin path (
apply_remote_tokio) is unchanged and still passes only PATH. - Revoking works: the next import doesn't list the name and the shell doesn't export it.
Tests
- Unit
delivered_names_reads_only_export_names: reads export names only, dedupes them, refuses a symlink, and returns nothing when the file is missing. - Integration
a_secret_delivered_after_start_reaches_the_next_agent(tests/claude_auth_env.rs, real daemon plus a fake claude):- the secret is delivered after the daemon has started, and the next agent gets
DEMO_KEY; - an undelivered
GITHUB_TOKENstays out; - the cache doesn't hold the dummy value;
- after revocation the next agent doesn't get the key.
- the secret is delivered after the daemon has started, and the next agent gets
Risk: low. It widens what a local agent inherits, but only to names the person explicitly delivered to this machine for its shells and agents. No UI change.
š¤ Generated with Claude Code
Agent session: agent session