Skip to content

Register optional Anthropic Admin API credential metadata - #850

Open
seathatflowsinourveins wants to merge 2 commits into
mainfrom
codex/ns2604-api-credit-sota-20261008
Open

seathatflowsinourveins wants to merge 2 commits into
mainfrom
codex/ns2604-api-credit-sota-20261008

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Scope

Register the optional anthropic-admin inventory id so the owner can later store a Claude Console Admin API key with the existing hidden-input setter. The row declares only the foundation lane, existing provider-key class, private file path template and ANTHROPIC_ADMIN_KEY; the runbook cites the official key type and variable name. The existing credential tests classify the new id, the secret-name guard protects its variable, and current file hashes/pins are registered. The Admin runbook section is appended after existing text so established source-line citations remain valid.

  • Base commit: 28af9b9321d0492f6cdabe75091376560e7cdc4d.
  • Lane: lane:foundation, matching the PR label.
  • Owned paths touched: adoption/credential-inventory.json, docs/secret-storage.md, tests/test_credential_run.py, scripts/hooks/secret_path_guard.py, adoption/hooks/claude/SHA256SUMS, and derived registrations in manifests/evidence.json.

SOTA sources

Evidence-class table

Claim Evidence class Command / receipt
Console Admin key type, privilege and variable name source_review Official Anthropic sources above; references retained in the inventory and runbook
Existing inventory validator and metadata-only setter accept the id local_integration credential_status.inventory_errors(...) returned no errors; set_credential.load_entry("anthropic-admin", env={}) accepted the metadata without invoking the writer
Credential fixture suites accept the registered row local_integration 170 core tests plus 21 boot-receipt tests passed; final guard/citation/credential regression rerun recorded below; all credential stores are temporary synthetic fixtures
Published metadata and file registrations are valid native_proven python3 scripts/validate.py rc0; 69 components, 10,820 hashed files, 4 profiles, 226 receipts
No key value, live Admin API client, runtime install or broker-path change is introduced source_review Initial metadata diff and scoped guard/citation repair reviewed; no credential store or authenticated Admin API request was accessed

Local commands run

python3 -m unittest -q tests.test_credential_status tests.test_credential_tools tests.test_credential_run
rc=0; Ran 170 tests in 72.622s; OK

python3 -m unittest -q tests.test_credential_boot_receipt
rc=0; Ran 21 tests in 3.649s; OK

python3 -c 'import sys; from pathlib import Path; sys.path.insert(0, "scripts"); import host_receipts; [host_receipts.register_file(Path("."), p) for p in sys.argv[1:]]' adoption/credential-inventory.json docs/secret-storage.md tests/test_credential_run.py scripts/hooks/secret_path_guard.py adoption/hooks/claude/SHA256SUMS
rc=0; existing repository registration method

python3 scripts/validate.py
rc=0; status=passed; components=69; hashed_files=10820; profiles=4; receipts=226
Integrity and scope checks only; no live provider or GPU execution.

git diff --check
rc=0

Scoped regression checks

The existing guard now covers ANTHROPIC_ADMIN_KEY on the same tuple line as ANTHROPIC_API_KEY, preserving guard source citations. Its current SHA256SUMS entry matches the source; historical guard receipts retain their historical pins. The runbook's original 2,525 lines remain an unchanged prefix, so existing disposition citations still point to their keys.

Final command: CI=true python3 -m unittest -q tests.test_secret_path_guard tests.test_upstream_surface_watch tests.test_credential_status tests.test_credential_tools tests.test_credential_run tests.test_credential_boot_receipt — rc0; 395 tests in 153.711s, 9 skips. The guard/citation/credential suite passes in its native Actions mode. A normal local run reported only test_host_profile_copy_is_verbatim: the installed host guard differs from the revised repository source. The current host guard is not deployed/updated by this PR work, and host parity remains unverified; the native CI mode skips that deployment assertion. Existing credential fixtures do not read a real store.

Decision record

No new architectural decision: this follows the existing secret-storage decision and CC 080508Z row 9. The owner provisions/stores the key later through tools/credentials/set_credential.py anthropic-admin. Admin API connectivity, valid credentials and SDK automatic environment discovery are UNVERIFIED.

Host evidence

Not applicable; this PR adds no host receipt or platform-status claim.

Checklist

  • No GitHub Actions changed; existing SHA pinning and permissions are unaffected.
  • No secrets printed, logged or committed; the new row is optional and the owner stores its key later.
  • No paid hosting, subscription or billing surface introduced.
  • Peer-owned files and worktrees preserved.
  • python3 scripts/validate.py and the credential tests pass.

This foundation metadata change is off the paper path and complies with the CC freeze/quiet window: it performs no runtime installation, runtime-worker wave, gateway restart or Alpaca key use.

@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Oct 8, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-08T08:40:04.321470Z 2b69f8c PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2b69f8c762

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

"kind": "private_env_file",
"path_template": "${XDG_CONFIG_HOME:-$HOME/.config}/native-agent-stack/anthropic-admin.env"
},
"variables": ["ANTHROPIC_ADMIN_KEY"],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the Admin variable to the secret guard

Once an owner stores this full-access Admin key and runs a command through credential_run.py, the Claude command guard does not recognize ANTHROPIC_ADMIN_KEY: guard.check("echo $ANTHROPIC_ADMIN_KEY") and Python environment lookups both return no refusal, allowing commands to access or transform the key beyond the output masker's coverage. This also makes tests.test_secret_path_guard.SecretPathGuardTests.test_inventory_secret_names_are_all_guarded fail because the new inventory variable is absent from scripts/hooks/secret_path_guard.py::SECRET_NAMES; update the guard and its associated registrations alongside the inventory entry.

Useful? React with 👍 / 👎.

seathatflowsinourveins added a commit that referenced this pull request Oct 8, 2026
### Scope

- What this PR changes: it adds the optional `anthropic-api-2` entry directly after `anthropic-api` in `adoption/credential-inventory.json`, so one command can select an additional Console key. It is an additional key: nothing is replaced. The setter, the runner and the guard are unchanged: the setter and the runner read the inventory, and the guard's rules already cover the variable and the whole store directory. Both entries declare only `ANTHROPIC_API_KEY`, which stays in `must_not_be_set`.
- Base commit: `e48de2e6fa1dd3b740af4793ea5824ea35476dba`
- Lane: `lane:foundation`
- Owned paths touched: `adoption/credential-inventory.json`, `docs/secret-storage.md`, `docs/decisions/2026-10-08-anthropic-api-second-key.md`, `docs/harness-defaults.md`, `tests/test_credential_run.py`, `catalogs/foundation/upstream-surface-dispositions.json`, and `manifests/evidence.json` (the hashes of those six files, written by `scripts/host_receipts.py:register_file`).

