Skip to content

Claude: consented direct keychain read + CLI usage fallback (#2634) - #2675

Merged
steipete merged 16 commits into
mainfrom
fix/2634-claude-consented-read
Aug 9, 2026
Merged

Claude: consented direct keychain read + CLI usage fallback (#2634)#2675
steipete merged 16 commits into
mainfrom
fix/2634-claude-consented-read

Conversation

@steipete

@steipete steipete commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the maintainer decision on #2634 (both paths): a consented direct keychain read as the primary recovery for Claude usage on 0.47+, and a CLI usage-probe fallback for users who decline. Root cause context: e17ba24bd hard-disabled keychainAccessAllowed in release, which silently blocked the direct read, the freshness sync, AND the delegated-refresh success verification — making .attemptedSucceeded structurally unreachable (analysis on the issue).

Consent gate (primary)

  • New durable, default-OFF flag (claudeOAuthDirectKeychainReadAllowed) resolved through the same defaults-domain path as the existing prompt-mode preference, so app/CLI/helpers agree. No migration, no silent re-enable on upgrade.
  • keychainAccessAllowed (the exact line e17ba24bd changed) now resolves gate + consent, and remains the single choke point: direct read (both readers), freshness sync, and fingerprint verification open and close together. The existing prompt-policy setting layers on top unchanged.
  • UI: a toggle on the Claude settings card, plus a call-to-action on the provider card when the Report unreadable Claude OAuth refresh as terminal #2650 terminal unreadable state is hit with consent off. The terminal error copy now explains why and names both recovery paths.

CLI fallback (degraded, honest)

No new scraper — reuses the existing hardened PTY /usage probe (ClaudeStatusProbe: shape validation, bounded timeouts, rate-limit gate, background-launch gating). The terminal unreadable state is now a typed error, so explicit-OAuth falls through to the CLI step; scraped snapshots carry dataConfidence: .percentOnly and the card says "Usage via Claude CLI (limited detail)". Fail-soft preserved; background refresh keeps all existing no-PTY gates and cooldowns.

Testing

  • New ClaudeOAuthDirectKeychainReadConsentTests (9): choke-point flips with consent, kill-switch wins over consent, default-off / no-consent-no-read invariants, typed-error carve-outs, fallback routing per runtime/source-mode, .percentOnly propagation.
  • Pre-existing ClaudeCredentialOwnershipBoundaryTests untouched and passing — now proving the default posture.
  • Full sharded make test exit 0; make check 0 violations. Codex autoreview (gpt-5.6-sol, high): clean, no findings, "patch is correct (0.98)".
  • (Known pre-existing parallel flakes in PromptCoalescing/ProfileIsolation reproduce identically on the unmodified baseline.)

Live verification still needed (maintainer machine)

  1. Opt-in → Refresh → one macOS permission prompt, OAuth usage returns, .attemptedSucceeded observable in logs.
  2. Consent off → Refresh → CLI PTY fallback populates the card with the "via Claude CLI" note.
  3. No prompt storms across a Claude Code token rotation (the ACL-rewrite scenario that motivated e17ba24bd).

Open judgment calls (flagged, defaults chosen)

  1. Degraded marker is .percentOnly (PTY exposes rendered percentages) rather than .estimated.
  2. If the CLI fallback itself fails, the card shows the CLI error, not the original terminal-state message (both recorded in the attempts list).
  3. A consent-off user refresh launches the CLI twice (delegated touch, then usage probe) — deliberate, since on older Claude Code the touch can still create the credentials file; both launches are bounded.

Fixes #2634.

@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: ec037bee64

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +622 to +623
self.defaultsState.claudeOAuthDirectKeychainReadAllowed = newValue
self.userDefaults.set(newValue, forKey: ClaudeOAuthDirectKeychainReadConsent.userDefaultsKey)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Invalidate cached Claude credentials on opt-out

When a user turns this setting back off after a successful OAuth read, the setter only flips the defaults flag. ClaudeOAuthCredentialsStore.loadRecord still returns valid in-memory or CodexBar keychain-cache entries before the keychainAccessAllowed-guarded freshness sync, so Claude OAuth usage can continue using the copied Claude Code token until the cache expires instead of immediately falling back to the CLI. Clear/invalidate the Claude OAuth credential cache when newValue is false.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P1 Urgent regression or broken agent/channel workflow affecting real users now. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. labels Aug 5, 2026
@clawsweeper

clawsweeper Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed August 9, 2026, 12:53 AM ET / 04:53 UTC.

ClawSweeper review

What this changes

Adds a default-off consent setting for direct Claude Code Keychain reads and falls back to reduced-detail Claude CLI usage when OAuth credentials are unreadable.

Regression provenance

Possible regression — probable (reproduction; reviewed change). No predecessor PR is attributed.

Merge readiness

⚠️ Ready for maintainer review - 2 items remain

Keep open: the owner-authored PR now addresses the prior all-profile cache-revocation blocker, but remains pending normal owner validation of its intentional Keychain-access boundary.

Priority: P1
Reviewed head: 59d59e405ad34f82c3dfe33a787d7d5921822d08

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) The current implementation is well-scoped and resolves the prior cache finding; remaining work is release-path validation rather than a patch defect.
Proof confidence 🌊 off-meta tidepool Not applicable: This owner-authored PR is not subject to the external-contributor proof gate, though its stated signed-app checks remain useful release validation.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: This owner-authored PR is not subject to the external-contributor proof gate, though its stated signed-app checks remain useful release validation.
Evidence reviewed 3 items Prior blocker resolved: The current head adds a shared revocation epoch, clears the active cache, and rejects older Claude CLI-owned profile and legacy cache entries before they can be reused or migrated.
Cross-profile regression coverage: The added test seeds two independent Claude configuration-profile caches, revokes consent, and verifies neither cache remains usable or stored.
Current main does not contain the PR head: The PR head is not an ancestor of current main and has a non-empty three-way diff, so this remains an active implementation rather than an implemented-on-main cleanup.
Findings None None.
Security None None.

How this fits together

CodexBar’s Claude provider obtains usage through OAuth credentials or the installed Claude CLI, then renders a usage snapshot in the menu bar. This change gates foreign-Keychain reads and changes the credential-cache and fallback path used before usage is fetched.

flowchart LR
    A[Claude refresh] --> B[Consent and prompt policy]
    B -->|Consent granted| C[Claude Code Keychain]
    B -->|Not consented or unreadable| D[Claude CLI usage probe]
    C --> E[Credential cache]
    E --> F[OAuth usage request]
    D --> G[Reduced-detail usage snapshot]
    F --> H[Claude menu card]
    G --> H
Loading

Before merge

  • Resolve merge risk (P2) - The consented foreign-Keychain read can reintroduce macOS permission-prompt behavior; the PR body identifies real signed-app checks for consent-on recovery, consent-off CLI fallback, and token rotation that should be completed before release.
  • Complete next step (P2) - No mechanical repair remains; this owner-authored PR needs ordinary merge and release-path validation.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Changed surface 17 files, +700/-42 lines The PR spans persisted settings, credential caching, provider routing, UI messaging, and focused tests.
Consent regression suite 316 added test lines The suite covers the default-off gate, fallback routing, fidelity marker, and cross-profile cache revocation.

Root-cause cluster

Relationship: fixed_by_candidate
Canonical: #2634
Summary: This PR is the active candidate fix for the Claude OAuth recovery regression tracked by the canonical issue.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge-risk options

Maintainer options:

  1. Validate the release path (recommended)
    Run the three redacted signed-app checks described in the PR to confirm predictable prompts, CLI fallback, and token-rotation behavior.
  2. Accept the remaining runtime uncertainty
    Merge based on the focused coverage while accepting that live Keychain ACL behavior is not captured for this head.

Technical review

Best possible solution:

Keep the default-off consent boundary and cross-profile revocation, then validate the documented signed-app scenarios before release.

Do we have a high-confidence way to reproduce the issue?

Yes for the source-level cache and fallback behavior: focused tests cover both profiles and routing, while the macOS Keychain prompt path requires the signed-app validation described in the PR.

Is this the best way to solve the issue?

Yes: the explicit default-off consent plus CLI fallback is a bounded recovery path, and the current head now prevents pre-revocation caches from surviving a profile switch.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 81ca9f66338c.

Labels

Label changes:

  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: This owner-authored PR is not subject to the external-contributor proof gate, though its stated signed-app checks remain useful release validation.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: This repairs Claude usage recovery for an affected released OAuth/Keychain workflow.
  • merge-risk: 🚨 compatibility: The persisted consent flag and cache invalidation change existing Claude-profile recovery behavior.
  • merge-risk: 🚨 auth-provider: The patch changes Claude OAuth credential retrieval, cache reuse, and CLI fallback routing.
  • merge-risk: 🚨 security-boundary: The patch enables a consent-controlled read of a Keychain item owned by Claude Code.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: This owner-authored PR is not subject to the external-contributor proof gate, though its stated signed-app checks remain useful release validation.

Evidence

What I checked:

Likely related people:

  • steipete: Authored and integrated the consent, settings, cache-revocation, and provider-routing work on this branch. (role: feature owner; confidence: high; commits: ec037bee64be, 8ba7af523d50, 59d59e405ad3; files: Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift, Sources/CodexBar/SettingsStore+Defaults.swift)
  • Avenox: Merged refresh-chain ownership work directly composes with the affected Claude OAuth cache ownership boundary. (role: adjacent contributor; confidence: high; commits: bdea8222ad0b, 89b7f8fd4aac; files: Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift, Tests/CodexBarTests/ClaudeOAuthRefreshChainOwnershipTests.swift)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Run the stated signed-app checks before release.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (5 earlier review cycles)
  • reviewed 2026-08-05T14:39:55.168Z sha ec037be :: needs changes before merge. :: [P1] Invalidate cached credentials when consent is revoked
  • reviewed 2026-08-06T23:25:41.339Z sha 8ba7af5 :: needs changes before merge. :: [P1] Invalidate every Claude profile cache on opt-out
  • reviewed 2026-08-08T21:40:38.456Z sha d0f400e :: needs changes before merge. :: [P1] Invalidate every Claude profile cache on opt-out
  • reviewed 2026-08-08T21:51:57.140Z sha a7b9087 :: found issues before merge. :: [P1] Invalidate every Claude profile cache on opt-out
  • reviewed 2026-08-09T01:18:37.307Z sha 22c7fc8 :: needs changes before merge. :: [P1] Invalidate all Claude profile caches on opt-out

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Aug 8, 2026
…nted-read

# Conflicts:
#	Sources/CodexBarCore/Providers/Claude/ClaudeOAuth/ClaudeOAuthCredentials.swift
#	Tests/CodexBarTests/ClaudeOAuthCredentialsStoreCLIStorageOwnershipTests.swift
#	Tests/CodexBarTests/ClaudeOAuthRefreshChainOwnershipTests.swift
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Aug 9, 2026
@steipete
steipete merged commit 0954a74 into main Aug 9, 2026
9 checks passed
luisgonzaleznf added a commit to luisgonzaleznf/CodexBar that referenced this pull request Aug 9, 2026
Addresses both P1 findings on the draft, now that steipete#2675 has landed.

The feed carries only the 5h/7d windows. Publishing its result whole blanked
identity, plan, model-scoped weekly, Daily Routines, extra usage and cost —
the opposite of the "composes with, never replaces" constraint in the owner
ruling. Its windows are now merged over the last polled Claude snapshot, and a
window the observation omits means "no update" rather than "cleared".

Composition also carries the account guard the feed cannot supply itself.
Matching CLAUDE_CONFIG_DIR cannot distinguish an account switch within one
profile, so an observation is discarded outright when the active account has
changed since the snapshot it would compose over. Publishing it there would
render one account's windows beneath another account's identity.

Follows the existing shape of this apply path, which already reconciles
results against previous state for Codex, CommandCode, DeepSeek and reset
backfill. The helper lives in its own file and the call site replaces an
existing binding, so the refresh function stays within its length budget.

The card note keeps precedence over the dataConfidence note added by steipete#2675: a
composed snapshot inherits its confidence from the previous poll, so a prior
CLI scrape would otherwise label live statusLine windows as CLI-sourced.
luisgonzaleznf added a commit to luisgonzaleznf/CodexBar that referenced this pull request Aug 12, 2026
Addresses both P1 findings on the draft, now that steipete#2675 has landed.

The feed carries only the 5h/7d windows. Publishing its result whole blanked
identity, plan, model-scoped weekly, Daily Routines, extra usage and cost —
the opposite of the "composes with, never replaces" constraint in the owner
ruling. Its windows are now merged over the last polled Claude snapshot, and a
window the observation omits means "no update" rather than "cleared".

Composition also carries the account guard the feed cannot supply itself.
Matching CLAUDE_CONFIG_DIR cannot distinguish an account switch within one
profile, so an observation is discarded outright when the active account has
changed since the snapshot it would compose over. Publishing it there would
render one account's windows beneath another account's identity.

Follows the existing shape of this apply path, which already reconciles
results against previous state for Codex, CommandCode, DeepSeek and reset
backfill. The helper lives in its own file and the call site replaces an
existing binding, so the refresh function stays within its length budget.

The card note keeps precedence over the dataConfidence note added by steipete#2675: a
composed snapshot inherits its confidence from the previous poll, so a prior
CLI scrape would otherwise label live statusLine windows as CLI-sourced.
luisgonzaleznf added a commit to luisgonzaleznf/CodexBar that referenced this pull request Aug 13, 2026
Addresses both P1 findings on the draft, now that steipete#2675 has landed.

The feed carries only the 5h/7d windows. Publishing its result whole blanked
identity, plan, model-scoped weekly, Daily Routines, extra usage and cost —
the opposite of the "composes with, never replaces" constraint in the owner
ruling. Its windows are now merged over the last polled Claude snapshot, and a
window the observation omits means "no update" rather than "cleared".

Composition also carries the account guard the feed cannot supply itself.
Matching CLAUDE_CONFIG_DIR cannot distinguish an account switch within one
profile, so an observation is discarded outright when the active account has
changed since the snapshot it would compose over. Publishing it there would
render one account's windows beneath another account's identity.

Follows the existing shape of this apply path, which already reconciles
results against previous state for Codex, CommandCode, DeepSeek and reset
backfill. The helper lives in its own file and the call site replaces an
existing binding, so the refresh function stays within its length budget.

The card note keeps precedence over the dataConfidence note added by steipete#2675: a
composed snapshot inherits its confidence from the previous poll, so a prior
CLI scrape would otherwise label live statusLine windows as CLI-sourced.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. merge-risk: 🚨 security-boundary 🚨 Merging this PR could weaken sandboxing, authorization, credentials, or sensitive data. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude usage stops updating on 0.47.0; refresh fails with SessionError

2 participants