fix(wiki): headless drain uses Claude subscription by default, API key opt-in - #71
Merged
Merged
Conversation
…y opt-in The headless wiki-authoring drain used `--bare`, which per `claude --help` (CLI 2.1.197) forces "ANTHROPIC_API_KEY or apiKeyHelper (OAuth and keychain are never read)" — i.e. it billed the API and ignored a logged-in Max/Pro subscription. Worse, its fail-closed guard skipped the whole drain when no ANTHROPIC_API_KEY was set, so on a subscription-only machine the drain never ran at all. - Replace `--bare` with `--safe-mode`: same config isolation (no CLAUDE.md / skills / plugins / hooks / MCP / settings — security control B-1 intact) but it leaves OAuth/keychain readable, so the call runs on the subscription. - Remove the ANTHROPIC_API_KEY fail-closed guard. - New `_subprocess_env()` selects auth via `CORTEX_HEADLESS_AUTH` (default `subscription`): strip ANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKEN from the child env so the CLI uses the subscription; `api` passes them through so users who want API billing still can. - Pass the explicit `env=` to create_subprocess_exec (it inherits the parent env by default, where a stray API key would otherwise win and bill the API). Verified: `claude --print --safe-mode --tools Read,Glob,Grep` returns 0 with no API key in env (subscription path). Tests: 16 passed (added subscription + api-mode auth tests); consolidation suite 37 passed; ruff format + check clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au
This was referenced Jun 30, 2026
Merged
cdeust
added a commit
that referenced
this pull request
Jun 30, 2026
…forgetting (#73) Bumps version across pyproject/server/manifest/marketplace/plugin manifests and records the 3.25.0 CHANGELOG section covering #67–#72: - feat: active forgetting module (#69), safe headless drain (#70), full zetetic agent roster for wiki authoring (#72) - fix: Windows portability (#68), subscription-default billing (#71), variadic --add-dir prompt-swallow root cause (#72) - deps: pydantic-settings 2.14.0→2.14.2 security patch (#67) Claude-Session: https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The headless wiki-authoring drain (
_claude_invoke) billed the API and ignored a logged-in Claude subscription — and on a subscription-only machine it never ran at all. This makes the subscription the default and keeps API billing as an explicit opt-in.Root cause
--baredocuments (perclaude --help, CLI 2.1.197): "Anthropic auth is strictly ANTHROPIC_API_KEY or apiKeyHelper via --settings (OAuth and keychain are never read)". So--bareforces API billing. Its companion fail-closed guard (if not ANTHROPIC_API_KEY: skip) meant the whole drain was skipped when only a subscription was present.A second, subtler trap: even without
--bare, the CLI prefersANTHROPIC_API_KEYwhen it is in the env (billing the API).create_subprocess_execinherits the parent env by default, so a stray key would still win.Change
--bare→--safe-mode: same config isolation (no CLAUDE.md / skills / plugins / hooks / MCP / settings — security control B-1 intact), but--safe-modeleaves OAuth/keychain readable, so the call runs on the subscription.ANTHROPIC_API_KEYfail-closed guard._subprocess_env()selects auth viaCORTEX_HEADLESS_AUTH(defaultsubscription):subscription— stripANTHROPIC_API_KEY/ANTHROPIC_AUTH_TOKENfrom the child env → keychain/OAuth → no API charge.api— pass the parent env through →ANTHROPIC_API_KEYbills the API (explicit user opt-in).env=tocreate_subprocess_exec.wiki_maintenance.pyenv-knob docs updated. Note: under subscription,total_cost_usd(feedingCORTEX_HEADLESS_USD_BUDGET) is a notional estimate — a volume throttle, not real spend.Security
Control B-1 preserved:
--safe-modeblocks the malicious-settings (permissions.allow:["Bash"]) and malicious-hook vectors that--bareblocked. Control 1 (--tools Read,Glob,Grep) unchanged.Verification
claude --print --safe-mode --tools Read,Glob,Grep --output-format json→ returncode 0,result: "OK", no API key in env (subscription path confirmed).pytest tests_py/handlers/test_headless_authoring_throttle.py— 16 passed (added subscription + api-mode auth tests).pytest tests_py/handlers/test_consolidate.py test_consolidate_telemetry.py tests_py/core/test_wiki_coverage.py— 37 passed.ruff format --check .+ruff check .— clean (0.15.20, matches CI).🤖 Generated with Claude Code
https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au