The new entry differs from the first in exactly six fields: `id`, `label`, `store.path_template` (file `anthropic-api-2.env` in the same directory), `loaders`, `rotation` and `notes`. Class, status, lane, variables, optional and pointer variables, consumers and store kind are identical. The repository precedent is `alpaca-paper-2` (#481, `c26800f3`).

**Scope beyond the brief.** Four edits go beyond adding the entry. They are declared here so that the read of this PR covers them:

1. **One sentence of the first entry's `notes` is corrected.** Old: "exported in a shell it would override every Claude Code session's native sign-in". New: "a shell export can switch Claude Code from subscription sign-in to API billing (headless mode uses the key when present; interactive mode first asks for approval)". Source: Claude Code's [environment variables](https://code.claude.com/docs/en/env-vars#variables) page, where a present key is always used in non-interactive mode (`-p`) and is approved once in interactive mode before it overrides the subscription. The new entry gives the same reason, so the two entries agree. No other field of `anthropic-api` changes.
2. **One row is added to the anti-pattern log of `docs/harness-defaults.md`.** It records that correction, which that page's "Record a correction the same turn" rule asks for.
3. **`docs/secret-storage.md` gains the first key's table row as well.** The entry table had no `anthropic-api` row. It now lists both entries, followed by one paragraph on selecting a key for one command.
4. **Five line citations into `docs/secret-storage.md` are updated in `catalogs/foundation/upstream-surface-dispositions.json`,** because the added rows moved the cited lines down by 18. The `source` locators of `CLAUDE_CODE_ENABLE_TELEMETRY` (1697 → 1715), `OTEL_LOG_USER_PROMPTS` and `OTEL_LOG_ASSISTANT_RESPONSES` (1684 → 1702), and `OTEL_LOG_TOOL_CONTENT` and `OTEL_LOG_RAW_API_BODIES` (1686 → 1704) change; no disposition or reason does. Hosted `validate-shard-3` failed five subtests of `DispositionCitationTests.test_every_cited_line_names_the_key` at `88d981c7`; #858 made the same repair for its own insertion.

### SOTA sources

Public primary documentation, fetched 2026-10-08; every cited anchor resolves.

- Anthropic [API overview, Getting API keys](https://platform.claude.com/docs/en/api/overview#getting-api-keys) and [Authentication, Create and use a key](https://platform.claude.com/docs/en/manage-claude/authentication#create-and-use-a-key): keys are created on the Console key page, the client SDKs pick up `ANTHROPIC_API_KEY` automatically, and a key scoped to one workspace needs no workspace header.
- Anthropic [Get your API key, Choose a key type](https://platform.claude.com/docs/en/get-api-key#choose-a-key-type), [Workspaces, API keys and resource scoping](https://platform.claude.com/docs/en/manage-claude/workspaces#api-keys-and-resource-scoping) and [Set workspace limits](https://platform.claude.com/docs/en/manage-claude/workspaces#set-workspace-limits): personal, service-account and legacy workspace keys; a multi-workspace key needs the `anthropic-workspace-id` header; spend limits are set per workspace.
- Claude Code [environment variables](https://code.claude.com/docs/en/env-vars#variables) and [authentication precedence](https://code.claude.com/docs/en/authentication#authentication-precedence): in non-interactive mode (`-p`) a present key is always used; in interactive mode the key is approved once before it overrides the subscription. These support the corrected wording.
- Anthropic [WIF reference, Profile configuration file](https://platform.claude.com/docs/en/manage-claude/wif-reference#profile-configuration-file): the configuration-profile alternative that the decision record names.
- [This repository at `c9ab2212142398234b6ba39a4cf33c5c2a6a643e`](https://github.com/seathatflowsinourveins/native-agent-stack/tree/c9ab2212142398234b6ba39a4cf33c5c2a6a643e), the reference implementation this change follows; the cited files are unchanged at the base commit: inventory entries `anthropic-api` (#841, `a35369aa`) and `alpaca-paper-2`; `tools/credentials/set_credential.py:load_entry`; `tools/credentials/credential_run.py:find_entry` and `injectable`; the guard's `SECRET_NAMES`, `STORE_PATHS` and `ENV_FILE_WORD` in `scripts/hooks/secret_path_guard.py`; the `RunnerCase` fixtures in `tests/test_credential_run.py`; `scripts/host_receipts.py:register_file` and `scripts/validate.py`. No credential runtime or dependency is added.

### Evidence-class table

| Claim | Evidence class | Command / receipt |
| --- | --- | --- |
| The Console key and environment contract, and the mode-dependent sign-in wording | source_review | The official pages linked above, fetched 2026-10-08 |
| The new entry differs from the first in six fields only; the inventory goes from 20 to 21 entries and from 10 to 11 injectable ids; of `anthropic-api`, only `notes` changes | source_review | Field-by-field comparison of both entries against `origin/main`, using the runner's own `injectable()` |
| The setter and the runner select separate files, leave the first file unchanged when the second is written, accept the same bare and quoted grammar, inject only the selected value and refuse expansion | local_integration; synthetic | `InjectionTests.test_second_anthropic_key_uses_the_same_setter_and_runner_grammar`: temporary store, generated fake values |
| The guard needs no edit: it lists no ids, its store rule covers the whole store directory, and the variable is already a guarded name | source_review; synthetic | `STORE_PATHS` and `SECRET_NAMES` in the guard; `tests.test_secret_path_guard`; `guard.read_command` on nine synthetic command forms returns the same reason for both ids (four store-path forms `credential_store_path`, the documented runner form allowed) |
| No leak in the six files of the first commit | local_integration | `betterleaks dir` 1.9.0 with `.gitleaks.toml` on a copy of those six files: no leaks, rc 0. The required gate is CI's pinned gitleaks 8.30.1, which is not installed on this host; hosted `secret-scan` passed at `88d981c7` |
| Each of the five re-pointed catalog locators names its key again, and no other maintained citation moved | local_integration; source_review | `tests.test_upstream_surface_watch`: 5 of 128 repository citations failed with the catalog at `88d981c7`, 0 fail now. Of 263 line citations into the four edited files, 126 point past the inserted lines: these 5; 46 pinned to a commit; 75 in dated records, frozen evidence or code comments that already pointed elsewhere before this PR or carry their own source revision |

Not measured here: the actual key type, the workspace spend limit, credit, fast mode and provider acceptance. A multi-workspace key needs the `anthropic-workspace-id` header and is outside this single-variable entry.

### Every changed existing test expectation

One constant changes, and two existing tests assert on it. The exact injectable set was `alpaca-paper`, `alpaca-paper-2`, `sec-contact`, `databento`, `typesafe`, `omniroute`, `tavily`, `claude-oauth-token`, `canary-e2e` and `anthropic-api`. `INJECTABLE_IDS` now adds only `anthropic-api-2`.

| Existing test | Old contract → new contract |
| --- | --- |
| `InjectionTests.test_only_the_selected_entrys_own_pointer_variables_stay_in_the_child` | Exactly the ten ids above are injectable, and each keeps only its own pointer variables → the same contract for eleven ids, adding `anthropic-api-2`, whose own pointer list is empty. |
| `InventoryAndGrammarTests.test_every_injected_name_is_masked_or_public` | Exactly those ten ids classify every injected variable as masked or public → eleven ids, adding the masked required variable of `anthropic-api-2`. The exact optional-variable classification is unchanged, because the new entry has none. |

No other existing assertion is edited. `NOT_INJECTABLE_IDS` is unchanged. The status module checks the canonical inventory dynamically and by subset. The boot-receipt allowlist test compares the receipt's rows with the canonical inventory, so its expected id sequence follows the inventory (20 → 21 rows; file-kind rows 18 → 19). The guard's tie tests read the inventory's variable and pointer names, which do not change. Fixtures are unchanged.

`InjectionTests.test_second_anthropic_key_uses_the_same_setter_and_runner_grammar` is new coverage, not a relaxed assertion.

### Local commands run

One module per command (no suite discovery). The twelve modules below ran on the tree committed as `5117734b`, the first commit on `e48de2e6`. The second commit, `88d981c7`, only removes two process claims from the decision record that nothing in the repository can verify; the docs module and the validator were run again on its tree. The third commit, `75fa64b3`, re-points five catalog citations; the last block covers it and the whole suite.

```
$ timeout 600 nice -n 10 ionice -c2 -n7 python3 -m unittest tests.<module> -v
tests.test_credential_status            rc 0   Ran 48    OK
tests.test_credential_tools             rc 0   Ran 34    OK
tests.test_credential_run               rc 0   Ran 94    OK
tests.test_credential_boot_receipt      rc 0   Ran 21    OK
tests.test_secret_path_guard            rc 0   Ran 90    OK (skipped=2: scratch adapter supplied by the permanent-test mutation driver)
tests.test_canary_proof                 rc 0   Ran 143   OK (skipped=134: reviewed ripgrep unavailable on this host)
tests.test_codex_worker_lane            rc 0   Ran 123   OK (skipped=12: real app-server runs need NAS_CODEX_INTEGRATION=1)
tests.test_host_requests                rc 0   Ran 49    OK
tests.test_new_wsl_definitive_defaults  rc 0   Ran 130   OK (skipped=3: GNU chmod --reference and readlink -f commands)
tests.test_adoption_docs_consistency    rc 0   Ran 39    OK (skipped=1: no profile's coverage differs between the pinned release and HEAD)
tests.test_sota_convergence             rc 0   Ran 157   OK
tests.test_ecosystem_manifest           rc 0   Ran 82    OK
$ python3 scripts/evidence_manifest.py --check
rc 0   {"files": 10901, "status": "passed"}
$ timeout 900 nice -n 10 ionice -c2 -n7 python3 scripts/validate.py
rc 0   {"components": 70, "hashed_files": 10901, "profiles": 4, "receipts": 239, "status": "passed"}
```

On the tree committed as `88d981c7`:

```
$ timeout 600 nice -n 10 ionice -c2 -n7 python3 -m unittest tests.test_adoption_docs_consistency -v
rc 0   Ran 39    OK (skipped=1)
$ timeout 900 nice -n 10 ionice -c2 -n7 python3 scripts/validate.py
rc 0   {"components": 70, "hashed_files": 10901, "profiles": 4, "receipts": 239, "status": "passed"}
```

Hosted validate at `88d981c7` (run 37846469750) failed one shard: `validate-shard-3`, `Ran 1514 tests`, `FAILED (failures=5, skipped=127)`. All five failures were the stale catalog citations. The third commit, `75fa64b3`, fixes them. On its tree:

```
$ timeout 600 python3 -m unittest tests.test_upstream_surface_watch -v
rc 0   Ran 114   OK (skipped=6)
$ timeout 600 python3 -m unittest tests.test_adoption_docs_consistency tests.test_credential_run
rc 0   Ran 133   OK (skipped=1)
$ timeout 900 python3 scripts/validate.py
rc 0   {"components": 70, "hashed_files": 10901, "profiles": 4, "receipts": 239, "status": "passed"}
$ for m in tests/test_*.py: timeout 600 python3 -m unittest tests.$m   (each module on its own, no discovery)
276 modules: 255 rc 0, 21 rc 1; 10,725 tests ran, 911 skipped
```

The 21 nonzero modules all passed in hosted validate at `88d981c7`. Locally they fail for host reasons, not because of this diff:

- **18 modules: `exchange_calendars` is not installed** in this host's Python 3.13. Hosted installs 4.13.2 from `.github/requirements-calendar.txt`. Sixteen modules do not import: the 13 `test_adaptive_paper_*` modules other than credential_race, plus `test_alpaca_admission_regressions`, `test_native_faults_min` and `test_order_throughput`. `test_ingest_snapshot` has 2 errors. `test_adaptive_paper_credential_race` has 5 mutation self-test failures, because its pristine run imports the runner module. All 18 fail the same way on a checkout of the base `e48de2e6`.
- **`test_token_e2e_grader`:** `git clone --local` from the worktree (on disk) into `/tmp` (tmpfs) fails with `Invalid cross-device link`. It passes on the base checkout, which lives on tmpfs.
- **`test_windows_terminal_defaults`:** the Claude Code client installed on this host reports a `plugin_notification` notification type that has no repository decision. The base gives the same failure.
- **`test_new_wsl_mcp_conformance_lifecycle`:** host-state dependent. Three runs at this head gave a listener assertion, a missing `cleanup.json`, then a pass; it passes on the base.

The fourth commit, `67d9e8d5`, corrected a stale comment citation in `blueprints/runtime-workers/openhands/resolver/patch_policy.py`. That citation was already wrong on main, so it is outside this PR's scope and will be handled separately. The fifth commit, `2fa607e1`, reverts it. The head's tree is identical to `75fa64b3`'s (tree `d399373a`). On it, `timeout 900 python3 scripts/validate.py` returns rc 0.

### Decision record

`docs/decisions/2026-10-08-anthropic-api-second-key.md`: the decision, the handling rules, the sources, the `alpaca-paper-2` precedent, the correction, the evidence boundaries, the alternatives and what would reopen the decision, and the inverse.

### Host evidence

Not applicable: this PR adds or changes no file under `evidence/hosts/`.

### Checklist

- [x] New/changed GitHub Actions are pinned to a full commit SHA with a version comment (none changed).
- [x] New/changed workflows declare least-privilege permissions (none changed).
- [x] No secrets are printed, logged or committed; no new required secret was added. No credential value or credential store was read, inspected, created or changed for this PR: the tests use a temporary store and generated fake values, and the setter, launcher and runner were not run against a real entry.
- [x] No new paid hosting, subscription or billing surface is introduced by this declaration. The key itself is supplied later through the existing hidden prompt; its credit and spend limit are not measured here.
- [x] Peer-owned untracked files and worktrees were preserved.

### Landing notes

- This PR declares a store; it neither creates one nor opens the paste window.
- #850 (open) inserts `anthropic-admin` at the same position in the inventory and edits the same `INJECTABLE_IDS` line; #769 and #770 (drafts) also touch the inventory, `docs/secret-storage.md` and `tests/test_credential_run.py`. Whichever lands second needs a rebase and a fresh `register_file` pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant