# 09 — Claude account and window semantics (https://jackin.tailrocks.com/research/product/desktop/usage-provider-apis/09-claude-followups/)



## Summary [#summary]

Claude credentials live in Keychain by default on macOS, and OAuth profile plus usage endpoints provide identity and bounded quota metadata.

## Question and scope [#question-and-scope]

* **Q1** — On macOS, does Claude Code also write/maintain `~/.claude/.credentials.json` (or Keychain only)? What is the Keychain generic-password service name, corroborated from multiple public tools/issues? Decides whether a macOS Desktop app can obtain Claude credentials without Keychain access. - **Q2** — Public corroboration for payload fields chapter 03 held as single-source: `weekly_scoped`, `seven_day_cowork`, `seven_day_routines`, `amber_ladder`, `seven_day_oauth_apps`. Plus: does any public non-banned tool call `api.anthropic.com/api/oauth/profile`, and what does it return? - **Q3** — Window boundary semantics from primary/support sources: is the 5-hour session rolling or fixed-start ("session starts with your first message"?); is the weekly window a fixed reset time? Decides validity of a linear-from-window-start projection model. - **Q4** — Any public evidence whether `claude setup-token` long-lived tokens are accepted by the `oauth/usage` endpoint (chapter 03 open unknown, line 72)?

This chapter informs jackin-desktop.

## Method [#method]

**Verification cutoff:** 2026-07-24
&#x2A;*Method:** web (WebSearch/WebFetch + GitHub search API via `gh`, verified live 2026-07-24). Clean-room upheld: no CodexBar/OpenUsage source read; their issue titles cited only as concept evidence. No embedded-instruction/prompt-injection attempts observed in fetched pages (the code.claude.com docs page embeds a benign "fetch llms.txt index" navigation note only).

## Findings [#findings]

### Q1 — macOS Keychain vs file [#q1--macos-keychain-vs-file]

* Official storage split confirmed live: "On macOS, credentials are stored in the encrypted macOS Keychain. On Linux, credentials are stored in `~/.claude/.credentials.json` with file mode `0600`. On Windows, credentials are stored in `%USERPROFILE%\.claude\.credentials.json`" — [https://code.claude.com/docs/en/authentication](https://code.claude.com/docs/en/authentication) (confidence: HIGH)
* On macOS a fresh `/login` writes **only** to Keychain; `~/.claude/.credentials.json` "did not exist by default" — [https://github.com/anthropics/claude-code/issues/44028](https://github.com/anthropics/claude-code/issues/44028) (confidence: MED-HIGH — detailed user report in the official repo, closed as duplicate without maintainer contradiction)
* Worse than "not written": macOS Claude Code actively **deletes** the file. fs trace shows `node` calling `unlink /Users/<user>/.claude/.credentials.json`; reported for shared-volume macOS/Linux setups — [https://github.com/anthropics/claude-code/issues/1414](https://github.com/anthropics/claude-code/issues/1414) (has-repro label; fs-usage evidence) and duplicate lineage [https://github.com/anthropics/claude-code/issues/10039](https://github.com/anthropics/claude-code/issues/10039) (confidence: MED-HIGH)
* However, Claude Code on macOS **reads** `~/.claude/.credentials.json` as a fallback if it exists (e.g. over SSH where Keychain is inaccessible): "Claude Code has a file-based credential fallback (\~/.claude/.credentials.json) that works over SSH when the file exists" — [https://github.com/anthropics/claude-code/issues/44028](https://github.com/anthropics/claude-code/issues/44028), corroborated by [https://github.com/anthropics/claude-code/issues/29816](https://github.com/anthropics/claude-code/issues/29816) ("SSH sessions require re-login despite valid \~/.claude/.credentials.json") (confidence: MED)
* Keychain generic-password service name is &#x2A;*`Claude Code-credentials`**: the official-repo bug #9403 shows `security find-generic-password -s "Claude Code-credentials" -w` returning the credential JSON (`{"claudeAiOauth":{"accessToken":…,"refreshToken":…,"expiresAt":…,"scopes":…}}` — same shape as the Linux file, so one parser covers both) — [https://github.com/anthropics/claude-code/issues/9403](https://github.com/anthropics/claude-code/issues/9403) (confidence: HIGH)
* Mass independent corroboration of the service name: GitHub code search for `"Claude Code-credentials" find-generic-password` returns \~3,024 indexed files across independent tools (e.g. `matryer/xbar-plugins` clawdbar, `Z6543/claude-usage`, `TravisWheelerLab/claude-watch`, `kristianvalco/claude-usage-bar`, `torkay/ccpool`) — method: `gh api search/code`, 2026-07-24, CodexBar/OpenUsage excluded from query (confidence: HIGH)
* Custom `CLAUDE_CONFIG_DIR` accounts do **not** use the bare service. Claude Code derives `Claude Code-credentials-<suffix>`, where suffix is the first eight hexadecimal SHA-256 characters of the absolute config-directory path. jackin❯ already carries this derivation in <RepoFile path="crates/jackin-instance/src/auth.rs" /> and pins two live entries in `auth/tests.rs` (`~/.claude-chainargos` → `93aecf3d`, `~/.claude-work` → `3342f2c7`). This in-repo live evidence is authoritative for integration planning; the shared helper must be reused rather than reimplemented. (confidence: HIGH for the two observed accounts and current Claude Code scheme)
* Third-party Keychain reads can trigger macOS user-consent prompts (ACL requires interaction); an open feature request asks Anthropic to reduce this friction for usage tools via credential export or usage cache — [https://github.com/anthropics/claude-code/issues/22144](https://github.com/anthropics/claude-code/issues/22144) and [https://github.com/anthropics/claude-code/issues/44028](https://github.com/anthropics/claude-code/issues/44028) (confidence: MED-HIGH)
* Security.framework result classification matters: explicit user cancellation (`errSecUserCanceled`, -128) and authentication/ACL denial (`errSecAuthFailed`, -25293) are terminal denial for the queried service. `errSecInteractionNotAllowed` (-25308), common in SSH/headless contexts, proves only that UI cannot be shown; it must be treated as **missing/unavailable** so Claude Code's documented file fallback remains usable. Item-not-found (-25300) is also missing. (confidence: HIGH for OSStatus semantics; product mapping is the planning decision)
* **Decision answer**: on a default macOS install, a file-path-only reader (jackin❯ today: `crates/jackin-usage` reads only `~/.claude/.credentials.json`) gets **nothing** — the file is absent by default and deleted by macOS Claude Code even if materialized. jackin❯ desktop needs a Security.framework generic-password read for the service derived from the effective Claude config directory. macOS may present consent UI: "Always Allow" makes later reads silent, while "Allow" can prompt again; the app must serialize reads, remember explicit denial per service, and keep the blocking consent path off its main actor. (confidence: HIGH for storage/service/prompt requirement; platform consent-label behavior)

### Q2 — public corroboration for payload fields; oauth/profile [#q2--public-corroboration-for-payload-fields-oauthprofile]

All counts are GitHub code-search `total_count` (approximate, indexed files) on 2026-07-24 with `-repo:steipete/CodexBar -repo:steipete/OpenUsage`; the jackin❯ repository appears in results and is excluded from independence claims.

* `weekly_scoped` — \~840 files; independent decoders/fixtures include `tulinmola/claude-bar` (Swift), `soulduse/ai-token-monitor` (Rust), `lobehub/lobehub`, `joshuadavidthomas/vibeusage` (Go), `akitaonrails/ai-usagebar` (Rust) (confidence: MED-HIGH — many independent community observations, still no official doc)
* `seven_day_cowork` — \~850 files; e.g. `router-for-me/Cli-Proxy-API-Management-Center`, `kittors/CliRelay` (Go), `xiaolai/claudepot-app` (Rust), `onllm-dev/onWatch` (Go) (confidence: MED-HIGH)
* `seven_day_routines` — \~99 files; e.g. `tsouth89/ceiling` (rust provider), `zioder/TaskbarQuota` (C# provider), `lfmundim/UsageBar`, `zhengzizhe/conductor` (Swift) (confidence: MED — fewer, and several are spec/notes files rather than live captures; whether this key carries data on routines-active accounts remains open)
* `amber_ladder` — \~46 files; e.g. `LeenHawk/gproxy` (Rust), `CrocSwap/claude-meter` docs, `TopiCsarno/yapcap` fixture (confidence: MED)
* `seven_day_oauth_apps` — \~1,232 files; e.g. `musistudio/claude-code-router`, `basecamp/omarchy` (QML usage plugin), `Kuberwastaken/claurst` (Rust) (confidence: MED-HIGH)
* A full public captured `oauth/usage` response matching jackin❯ live fixture structure exists: `five_hour`/`seven_day` with `utilization` + `resets_at`, `limits[]` with `kind: session | weekly_all | weekly_scoped` (the `weekly_scoped` entry scoped `{model: {display_name: "Fable"}}`), `seven_day_cowork: null`, `seven_day_oauth_apps: null`, `amber_ladder: null`, codename keys (`tangelo`, `iguana_necktie`, `nimbus_quill`, `cinder_cove`, `omelette_promotional`), `spend` with `amount_minor`/`currency`/`exponent`, `extra_usage`, `member_dashboard_available` — [https://github.com/TopiCsarno/yapcap/blob/152ea67c3abd44776268627d58533003099da951/fixtures/claude/oauth\_usage\_response.json](https://github.com/TopiCsarno/yapcap/blob/152ea67c3abd44776268627d58533003099da951/fixtures/claude/oauth_usage_response.json) (confidence: MED-HIGH — independent capture agreeing field-for-field with jackin❯ in-repo live fixture; chapter 03's "single-source" caveats on `weekly_scoped`/`seven_day_cowork`/`amber_ladder`/`seven_day_oauth_apps` can be lifted to multi-source)
* `api.anthropic.com/api/oauth/profile` **exists and is widely used**: \~518 indexed files. Reputable non-banned callers: `dagger/dagger` defines `oauthProfileURL = "https://api.anthropic.com/api/oauth/profile"` alongside scopes `org:create_api_key user:profile user:inference` — [https://github.com/dagger/dagger/blob/main/internal/cmd/dagger/llmconfig/oauth.go](https://github.com/dagger/dagger/blob/main/internal/cmd/dagger/llmconfig/oauth.go); `elizaOS/eliza` calls it with `Authorization: Bearer` + `anthropic-beta: oauth-2025-04-20` and parses response shape `{account: {uuid, email | email_address}, organization: {uuid}}` — [https://github.com/elizaOS/eliza/blob/main/packages/auth/src/oauth-flow.ts](https://github.com/elizaOS/eliza/blob/main/packages/auth/src/oauth-flow.ts) (confidence: MED-HIGH for existence + account-email/uuid + organization-uuid; LOW/unknown for a plan label field)
* Official-repo corroboration of the endpoint plus its scope gate: `GET /api/oauth/profile` returns 403 "OAuth token does not meet scope requirement any\_of(user:profile, user:office)" for inference-only tokens; the same issue also names `GET /api/oauth/claude_cli/roles` (entitlements/roles) — [https://github.com/anthropics/claude-code/issues/79360](https://github.com/anthropics/claude-code/issues/79360) (confidence: HIGH for endpoint existence and scope requirement)
* The profile endpoint requires `user:profile` or `user:office` scope and returns account email, enabling jackin❯ email attribution without traffic sniffing.

### Q3 — window boundary semantics [#q3--window-boundary-semantics]

* The 5-hour session is a bounded window with a countdown, per live support docs: "&#x2A;*Current session:** How much of your plan's five-hour session limit you've used thus far, plus the amount of time remaining in the session" — [https://support.claude.com/en/articles/9797557-usage-limit-best-practices](https://support.claude.com/en/articles/9797557-usage-limit-best-practices) (confidence: HIGH)
* Session limit-reached messaging carries an explicit reset time: "5-hour limit reached - resets \[time]" and "5-hour limit resets \[time] - continuing with usage credits" — [https://support.claude.com/en/articles/12466728-troubleshoot-claude-error-messages](https://support.claude.com/en/articles/12466728-troubleshoot-claude-error-messages) (confidence: HIGH)
* "Session starts with your first message" (fixed-start, not rolling): this exact wording lived in the retired support article "Does Claude have any message limits?" (8602283) — the URL now 404s after the support.anthropic.com → support.claude.com migration, and no current live article restates the trigger; secondary echoes (e.g. [https://usagebar.com/blog/when-does-claude-code-usage-reset](https://usagebar.com/blog/when-does-claude-code-usage-reset), [https://pranoti.thesciencetalk.com/perspectives/claude-5-hour-session-limit-how-it-works/](https://pranoti.thesciencetalk.com/perspectives/claude-5-hour-session-limit-how-it-works/)) all quote the first-message-start model (confidence: MED — historical primary wording, currently only secondary-corroborated; no source anywhere describes a continuously-rolling 5-hour lookback)
* Weekly window is **fixed-reset, account-assigned**, stated live and unambiguously: "Weekly limits reset at a fixed time each week that is assigned to your account. Your reset day and time stay the same regardless of when you start using Claude or when your subscription begins, and you receive your full weekly allowance each cycle." — [https://support.claude.com/en/articles/11049741-what-is-the-max-plan](https://support.claude.com/en/articles/11049741-what-is-the-max-plan) (confidence: HIGH)
* Projection-model verdict: linear-from-window-start is **structurally valid** for both windows — each is a fixed interval with a known end (`resets_at` is provided per window in the `oauth/usage` payload itself, see yapcap fixture above), not a rolling lookback. For the session window the *start* is user-triggered (first message), so anchor projections on the server-supplied `resets_at` (start = `resets_at - 5h`) rather than on local first-request observations. Weekly `resets_at` gives the fixed weekly boundary directly. (confidence: HIGH for using `resets_at` as the anchor; MED for the session start=first-message assumption feeding any "elapsed fraction" denominator)

### Q4 — setup-token vs oauth/usage [#q4--setup-token-vs-oauthusage]

* **Rejected.** A detailed official-repo issue shows a `claude setup-token` credential (Max plan) failing the usage endpoint with a scope error: "`GET /api/oauth/usage` returns 403: 'OAuth token does not meet scope requirement user:profile'". Root cause quoted: "Setup-token tokens carry only the `user:inference` scope, and the entitlement endpoints reject that scope" — while `POST /v1/messages` with the same token succeeds — [https://github.com/anthropics/claude-code/issues/79360](https://github.com/anthropics/claude-code/issues/79360) (confidence: MED-HIGH — precise first-hand report with quoted response bodies in anthropics/claude-code; no maintainer contradiction; consistent with the official doc's "It can only make model requests" — [https://code.claude.com/docs/en/authentication](https://code.claude.com/docs/en/authentication))
* Scope contrast corroborated independently: interactive `/login`-style flows request `org:create_api_key user:profile user:inference` (dagger's OAuth constants — [https://github.com/dagger/dagger/blob/main/internal/cmd/dagger/llmconfig/oauth.go](https://github.com/dagger/dagger/blob/main/internal/cmd/dagger/llmconfig/oauth.go)), and `user:profile` is exactly the scope `oauth/usage` demands per #79360 (confidence: MED-HIGH)
* Concept-level corroboration from tool ecosystems (issue titles only, clean-room): CodexBar issue #1894 proposes a Claude usage source "reading anthropic-ratelimit-unified-\* headers (multi-account with setup-tokens / user:inference)" — i.e. header-scraping precisely because setup-tokens cannot use the usage endpoint — [https://github.com/steipete/CodexBar/issues/1894](https://github.com/steipete/CodexBar/issues/1894); similarly `Nanako0129/TokenBar` #27 "Claude usage limits for setup-token users via ratelimit headers" — [https://github.com/Nanako0129/TokenBar/issues/27](https://github.com/Nanako0129/TokenBar/issues/27) (confidence: MED, concept evidence)
* Chapter 03 line 72 open unknown → closed: `CLAUDE_CODE_OAUTH_TOKEN` setup-tokens are **not** a valid credential for jackin❯ Claude usage polling; only `/login`-minted OAuth credentials (file or Keychain) carry `user:profile`.

### Dead ends and contradictions [#dead-ends-and-contradictions]

* Issue #44028 states the Keychain item is "service 'Claude Safe Storage' with account 'Claude Key'" — contradicts #9403 and \~3k public tools using `Claude Code-credentials`. "Claude Safe Storage" is the Claude **Desktop** (Electron safeStorage) key name; the reporter almost certainly conflated the two products. Treat `Claude Code-credentials` as canonical for the CLI — [https://github.com/anthropics/claude-code/issues/44028](https://github.com/anthropics/claude-code/issues/44028) vs [https://github.com/anthropics/claude-code/issues/9403](https://github.com/anthropics/claude-code/issues/9403)
* \#9403 documents a transient v2.0.14 bug where the CLI wrote to `Claude Code-credentials` but read from service `Claude Code` — a version-specific read/write mismatch, not a second canonical name — [https://github.com/anthropics/claude-code/issues/9403](https://github.com/anthropics/claude-code/issues/9403)
* Weekly-reset contradiction from chapter 03 stands unresolved: support says fixed weekly reset (11049741), while an 11-day longitudinal poll observed `seven_day.utilization` refreshing every \~72h — [https://gist.github.com/monperrus/3ac4b303a84946bbeaf2b1123ee99491](https://gist.github.com/monperrus/3ac4b303a84946bbeaf2b1123ee99491). Continue treating `resets_at` as window-tail metadata, not a refill promise.
* Current support.claude.com articles do not document a session-start reset trigger. "How do usage and length limits work?" (11647753) contains no session/reset statement, while "Models, usage, and limits in Claude Code" (14552983) says only that Enterprise seats meter on a "rolling window" without specifying its duration.
* `opgginc/opencode-bar` docs/AI\_USAGE\_API\_REFERENCE.md contains no profile-endpoint section (checked via API) — dead end for profile-shape corroboration; eliza + dagger + #79360 used instead.

## Implications for jackin❯ [#implications-for-jackin]

Desktop needs the shared Security.framework credential reader and must classify denial, unavailable interaction, and missing items without bypassing Claude’s file fallback.

## Limitations and unknowns [#limitations-and-unknowns]

* Whether `api/oauth/profile` returns a plan/subscription label (eliza parses only `account.uuid`, `account.email`/`email_address`, `organization.uuid`); plan attribution may still need the credential file's `subscriptionType` or the `claude_cli/roles` endpoint — needs an operator-authenticated capture.
* Which key carries routines data on a routines-active account (`seven_day_routines` vs `seven_day_cowork`); all public captures observed show `seven_day_cowork: null`.
* The undocumented mechanism by which macOS Claude Code sometimes writes `~/.claude/.credentials.json` after a manual Keychain unlock (#44028 observed it "spontaneously"); do not design around it.
* A current live primary source for "session starts with your first message" (MED via retired-article echoes only).
* Whether Anthropic will ship the #22144 credential-export/usage-cache path, which would remove the Keychain-prompt requirement for jackin❯ desktop.

## Sources [#sources]

* [Claude Code authentication](https://code.claude.com/docs/en/authentication)
* [Usage-limit best practices](https://support.claude.com/en/articles/9797557-usage-limit-best-practices)

## Related work [#related-work]

* [Claude API evidence](/research/product/desktop/usage-provider-apis/03-claude-api/)
* [Provider API coverage](/research/product/desktop/usage-provider-apis/)
