hraness

isolated provider accounts: why agent capacity means n subscriptions, not one api key

clean handoffs between accounts without losing the thread

Drafted by an AI agent at Ben Guo's direct request from the Hraness source repositories, and checked against those sources before publication.

One API key is a single lane. If you run coding agents seriously, you hit the provider’s rate limits, weekly quotas, or concurrency caps, and then the honest answer is that capacity means N subscriptions, not one bigger key. The infrastructure question is how to keep N provider accounts on one machine without credential bleed, silent failover, or lost threads.

Oompa’s answer has three parts: isolate each account’s provider home on disk, make account selection explicit and user-directed, and treat any movement of a session between accounts as an evidence-bound operation rather than a convenience feature.

isolated homes

Each Oompa profile owns independent provider homes. For Claude Code, the profile’s home is exported as an isolated CLAUDE_CONFIG_DIR: Claude authentication is provider-scoped, so it neither reads nor overwrites Codex state, and Codex sign-in does not imply Claude sign-in. For Codex, the daemon forces both pinned credential stores to file mode at the process boundary and proves the effective values before any account, plugin, or session effect.

The point of isolation is not secrecy between your own accounts; it is non-interference. Two profiles can hold two different Codex subscriptions, and a sign-out, credential rotation, or rate-limit event on one never mutates the other’s home. oompa account list --provider codex reports the stored account order, default marker, and each account’s last readiness observation, with an explicit caveat: readiness is cached local evidence, not current sign-in proof or quota freshness.

explicit selection

Sessions bind to an account at start: oompa session start <account> records the provider account the session will run under, and provider effects recheck that identity before dispatch. Account selection is user-directed throughout. A provider switch can take --account, but the option is refused once the session has acquired its working-memory authority, because that account-bound lane does not transfer in the current release; a same-profile provider switch remains supported.

Remote control respects the same boundary. The hosted set_provider payload deliberately has no account field: a remote switch keeps the session’s account, because account selection stays on the machine that holds the credentials.

adopting what already exists

Multi-account setups rarely start clean; there are already sessions in ~/.codex and ~/.claude created outside the manager. Oompa’s adoption path shows how to bring those under one profile without copying credentials. Discovery is opt-in per provider and read-only: for Codex it asks the pinned personal-home app-server for bounded session-list pages, and for Claude it reads only allowlisted scalar fields from the local live-session registry, accepting only records that name the exact pinned Claude Code version. It never opens a credential file, a registry key, a socket, or a transcript.

Admission is deliberately narrow. A candidate needs a recent nonterminal observation (normally within fifteen minutes), a reported project root matching a registered Oompa project, no other session owning its provider thread, and a successful resume-and-recheck. Codex custody uses an inactivity inference (a row goes admissible after ten idle minutes, or when a valid Codex Desktop heartbeat automation targets it); Claude custody requires the prior process to be proven not live from PID-domain, PID, and process-start-token evidence. The selector records ownership and routing; the personal home’s credentials are never copied or moved.

the narrow automatic corridor

The codebase’s rule for automation is precise enough to quote in full: explicit account selection and work-task routing are user-directed; automatic account movement is limited to managed Codex sessions after reset handling, under fresh exact source and target authority and durable crash-safe evidence. The corollaries are equally important:

  • An explicit session is never moved automatically.
  • A Claude account is never rotated automatically.
  • A failed or ambiguous turn is never replayed under another account. If you cannot prove what the first attempt did, you do not get to spend a second account finding out.
  • No movement happens from stale or unknown state.

This is the difference between “failover” as a marketing word and failover as a custody operation. Moving a session to another subscription spends that account’s quota against work whose prior effects must be fully accounted for. The corridor exists because Codex exposes managed reset-credit mechanics and Oompa can fence both sides of the move; it stays narrow because the evidence requirements are strict, and because silent account drift is how a user ends up unable to explain which subscription paid for which work.

keeping the thread

Account isolation would be useless if moving providers meant losing the conversation. Oompa keeps a provider-neutral transcript of every session it admits: bounded user_message, item_started/item_completed, and provider_switched events, each text field capped at 16,384 characters, with the redaction rules applied at write time and never raw tool arguments or tool output.

oompa session switch <session> --provider codex|claude is the handoff. It refuses unsafe moves (an active turn, a quarantined session, an impossible preset), builds the neutral transcript, renders a bounded seed, starts a new thread on the target, and sends the seed as its first user message. The seed opens with the literal header [Oompa provider handoff], states plainly that it is Oompa’s own record rather than the previous provider’s transcript, and instructs the model to ask rather than assume. It is capped at 24,576 characters, and when the transcript does not fit, the most recent records are kept: a handoff needs the end of a conversation more than its beginning.

What crosses is the retained conversation as Oompa saw it. What does not cross is equally documented: provider-hidden state, the native thread itself (neither provider can import the other’s), prompt-cache warmth, anything redaction removed, attachment bytes, and history from before Oompa admitted the session. The provider_switched event records the transcript digest, the seed digest, and the exact omission count, so the boundary is provable after the fact.

The retention rules behind the transcript are honest ones. The event ledger keeps at most 50,000 events, 64 MiB, and seven days per session; when a limit prunes a prefix, exports and handoffs carry a retentionGapReason and never invent a count for the missing records. A session history that is partly unknowable says so, which matters when the seed is the only context the target provider will ever see.

observing usage honestly

Multi-account operation needs usage visibility without turning observation into action. oompa usage auto reads or changes the automatic usage policy: a default plus per-provider inherit | on | off overrides, each change requiring the observed revision and a fresh idempotency key. Effective Codex disable suppresses new automatic reset-credit dispatches; it never pretends to cancel an already-admitted provider operation.

The hosted usage surface reports daily Codex reports, at most one per account per 24 hours per machine, grouped across machines for the same account. It uses only reports under two hours old with complete windows, states plainly that a missing account makes the combined percentage unknown, and treats the legacy unlimited bit as possibly-missing quota details rather than capacity proof. The browser only reads; it never spends credits or changes an account.

limits of the model

The design has real edges. Claude profiles default to two concurrent sessions per account, and swarm-scale traffic may be judged non-ordinary by the provider; raising the cap is a knowing user decision. Cross-account working-memory transfer is not implemented, so an account move starts a fresh lane even when the transcript survives. Managed Claude sign-in is Linux-only while macOS Keychain custody awaits live acceptance evidence. And none of this makes provider enforcement disappear: subscriptions, quotas, and acceptable-use policy belong to the provider.

The transferable lesson is the shape, not the vendor specifics. Give each provider account its own home, let the human pick accounts and the system prove movement, keep a neutral record that survives the handoff, and report usage as evidence with dates rather than capacity with confidence.

sources

  • oompa: profiles, isolated provider homes, and the account-movement rule in AGENTS.md; account and usage surfaces in docs/usage-management.md.
  • Oompa provider portability: the neutral transcript, the provider switch sequence, and the seeding rule.
  • Oompa session adoption: how personal provider homes become managed sessions without copying credentials.