Skip to content

fix(t27os): panes no longer inherit a Claude Code host session's environment - #11029

Merged
gHashTag merged 4 commits into
feat/t27os-petal-menufrom
claude/t27os-clean-env
Oct 22, 2026
Merged

gHashTag merged 4 commits into
feat/t27os-petal-menufrom
claude/t27os-clean-env

Conversation

@gHashTag

Copy link
Copy Markdown
Collaborator

Refs #10782

What

A PTY server started from inside a Claude Code session handed that session's environment to every pane it forked. ANTHROPIC_BASE_URL and ANTHROPIC_AUTH_TOKEN pointed the Claude pane at the host's proxy ("Not logged in - Run /login"), and the host's CLAUDE_CODE_MESSAGING_* credential leaked into a child. Observed on the Mac 2026-10-19; the workaround was restarting the server under env -i.

The forked child now unsets the session's variables before exec.

Where

  • specs/t27os/pty_logic.t27: pure env_drops(name, in_session) and env_name_len(entry), with four tests.
  • specs/t27os/pty_server.t27: scrub_env walks environ in the child and unsets what env_drops names. Names only are read, never values.
  • specs/t27os/pty_sys.t27: unsetenv and _NSGetEnviron imports.

What is dropped

  • Prefixes CLAUDE_CODE_, CLAUDE_AGENT_, CLAUDE_PREVIEW_.
  • Exact names CLAUDE_EFFORT, CLAUDE_PID, CLAUDECODE, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL, API_TIMEOUT_MS.
  • Everything else is kept, including CLAUDE_CONFIG_DIR, HOME, PATH, LANG.

Decision: ANTHROPIC_API_KEY

The server cannot tell a user's own key from a host's by looking at it, so it asks whether it runs inside a session (CLAUDECODE set, which only Claude Code sets). Outside a session the key stays; inside one it is dropped. A user who exports a key and then starts the server from a Claude Code session loses it in the panes (log in in the pane, or start the server from a plain terminal or launchd).

Trade-off on CLAUDE_CODE_: a user's own CLAUDE_CODE_USE_BEDROCK or CLAUDE_CODE_OAUTH_TOKEN is dropped too when the server starts inside a session. The prefix is cut whole because a session sets about thirty of them and a name list goes stale with every release.

Evidence (Railway lab, t27b under qemu-aarch64)

  • t27b test specs/t27os/pty_logic.t27: 21 passed, 0 failed.
  • Negative controls on a lab copy: env_drops forced to false fails 3 tests; removing the CLAUDE_AGENT_ rule fails 1; widening CLAUDE_CODE_ to CLAUDE_ fails 3 (the near-miss, API-key and name tests).
  • t27c typecheck on pty_server.t27, pty_logic.t27, pty_sys.t27: 0 errors.

Not verified

The glue (scrub_env) needs the server binary on macOS: pty_server.t27 is blocked under the JIT by @extern, so no unit test runs it. It assumes unsetenv shifts later environ entries down (Darwin does); a 4096-step guard bounds the loop if not. A live check is still owed: start the server inside a Claude Code session, spawn a pane, read its environment names.

Stack

Based on #11015 (claude/t27os-ctl-verbs), which also edits pty_logic.t27 and pty_server.t27, so this diff carries that commit until #11015 lands. Inherited reds on the base (duplicate-bodies, Verilog widths for glass_window / pty_server) are not blessed here.

🤖 Generated with Claude Code

gHashTag and others added 2 commits October 19, 2026 18:31
The T27OS_CTL client dispatched on the first letter of the verb, so
`keys` ran `kill` and took down six panes during the agent check.

- ctl_verb() matches the whole word; unknown words print a usage line
- keys/write decode \e \r \n \t \\ \xHH, T27OS_RAW=1 skips the final CR
- expect polls a pane (ANSI stripped) for T27OS_EXPECT up to T27OS_WAIT s
- 4 new pure tests in pty_logic (17/17); negative control on ctl_verb
  fails as expected

Refs #10782

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ronment

A PTY server started from inside a Claude Code session handed that session's
environment to every pane it forked: ANTHROPIC_BASE_URL and
ANTHROPIC_AUTH_TOKEN pointed the Claude pane at the host's proxy ("Not logged
in - Run /login"), and the host's CLAUDE_CODE_MESSAGING_* credential leaked
into the child.

pty_logic.t27 gets the decision as pure functions with tests: env_drops(name,
in_session) drops the CLAUDE_CODE_, CLAUDE_AGENT_ and CLAUDE_PREVIEW_ prefixes,
the names CLAUDE_EFFORT, CLAUDE_PID, CLAUDECODE, ANTHROPIC_AUTH_TOKEN,
ANTHROPIC_BASE_URL and API_TIMEOUT_MS, and ANTHROPIC_API_KEY only when the
server itself runs inside a session (CLAUDECODE set). Near-misses such as
CLAUDE_CONFIG_DIR are kept. env_name_len splits NAME=value entries.

pty_server.t27 scrub_env walks environ (via _NSGetEnviron) in the forked child
before exec and unsets what env_drops names; only names are read, never values.
pty_sys.t27 gains the unsetenv and _NSGetEnviron imports.

Tests and a negative control (matcher forced to false, a prefix removed, a
prefix widened) ran on the Railway lab. The glue itself needs the server
binary on macOS and is not run there yet.

Refs #10782

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@gHashTag gHashTag added the no-auto-merge Opt out of the issue gate's default squash auto-merge (issue-gate.yml) label Oct 19, 2026
This was referenced Oct 19, 2026
This was referenced Oct 22, 2026
@gHashTag
gHashTag merged commit 4deefe6 into feat/t27os-petal-menu Oct 22, 2026
26 of 31 checks passed
@gHashTag
gHashTag deleted the claude/t27os-clean-env branch October 26, 2026 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-auto-merge Opt out of the issue gate's default squash auto-merge (issue-gate.yml)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants