Credential storage and safety
One credential store per account, who writes it, and the rules onboarding enforces
All state lives in ~/.config/tokenmaxxing/. Each pooled Claude account owns one Claude Code credential store, stores/<uuid8>/, named from the first 8 characters of the account id.
A session runs on an account because its CLAUDE_SECURESTORAGE_CONFIG_DIR points at that store; Claude Code resolves the credential from that variable first. tokenmaxxing never derives a store from ambient variables: it names the directory itself (the account's store, or a throwaway onboarding home) and computes the same keychain item name Claude Code does.
Platforms
Credential I/O goes through a single platform-selected store facade; call sites never branch on platform.
| macOS | Linux | |
|---|---|---|
| Where Claude Code keeps a store's credential | a generic-password item in the login keychain | stores/<uuid8>/.credentials.json |
| Plaintext on disk | none | the credential file, 0600 in a 0700 directory |
| The tokenmaxxing write | an add-generic-password line piped into security -i over stdin | atomic: write-to-temp, fsync, then rename |
| Codex, pi, and grok stores | 0600 auth.json files | 0600 auth.json files |
The other pools are plaintext files on both platforms because those CLIs keep plaintext files themselves.
macOS keychain item
- Item name:
Claude Code-credentials-<sha256(store path)[0:8]>. The hash is over the NFC-normalized path, so the path must be byte-stable. - Account name: the one Claude Code derives,
$USERoros.userInfo().username. It isclaude-code-userwhen that lookup throws, or when the name is empty or holds a character outside letters, digits,.,_, and-.TOKENMAXXING_KEYCHAIN_ACCOUNToverrides it. - Read:
security find-generic-password -w, so the secret only ever appears on stdout. A read that fails for any reason other than a missing item (a locked keychain or a denied ACL, for example) throws, and the account is skipped for that evaluation rather than read as empty. - Write: the one write tokenmaxxing makes pipes an
add-generic-passwordline intosecurity -iover stdin, so the secret never appears in any process's argv. The interactive mode has a roughly 4 KB line buffer, so an oversized blob falls back to an argv write. The onboarding write stores theclaudeAiOauthobject alone, which stays well under that size.
Linux file
A write is atomic: write-to-temp, fsync, then rename, so a concurrent reader always sees a whole old or whole new file, never a partial one.
Rules
One store per account, never per session
Sessions on the same account share that account's store and coordinate refreshes through Claude Code's own refresh lock, which is rooted at the store. A refresh rotation revokes the previous access token of that grant, so two stores holding one grant break each other:
Onboarding is the only tokenmaxxing write
- The throwaway home and its credential item are deleted afterwards.
- After onboarding, Claude Code's refresh is the only writer: no harvest, no park, no install, and no refresh grant from tokenmaxxing.
- The login you already had before
initkeeps its own grant in Claude Code's default store, for sessions started outside the supervisor. rmis the only deletion: it removes the store, its keychain item on macOS, and the account's tee.
Identity by true owner
Before a harvested login lands in a store, its access token is resolved through the OAuth profile endpoint (fetchTokenIdentity, matched on the account UUID). The match never uses the organization, because every seat of a Team plan shares one organization, and never a stored label.
authrefuses a login that lands on a different account than the one being repaired.doctorruns the same check for every store whose access token is not about to expire. It reports a store past that as unverifiable, because tokenmaxxing holds no refresh grant.
Dead grants degrade cleanly
- A store whose credential Claude Code cleared after a failed refresh (an empty access token or an empty refresh token) is recognized as dead without spending a request. It flags the account as needs-reauth, and the decision tries the next candidate.
- Five consecutive failed store reads on the check tick flag the account the same way.
tokenmaxxing authfixes a flagged account in place.
No token logging
Tokens never appear in the log, in errors, or in command output. Error bodies from the identity endpoint are never surfaced raw; only an allowlisted set of fields survives.
Spawn hygiene
Every spawned claude child that runs on a pooled account (a supervised session, its /compact run, and the refresh spawn) scrubs the environment variables claude honors before its store lookup, then sets the store it means to read. A credential exported in the shell therefore never replaces the seat.
| The shell sets | ANTHROPIC_BASE_URL |
|---|---|
| one of the scrubbed variables | dropped with it, so a gateway configured for another credential never receives a pooled token |
ANTHROPIC_BASE_URL alone, with none of the scrubbed variables | stays; Claude Code sends the pooled token to it the same way it sends your own login's token |
The onboarding login scrubs the same list, so add run inside a supervised session does not land the new login in that session's store.
The usage read
The direct usage read is the one place a stored access token leaves the store without a claude child: every sampler (the check tick, status, the seat's in-decision read, and onboarding) sends it to the no-spend usage endpoint over HTTPS. tokenmaxxing holds no refresh grant, so an expired token goes through Claude Code first:
The refresh spawn runs with the account's store set and a throwaway CLAUDE_CONFIG_DIR. The sample reads with the token Claude Code wrote, and the throwaway home is deleted afterwards.
API keys
An Anthropic API key stored with tokenmaxxing key add is a secret of its own, apart from the account stores.
- Storage: the key is read from stdin, never from an argument, and written once to
api-keys/<id>, a 0600 file in a 0700 directory, through the same atomic write as a credential file.api-keys.jsonbesideaccounts.jsonholds the label, balance, spend, and refusal of each key, and no secret.key rmdeletes the file outright instead of moving it to the trash, so a live key never sits in a trash folder. - Delivery: the supervisor gives the
claudechild a Unix socket as file descriptor 3, withCLAUDE_CODE_API_KEY_FILE_DESCRIPTOR=3naming it, writes the key and a newline into its own end, and closes that end. Claude Code reads the key from the socket once, then closes the descriptor and removes the variable from its environment, so the processes it starts inherit neither. It does not consult a credential store. The key never appears in an argument or an environment variable, and every relaunch and the compaction spawn get a socket of their own. This works the same on macOS and Linux. - Why not
ANTHROPIC_API_KEY: an interactive Claude Code session ignoresANTHROPIC_API_KEYuntil you approve the key in~/.claude.json, which tokenmaxxing never writes. The descriptor needs no approval in interactive or print mode. - No store: a key session starts from the scrubbed environment and carries no
CLAUDE_SECURESTORAGE_CONFIG_DIR, so no pooled account's credential is in reach.TOKENMAXXING_API_KEY_IDnames the key for the hooks. - Workspace header: a key stored with a workspace id gets
anthropic-workspace-id: <id>appended toANTHROPIC_CUSTOM_HEADERS, after any headers your environment already sets. - Compaction: the
/compactrun before a move off a key gets the key the same way.
Lending a Claude store
tokenmaxxing seat <pid> lends one pooled Claude account to a consumer that starts its own Claude Code, such as a test runner.
- The fold and the ranking run under the pool lock.
- The ranking reads cached figures with every configured per-model cap gating, because the consumer's model is unknown. The command sends no usage read.
- A candidate is an account that is not flagged for reauthentication and holds a usable credential in its store.
- With no usable account the command exits 1, because a borrower cannot wait for a reset.
| Later event | Result |
|---|---|
| a repeat call by the same live pid | returns the account it holds |
| a repeat call by the same live pid while the account it holds is flagged for reauthentication or its store lost its credential | refuses |
| the pid exits | the record reaps itself |
Naming a store
tokenmaxxing seat <pid> --store <id> skips the ranking and lends the store whose directory name is <id>, the first 8 characters of the account id. A consumer with its own rules, such as lower bars, no credits account, or a cap on its own processes per store, picks the store itself and still writes the record, so placement and moves of host sessions count each of its processes as one session on that store.
- Under the pool lock the command checks only that the store is in the Claude pool, holds a usable credential, and is not flagged for reauthentication. Bars, the credits flag, and which stores to avoid are the caller's job.
- On success it writes
live/seat-<pid>and prints the store directory, the same as a ranked grant.
| Exit | Meaning |
|---|---|
| 0 | granted, or the same live pid already holds this store |
| 1 | the store is flagged for reauthentication or has no usable credential |
| 2 | no store with that id in the pool, the live pid already holds another store, or bad usage (a missing id, --codex) |
Shared seats
Claude seats are shared: a lent account stays a placement and move target for host sessions and a candidate for other seat callers, and each record counts as one session on it. The ranking divides an account's session headroom by its session count plus one and breaks remaining ties by the session count. A burst of seat calls therefore spreads across the usable accounts the way a burst of launches does.
Why the grant stays intact
Every Claude Code process that runs with the store as CLAUDE_SECURESTORAGE_CONFIG_DIR takes Claude Code's own refresh lock, <store>/.oauth_refresh.lock, and re-reads the store under it before it refreshes:
That holds only when the consumer runs Claude Code with the printed directory itself as CLAUDE_SECURESTORAGE_CONFIG_DIR, as the user that owns the store on the host.
| How the consumer uses the store | Result |
|---|---|
| the printed directory itself, as the host user that owns the store | works |
| a read-write bind mount of the directory into a container whose process maps to that host user: a rootless runtime that maps the container user to you, or a container user with your uid | works |
| a container process that maps to another host user, root under a rootful runtime included | Claude Code replaces .credentials.json with a new 0600 file on each refresh, so the file ends up owned by that user, and the host sessions on the account can no longer read it |
a copy of .credentials.json | one grant in two stores (see One store per account, never per session) |
| a single-file bind mount | the refresh lock is in another directory, so the consumer and the host sessions refresh without excluding each other |