fix(aos): forward every Claude credential the auth check accepts #1001

Merged
coilysiren merged 2 commits from aos/claude/claude-env-credentials-cross into main 2026-08-12 07:24:59 +00:00
Member

docs/aos-claude-auth.md states the contract: ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, and CLAUDE_CODE_OAUTH_TOKEN "cross the container boundary by name when present, so nothing is projected."

Only the first one actually crossed.

The gap

claudeEnvironmentAuthPresent accepted all three and returned an empty projection on that basis. forwardedEnvironment forwarded ANTHROPIC_API_KEY alone. Those two lists were the only places either name appears outside tests.

So a host holding only ANTHROPIC_AUTH_TOKEN or CLAUDE_CODE_OAUTH_TOKEN, with no credentials file and no Keychain entry, passed the auth gate, projected nothing, forwarded nothing, and started the container logged out. That is #968's symptom reached through a different door, and it contradicts the repo's own doc.

The Codex side was correct, but by luck rather than by structure: its check-set and its forward-set were two separate literals that happened to agree.

The change

Not "add the two missing keys to the second list." That leaves the shape that produced the bug. This removes the second list.

Both the check and the forward now read claudeEnvironmentAuthKeys and codexEnvironmentAuthKeys, so accepting a key and forwarding it are one edit rather than two that can disagree. environmentAuthPresent collapses the two identical loops.

TestEveryAcceptedEnvironmentCredentialCrossesTheBoundary asserts the invariant by reading those slices rather than restating them, per the AGENTS rule that tests never re-encode configuration. A key added to either set has to cross the boundary or the test fails.

Verified

  • ward exec aos-test green.
  • ward exec pre-commit-all green.
  • Behavior under --auth=false is unchanged: no credential key is forwarded, and non-secret harness tuning still is.

Not in this PR

While tracing this I hit a related question that is yours to answer rather than mine, so I left it alone: --dry-run currently resolves credentials and fails closed without them, which is why the release smoke has to pass --auth=false at four call sites and why #907 and #1000 were the same one-line patch twice. docs/aos-codex-auth.md documents that dry-run behavior deliberately ("Dry-run output contains only the auth path and mount shape"), so changing it is a contract decision, not a bug fix. Raised separately.

🤖 Generated with Claude Code

`docs/aos-claude-auth.md` states the contract: `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and `CLAUDE_CODE_OAUTH_TOKEN` "cross the container boundary by name when present, so nothing is projected." Only the first one actually crossed. ## The gap `claudeEnvironmentAuthPresent` accepted all three and returned an empty projection on that basis. `forwardedEnvironment` forwarded `ANTHROPIC_API_KEY` alone. Those two lists were the only places either name appears outside tests. So a host holding only `ANTHROPIC_AUTH_TOKEN` or `CLAUDE_CODE_OAUTH_TOKEN`, with no credentials file and no Keychain entry, passed the auth gate, projected nothing, forwarded nothing, and started the container logged out. That is #968's symptom reached through a different door, and it contradicts the repo's own doc. The Codex side was correct, but by luck rather than by structure: its check-set and its forward-set were two separate literals that happened to agree. ## The change Not "add the two missing keys to the second list." That leaves the shape that produced the bug. This removes the second list. Both the check and the forward now read `claudeEnvironmentAuthKeys` and `codexEnvironmentAuthKeys`, so accepting a key and forwarding it are one edit rather than two that can disagree. `environmentAuthPresent` collapses the two identical loops. `TestEveryAcceptedEnvironmentCredentialCrossesTheBoundary` asserts the invariant by reading those slices rather than restating them, per the AGENTS rule that tests never re-encode configuration. A key added to either set has to cross the boundary or the test fails. ## Verified * `ward exec aos-test` green. * `ward exec pre-commit-all` green. * Behavior under `--auth=false` is unchanged: no credential key is forwarded, and non-secret harness tuning still is. ## Not in this PR While tracing this I hit a related question that is yours to answer rather than mine, so I left it alone: `--dry-run` currently resolves credentials and fails closed without them, which is why the release smoke has to pass `--auth=false` at four call sites and why #907 and #1000 were the same one-line patch twice. `docs/aos-codex-auth.md` documents that dry-run behavior deliberately ("Dry-run output contains only the auth path and mount shape"), so changing it is a contract decision, not a bug fix. Raised separately. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(release): keep the director dry-run smoke unauthenticated
All checks were successful
ci / aos-cli-tests (pull_request) Successful in 27s
ci / ward-doctor (pull_request) Successful in 11s
ci / gate (pull_request) Successful in 55s
b5139e99e8
`check-aos-release.sh` runs two role-default `aoscompose --dry-run` smokes.
The engineer one passes `--auth=false`. The director one, added in #931, did
not. The director launch profile defaults to `claude`, so that call resolves
Claude credentials on a runner that has none.

It stayed green until #974 made Claude auth fail closed. Before that, a
missing `~/.claude/.credentials.json` returned an empty projection and the
launch continued logged out. Since then the same call aborts:

    aoscompose: claude auth: file-backed credentials not found at
    /root/.claude/.credentials.json and host keyring projection is
    unsupported on this platform (use --auth=false for unauthenticated
    commands)

That has failed every `aos-cli-release` run since 2026-08-09 (runs 3130,
3202, 3213), so the AOS CLI release train has published nothing in three
days. This is the same fix #907 applied to the other dry-run smokes.

A dry-run prints a plan and launches nothing, so no smoke in this script
needs real credentials.

Verified with `ward exec pre-commit-all` (all hooks pass, shellcheck
included).

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
fix(aos): forward every Claude credential the auth check accepts
All checks were successful
ci / ward-doctor (pull_request) Successful in 22s
ci / aos-cli-tests (pull_request) Successful in 32s
ci / gate (pull_request) Successful in 53s
0c2a785441
`docs/aos-claude-auth.md` says `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`,
and `CLAUDE_CODE_OAUTH_TOKEN` "cross the container boundary by name when
present, so nothing is projected". Only the first actually crossed.

`claudeEnvironmentAuthPresent` accepted all three and returned an empty
projection, and `forwardedEnvironment` forwarded one. A host holding only
`ANTHROPIC_AUTH_TOKEN` or `CLAUDE_CODE_OAUTH_TOKEN` therefore passed the auth
gate, projected nothing, forwarded nothing, and started the container logged
out. That is #968's symptom reached through a different door.

The Codex side had it right by luck rather than structure: its check-set and
its forward-set were two literals that happened to agree.

Rather than add the two missing keys to the second list, this removes the
second list. Both sets now read `claudeEnvironmentAuthKeys` and
`codexEnvironmentAuthKeys`, so accepting a key and forwarding it are one
edit. `TestEveryAcceptedEnvironmentCredentialCrossesTheBoundary` asserts the
invariant off those slices instead of restating them, so a key added to
either set has to cross or the test fails.

Verified with `ward exec aos-test` and `ward exec pre-commit-all`.

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
coilysiren deleted branch aos/claude/claude-env-credentials-cross 2026-08-12 07:24:59 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os!1001
No description provided.