Skip to content

fix(wiki): headless drain uses Claude subscription by default, API key opt-in - #71

Merged
cdeust merged 1 commit into
mainfrom
fix/headless-authoring-use-max-subscription
Jun 30, 2026
Merged

fix(wiki): headless drain uses Claude subscription by default, API key opt-in#71
cdeust merged 1 commit into
mainfrom
fix/headless-authoring-use-max-subscription

Conversation

@cdeust

@cdeust cdeust commented Jun 30, 2026

Copy link
Copy Markdown
Owner

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

--bare documents (per claude --help, CLI 2.1.197): "Anthropic auth is strictly ANTHROPIC_API_KEY or apiKeyHelper via --settings (OAuth and keychain are never read)". So --bare forces 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 prefers ANTHROPIC_API_KEY when it is in the env (billing the API). create_subprocess_exec inherits 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-mode leaves OAuth/keychain readable, so the call runs on the subscription.
  • Removed the ANTHROPIC_API_KEY fail-closed guard.
  • New _subprocess_env() selects auth via CORTEX_HEADLESS_AUTH (default subscription):
    • subscription — strip ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN from the child env → keychain/OAuth → no API charge.
    • api — pass the parent env through → ANTHROPIC_API_KEY bills the API (explicit user opt-in).
  • Pass an explicit env= to create_subprocess_exec.
  • Docstrings + wiki_maintenance.py env-knob docs updated. Note: under subscription, total_cost_usd (feeding CORTEX_HEADLESS_USD_BUDGET) is a notional estimate — a volume throttle, not real spend.

Security

Control B-1 preserved: --safe-mode blocks the malicious-settings (permissions.allow:["Bash"]) and malicious-hook vectors that --bare blocked. Control 1 (--tools Read,Glob,Grep) unchanged.

Verification

  • Live smoke test: 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.py16 passed (added subscription + api-mode auth tests).
  • pytest tests_py/handlers/test_consolidate.py test_consolidate_telemetry.py tests_py/core/test_wiki_coverage.py37 passed.
  • ruff format --check . + ruff check . — clean (0.15.20, matches CI).

🤖 Generated with Claude Code

https://claude.ai/code/session_019o58McF4LRfvGNNXaqG2Au

…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
@cdeust
cdeust merged commit 8d1a553 into main Jun 30, 2026
13 checks passed
@cdeust
cdeust deleted the fix/headless-authoring-use-max-subscription branch June 30, 2026 21:25
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant