09 — Claude account and window semantics
Documents Claude credential storage, payload fields, profile access, and quota-window semantics.
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
- 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 callapi.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 whetherclaude setup-tokenlong-lived tokens are accepted by theoauth/usageendpoint (chapter 03 open unknown, line 72)?
This chapter informs jackin-desktop.
Method
Verification cutoff: 2026-07-24
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
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.jsonwith file mode0600. On Windows, credentials are stored in%USERPROFILE%\.claude\.credentials.json" — https://code.claude.com/docs/en/authentication (confidence: HIGH) - On macOS a fresh
/loginwrites only to Keychain;~/.claude/.credentials.json"did not exist by default" — 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
nodecallingunlink /Users/<user>/.claude/.credentials.json; reported for shared-volume macOS/Linux setups — 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 (confidence: MED-HIGH) - However, Claude Code on macOS reads
~/.claude/.credentials.jsonas 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, corroborated by 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
Claude Code-credentials: the official-repo bug #9403 showssecurity find-generic-password -s "Claude Code-credentials" -wreturning 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 (confidence: HIGH) - Mass independent corroboration of the service name: GitHub code search for
"Claude Code-credentials" find-generic-passwordreturns ~3,024 indexed files across independent tools (e.g.matryer/xbar-pluginsclawdbar,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_DIRaccounts do not use the bare service. Claude Code derivesClaude Code-credentials-<suffix>, where suffix is the first eight hexadecimal SHA-256 characters of the absolute config-directory path. jackin❯ already carries this derivation incrates/jackin-instance/src/auth.rsand pins two live entries inauth/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 and 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-usagereads 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
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 includetulinmola/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-meterdocs,TopiCsarno/yapcapfixture (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/usageresponse matching jackin❯ live fixture structure exists:five_hour/seven_daywithutilization+resets_at,limits[]withkind: session | weekly_all | weekly_scoped(theweekly_scopedentry 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),spendwithamount_minor/currency/exponent,extra_usage,member_dashboard_available— 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 onweekly_scoped/seven_day_cowork/amber_ladder/seven_day_oauth_appscan be lifted to multi-source) api.anthropic.com/api/oauth/profileexists and is widely used: ~518 indexed files. Reputable non-banned callers:dagger/daggerdefinesoauthProfileURL = "https://api.anthropic.com/api/oauth/profile"alongside scopesorg:create_api_key user:profile user:inference— https://github.com/dagger/dagger/blob/main/internal/cmd/dagger/llmconfig/oauth.go;elizaOS/elizacalls it withAuthorization: Bearer+anthropic-beta: oauth-2025-04-20and parses response shape{account: {uuid, email | email_address}, organization: {uuid}}— 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/profilereturns 403 "OAuth token does not meet scope requirement any_of(user:profile, user:office)" for inference-only tokens; the same issue also namesGET /api/oauth/claude_cli/roles(entitlements/roles) — https://github.com/anthropics/claude-code/issues/79360 (confidence: HIGH for endpoint existence and scope requirement) - The profile endpoint requires
user:profileoruser:officescope and returns account email, enabling jackin❯ email attribution without traffic sniffing.
Q3 — window boundary semantics
- The 5-hour session is a bounded window with a countdown, per live support docs: "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 (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 (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://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 (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_atis provided per window in theoauth/usagepayload 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-suppliedresets_at(start =resets_at - 5h) rather than on local first-request observations. Weeklyresets_atgives the fixed weekly boundary directly. (confidence: HIGH for usingresets_atas the anchor; MED for the session start=first-message assumption feeding any "elapsed fraction" denominator)
Q4 — setup-token vs oauth/usage
- Rejected. A detailed official-repo issue shows a
claude setup-tokencredential (Max plan) failing the usage endpoint with a scope error: "GET /api/oauth/usagereturns 403: 'OAuth token does not meet scope requirement user:profile'". Root cause quoted: "Setup-token tokens carry only theuser:inferencescope, and the entitlement endpoints reject that scope" — whilePOST /v1/messageswith the same token succeeds — 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) - Scope contrast corroborated independently: interactive
/login-style flows requestorg:create_api_key user:profile user:inference(dagger's OAuth constants — https://github.com/dagger/dagger/blob/main/internal/cmd/dagger/llmconfig/oauth.go), anduser:profileis exactly the scopeoauth/usagedemands 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; similarly
Nanako0129/TokenBar#27 "Claude usage limits for setup-token users via ratelimit headers" — https://github.com/Nanako0129/TokenBar/issues/27 (confidence: MED, concept evidence) - Chapter 03 line 72 open unknown → closed:
CLAUDE_CODE_OAUTH_TOKENsetup-tokens are not a valid credential for jackin❯ Claude usage polling; only/login-minted OAuth credentials (file or Keychain) carryuser:profile.
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. TreatClaude Code-credentialsas canonical for the CLI — https://github.com/anthropics/claude-code/issues/44028 vs https://github.com/anthropics/claude-code/issues/9403 - #9403 documents a transient v2.0.14 bug where the CLI wrote to
Claude Code-credentialsbut read from serviceClaude Code— a version-specific read/write mismatch, not a second canonical name — 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.utilizationrefreshing every ~72h — https://gist.github.com/monperrus/3ac4b303a84946bbeaf2b1123ee99491. Continue treatingresets_atas 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-bardocs/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❯
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
- Whether
api/oauth/profilereturns a plan/subscription label (eliza parses onlyaccount.uuid,account.email/email_address,organization.uuid); plan attribution may still need the credential file'ssubscriptionTypeor theclaude_cli/rolesendpoint — needs an operator-authenticated capture. - Which key carries routines data on a routines-active account (
seven_day_routinesvsseven_day_cowork); all public captures observed showseven_day_cowork: null. - The undocumented mechanism by which macOS Claude Code sometimes writes
~/.claude/.credentials.jsonafter 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.