Changed
- Usage-aware selection only picks accounts with a measured quota. A Claude or Codex usage response whose percentages are missing, null or out of range, or that has no general usage window, is no longer read as unused capacity; plan, credit or model-only data cannot show an account is available. Such a read is now reported as unavailable or degraded instead of as spare capacity. Best-profile selection and threshold lists now leave out, for every provider, accounts whose quota read failed and accounts at a hard limit in any window, including model windows. When several model windows match, the most constrained one decides, so an idle family allowance can no longer hide an exhausted model cap or the reverse. Usage responses are read up to 1 MiB, and provider text is kept out of error messages.
- Native Gemini CLI logins are treated as renewing themselves. Gemini CLI renews its own cache with its built-in OAuth client, which caam cannot use. Such a cache now reports no
refresh_due, gets nocaam refreshrecommendation, and is not handed to the auth pool for a refresh that would only fail. A Gemini cache that carries its ownclient_idandclient_secretkeeps caam's refresh schedule. - Source builds need Go 1.26.10. The repository, CI and
install.shsource fallback now pin Go 1.26.10, the current patched 1.26 release (#122). The new minimum matters only when the installer has to build from source; it will not build with an older Go.
Added
- Isolated Claude logins can be saved to the vault without touching the live login (#125).
caam backup claude <new-name> --from-profile <isolated>, or the equivalentcaam profile backup claude <isolated> [--as <new-name>] [--json], saves a registered isolated OAuth login as a new vault entry. The active Claude login, the keychain and the isolated profile are left unchanged, and no tokens are refreshed, so agents on the live login keep running; activate the entry later withcaam activate claude <new-name>. The backup reads the same config directorycaam execuses (the XDG layout, or a legacy-onlyhome/.claudelogin when XDG holds no authentication) and never mixes files from both. It refuses a source whose two layouts hold different accounts, whose registered auth mode is not OAuth, or whosesettings.jsonor.claude.jsonwould make Claude use another credential or provider (API keys, helpers, cloud providers, endpoint or config-dir overrides), and it never replaces an existing vault entry. On Unix the snapshot is flushed to disk before success is reported; if only the final directory flush fails, the command exits with an error whose JSON sayspublished: trueand gives the snapshot's path, and the snapshot is kept rather than rolled back. Both commands share one locked capture and publication path. Details are indocs/isolated-claude-vault.md. caam keepalivereports why a native CLI run failed (#129). On an unsuccessful run the JSON result now addsnative_exit_codeand, for Claude,native_api_error_status(the HTTP status of the rejected request, for example 401 or 429) andnative_terminal_reason(Claude's own short reason, for exampleapi_error). Keepalive runs Claude with--output-format jsonand reads back only those two fields, after checking their form; Claude's message text is never copied. The fields are omitted after a timeout or cancellation, and Grok's output stays discarded apart from its exit code.
Fixed
caam doctorchecks Codex ChatGPT logins against an endpoint that accepts them (#127). It probedapi.openai.com/v1/me, which answers 403 to every ChatGPT-login access token, working or not, so working logins failed with advice to log in again. Doctor now reads the ChatGPT backend usage endpoint thatcaam limits codexalready uses, where 200 confirms the login and 401 rejects it. A 403 is reported as inconclusive instead of as a rejected login. A 401 for a vault snapshot whose access token has already expired by its own claim is a warning without login advice, because Codex rotates the active login in place and caam saves it back to the vault on the next switch.caam cost tokensreads Claude Code and Codex session logs (#126). It looked for Claude usage in~/.local/share/claude/logsand did not read Codex rollouts, so it returned nothing on hosts with fresh logs. It now reads Claude Code's per-session transcripts under<config>/projects/($CLAUDE_CONFIG_DIR, or both~/.config/claudeand~/.claude), including subagent transcripts, and counts each message once. Codex usage comes from rollouts under$CODEX_HOME/sessions/andarchived_sessions/. A line longer than 1 MiB no longer ends the scan of its file.- Gemini CLI logins show their real expiry (#123). Health did not read
expiry_date, the millisecond deadline Gemini CLI writes, so a native Gemini login always had an unknown expiry and a lapsed one stayed healthy. Health now reads it first, like the import validator. Gemini refresh updatesexpiry_dateinstead of writing a second, newer deadline next to the stale one. - Grok quota reads never start an interactive login (#124). The billing probe now starts Grok only with an access token whose absolute expiry is comfortably in the future, checks every entry of a multi-entry cache, and authenticates only when Grok advertises exactly one cached-token method, under its own 5-second deadline. It never falls back to an interactive method, and browser openers are stubbed out for the probe. A stale credential or an unexpected handshake used to leave Grok at a login prompt.
caam keepalivegives advice that works when two live homes hold one account (#128). The block told you to sign in to each home separately, which never clears it: keepalive cannot tell a home's own sign-in from a copy that rotated later. It now says to sign out of all but one of the listed homes. Homes for other accounts can still be selected while the conflict remains.- Quota for an isolated Claude profile comes from the login
caam execuses.caam limitstried legacy credentials before XDG. It now uses the same source selection ascaam exec, keeps the token and its usage cache in one layout, and reports an invalid isolated account instead of reading another layout or a shallow profile. - Codex quota for an API-key profile no longer reads a leftover ChatGPT login. Quota readers check the profile's selected auth method before any stored OAuth tokens, so an unused subscription login cannot stand in for the account being measured, and such profiles stay out of the bulk quota pool.
- The installer builds the version you asked for, or stops (#121). When
--versionfell back to a source build, it could buildmainand label the binary with the requested version. It now buildsrefs/tags/<version>and refuses to continue if that tag cannot be checked out. Unversioned source installs still buildmain. caam login claude,caam auth importandcaam renamesay which store they use (#125). A rejectedcaam login claudenow points to the isolated/login,/exitandcaam backup claude <name> --from-profile <isolated>steps.caam auth importhelp says it creates an isolated profile forcaam exec, not a vault entry, andcaam renamesays it copies vault profiles only. Renaming a profile that exists only as an isolated profile says so instead of reporting "not found", and for Claude it gives the exact backup command.- Switching honors the selected Codex or Gemini authentication method. Leftover OAuth tokens in an API-key profile no longer cause activation to keep the wrong live login, or allow outgoing OAuth preservation to overwrite the saved API key. Discovery and Codex restore use the same account boundary, while newer credentials for the same OAuth account remain protected.
- Claude auth detection and import preserve native credentials. Detection reads the path-scoped native authority without creating or updating a keychain mirror. An explicit
CLAUDE_CONFIG_DIRkeeps its own account, and a denied or malformed keychain read cannot silently select a stale file.caam auth import claudeand first-runcaam initcan import a keychain-only login directly into the new profile while leaving native files and the keychain unchanged.
Security
- Token refreshes and token checks no longer follow HTTP redirects. The Codex and Gemini token exchanges, the Claude token helper and the Codex token check validated only the first URL and then followed redirects, so a 307 or 308 could replay a refresh request, refresh token included, to another URL, and a redirected 200 could pass a token check. They now use a client that never follows redirects: a 3xx is reported with its status, without a second request, a credential verdict or a retry, and the redirect target is not echoed in errors. Timeouts, TLS checks and the 429/5xx retries are unchanged.
- Built with Go 1.26.10 and
golang.org/x/netv0.60.0 (#122). Go 1.26.10 carries the October Go security fixes, including the October 19crypto/x509fix, and x/net v0.60.0 is the fixed line for the HTTP/2 advisories.