agent auth and session resilience
oauth-mux
OAuth/account multiplexing for professional AI harnesses and autonomous agents.
Managed Codex resume has live installed-runtime evidence: a provider-originated usage_limit_reached turn on one account was retried on another account before Codex
saw the 429. Public install lanes now resolve to 0.1.7 across GitHub Release, npm, Homebrew,
curl, and deb/rpm package QA.
- #1 200max-2 · codex-max
successful traffic before the quota event
The engineered 2026-05-09 artifact includes successful max-2 responses before provider-originated quota exhaustion. - #2 429max-2 · codex-max
usage_limit_reached stayed inside the proxy
oauth-mux recorded durable quota evidence, did not deliver the 429 to Codex, and selected an eligible fallback route. - #3 200max-3 · codex-max
same buffered request recovered on fallback
oauth-mux dropped x-codex-turn-state, retried the same responses request with max-3 credentials, and received status 200. - #4 inspectcodex-max · live route state
route availability moves with auth and quota state
Current route counts are operator-local and volatile. Use oauth-mux codex preflight and route explain before making route-state claims.
max-2 to max-3. It does not claim same-thread provider continuity or mid-turn streaming
recovery.Install and probe
npm install -g oauth-mux
oauth-mux init --codex-max
oauth-mux doctor
oauth-mux route explain --profile codex-max --capability codex-max
oauth-mux codex resume agent-safe operations
Inspect auth and route state without touching secrets.
The control plane is built for humans and agents: inspect configured accounts, prove runtime access, explain route health, and receive exact next-action commands without printing token material.
Works now
Installed oauth-mux codex resume has live managed-frame evidence for provider-originated quota handoff; traffic recovered onto another account without restarting the harness process.
Safe by default
Inspection commands return redacted JSON and do not open browser auth, copy provider stores, or spend provider calls.
Explicit handoffs
Login, reauth, live probes, and revalidation remain labeled user-mediated or spend-confirmed commands.
Shim bounded
The package codex shim manages future shell launches only when PATH resolves it; direct native binaries and already-running Codex sessions are not globally protected.
No-spend inspection
Run these before any live probe. The outputs show route availability, local runtime readiness, repair actions, and latest managed Codex evidence without spending provider calls.
oauth-mux doctor runtime --profile codex-max --capability codex-max --json
oauth-mux accounts list --provider codex --json
oauth-mux route explain --profile codex-max --capability codex-max --json
oauth-mux repair-plan --profile codex-max --capability codex-max --json
oauth-mux codex status-latest --json Enroll the next account
Enrollment mutates oauth-mux-owned config and store scaffolding only. Upstream login remains a user-mediated handoff, then route explanation or repair planning records fresh next actions.
oauth-mux accounts list --provider codex --json
oauth-mux enroll plan codex --account max-4 --json
oauth-mux enroll codex --account max-4 --confirm-enroll --json
oauth-mux codex login-device max-4
oauth-mux repair-plan --profile codex-max --capability codex-max --json Repair without silent auth
Interactive reauth is a labeled command that the user runs. oauth-mux should not silently repair upstream OAuth state in the background.
oauth-mux route explain --profile codex-max --capability codex-max --json
oauth-mux repair-plan --profile codex-max --capability codex-max --json
# run the labeled login command shown in the handoff
oauth-mux codex login-device max-1
oauth-mux route explain --profile codex-max --capability codex-max --json Live path boundary
Live Codex work is explicit. Use the managed launch path, then inspect the redacted status artifact; do not treat raw wrapper scripts or repo-local binaries as public acceptance evidence.
# spend-confirmed paths stay explicit
oauth-mux accounts list --provider codex --json
oauth-mux route explain --profile codex-max --capability codex-max --json
oauth-mux codex resume
oauth-mux codex status-latest --json what oauth-mux is
A broker for AI harness auth, quota, tier, and local runtime state.
Developers and agents increasingly operate across personal, work, team, subscription, API-key, and service identities. Current CLIs often expose one active account path, so quota exhaustion or stale auth becomes a manual context switch.
oauth-mux wraps a harness with route-state diagnostics, managed auth/config overlays, and labeled fallback decisions. The goal is to keep the harness usable when account state changes without hiding the evidence from operators.
lifecycle
A brokered harness session, not a background wish.
The usable surface is a managed launch/resume path, route-state evidence, and a redacted artifact trail that a human or agent can inspect without touching credential material.
- 01
Install
- 02
init
- 03
enroll accounts
- 04
local diagnostics
- 05
route selection
- 06
managed harness launch
- 07
provider signal observed
- 08
broker decision
- 09
fallback materialization
- 10
redacted status artifact
- 11
repair / revalidate loop
Managed Codex flow
oauth-mux codex resume selects a route, builds a mux-owned auth/config overlay,
bridges native session authority, spawns Codex, classifies provider responses, retries fallback when eligible,
and writes redacted evidence.
Proof ladder
oauth-mux separates diagnostics from product proof. The current public proof reaches next_turn_seamless for managed Codex quota handoff; higher continuity claims remain open.
prepared_fallback
diagnosticA candidate fallback exists. This does not prove a harness turn survived account exhaustion.
broker_owned
implementedThe harness ran through oauth-mux-owned auth/config and redacted status instrumentation.
next_turn_seamless
live-proven for managed CodexQuota exhaustion was observed, retried on a distinct fallback account, and kept the Codex process alive.
mid_turn_streaming
openStreaming recovery during an active provider stream remains research.
cross_session_thread_continuity
openSame-thread provider semantics across account boundaries remain unclaimed.
unmanaged_daemon_handoff
openBare Codex hot-swap from a background daemon is not part of the proven managed path.
CredentialLiveness & Availability
// ── Credential Liveness ──
//
// Three distinct layers that the mux pipeline must reason about:
//
// 1. Authentication: Can the token prove identity to the provider?
// (not expired, not revoked, parseable)
//
// 2. Operability: Is the account in a state where it can serve requests?
// (not suspended, subscription active, tier sufficient)
//
// 3. Availability: Does the account have capacity right now?
// (not rate-limited, quota not exhausted, not in cooldown)
//
// The mux response differs for each:
// Auth failed → mark dead, never retry automatically
// Inoperable → mark degraded, retry after long interval (hours)
// Rate limited → cooldown timer, retry same account after seconds
// Quota exhausted → switch account, retry after window reset (hours/days)
// Provider down → switch provider entirely, not just account
pub const CredentialLiveness = union(enum) {
live: LiveCredential,
degraded: DegradedCredential,
dead: DeadCredential,
pub const LiveCredential = struct {
availability: Availability,
};
pub const DegradedCredential = struct {
reason: DegradedReason,
since: i64,
retry_at: ?i64 = null,
};
pub const DeadCredential = struct {
reason: DeadReason,
since: i64,
};
};
pub const Availability = union(enum) {
available,
rate_limited: RateLimitInfo,
quota_exhausted: QuotaInfo,
cooldown: CooldownInfo,
pub const RateLimitInfo = struct {
retry_after_s: u32,
limited_at: i64,
window: RateLimitWindow,
};
pub const QuotaInfo = struct {
window_resets_at: ?i64 = null,
usage_pct: ?u8 = null,
exhausted_at: i64,
};
pub const CooldownInfo = struct {
until: i64,
reason: []const u8,
};
}; DegradedReason & DeadReason
pub const DegradedReason = enum {
tier_insufficient,
subscription_paused,
provider_degraded,
scope_insufficient,
schema_invalid,
terms_required,
step_up_required,
pending_verification,
unknown_4xx,
};
pub const DeadReason = enum {
token_revoked,
account_deleted,
auth_permanently_failed,
}; MuxDecision
The fromHttpStatus switch is the canonical routing-semantics table — HTTP status maps to a single decision,
no extra prose required.
// ── Mux Decision ──
// What the pipeline should do after probing a credential.
pub const MuxDecision = enum {
use_this,
try_next_account,
try_next_provider,
wait_and_retry,
give_up,
pub fn fromHttpStatus(status: u16) MuxDecision {
return switch (status) {
200...299 => .use_this,
401 => .try_next_account,
403 => .try_next_account,
429 => .wait_and_retry,
500...599 => .try_next_provider,
else => .try_next_account,
};
}
pub fn isRecoverable(self: MuxDecision) bool {
return self != .give_up;
}
}; Install
Public npm, GitHub Release, Homebrew, curl, and package lanes now resolve to 0.1.7. Source dogfood remains a separate provenance lane when testing unreleased
worktree behavior.
npm
live (v0.1.7)Published from CI release tarballs; the private-source 0.1.7 publish used provenance disabled.
npm install -g oauth-mux GitHub Release tarballs
live (v0.1.7)Six platform tarballs per release; suitable for air-gapped or policy-managed systems.
# six platform tarballs per release
oauth-mux-x86_64-linux.tar.gz
oauth-mux-aarch64-linux.tar.gz
oauth-mux-x86_64-macos.tar.gz
oauth-mux-aarch64-macos.tar.gz
oauth-mux-x86_64-windows.tar.gz
oauth-mux-aarch64-windows.tar.gz Homebrew
live (v0.1.7)Public Jess-owned tap with strict local install QA and parsed stable-version proof.
brew tap jesssullivan/omux https://github.com/Jesssullivan/homebrew-omux.git
brew install jesssullivan/omux/oauth-mux deb / rpm
live (v0.1.7 packages)Release packages are attached to GitHub Releases; no public apt/yum repository is claimed.
# deb/rpm packages for Linux hosts
# download from the current GitHub Release curl installer
live (v0.1.7)Installer resolves the public release lane. Override the source repo only for operator testing.
curl -fsSL https://omux.xoxd.ai/install.sh | sh Local source dogfood
Use remove-then-copy for macOS local dogfood so taskgated does not keep stale signature state on the old Mach-O vnode.
just build
mkdir -p ~/.local/bin
rm -f ~/.local/bin/oauth-mux
cp ./zig-out/bin/oauth-mux ~/.local/bin/oauth-mux
shasum -a 256 ./zig-out/bin/oauth-mux ~/.local/bin/oauth-mux
which -a oauth-mux
oauth-mux version curl installer repo override
The canonical public source repo is Jesssullivan/oauth-mux; override the upstream
by setting REPO= before the curl pipeline.
# override the source repository (defaults to Jesssullivan/oauth-mux)
REPO=Jesssullivan/oauth-mux curl -fsSL https://omux.xoxd.ai/install.sh | sh Usage paths
Humans get a native managed Codex path. Agents get redacted JSON inspection. Provider authors get typed validation before any live proof lane.
Human Codex path
The managed command keeps Codex inside the oauth-mux frame while preserving native chooser/session authority.
oauth-mux init --codex-max
oauth-mux doctor
oauth-mux route explain --profile codex-max --capability codex-max
oauth-mux codex resume If upstream auth is stale, run the labeled command from route explanation, such as oauth-mux codex login-device max-3.
Generic provider author path
A new provider should usually start as data, not Zig:
- Write a JSON provider definition with credential parsing, injection, probes, and failure rules.
- Add redacted fixtures for the provider's success, rate-limit, quota, degraded, and auth-dead responses.
- Run `oauth-mux config validate` and the no-secret E2E harness.
- Run live QA only with explicit account-scoped consent.
Operator validation commands (verbatim, from oauth-mux/docs/onboarding.md:155-163):
oauth-mux config validate
oauth-mux doctor --json
oauth-mux report --redacted --json
oauth-mux discover --json Agent-safe inspection path
Agents start with redacted JSON discovery and never reach for credential paths. These commands expose runtime readiness, route state, repair actions, and status evidence without spending provider calls.
oauth-mux doctor runtime --profile codex-max --capability codex-max --json
oauth-mux accounts list --provider codex --json
oauth-mux route explain --profile codex-max --capability codex-max --json
oauth-mux repair-plan --profile codex-max --capability codex-max --json
oauth-mux codex status-latest --json Agent-safe command surface
- oauth-mux config validate
- oauth-mux doctor runtime --profile <profile> --capability <capability> --json
- oauth-mux accounts list --provider <provider> --json
- oauth-mux route explain --profile <profile> --capability <capability> --json
- oauth-mux repair-plan --profile <profile> --capability <capability> --json
- oauth-mux codex status-latest --json
- oauth-mux report --redacted --json
Agents must not
- read files referenced by `secret.path`;
- print token-shaped values;
- copy OAuth stores between accounts;
- run live probes or revalidation unless the user explicitly authorized spend.
Security & Privacy
oauth-mux holds four hard guarantees around how secrets enter the harness, where they live, and what shows up in machine-readable output.
Four guarantees
- no `.env` token dumping;
- no committed credential stores;
- no raw token output in discovery/health;
- explicit live probes only when they may spend calls.
Allowed secret backends
The backend stores or returns raw secret material. It does not define provider logic. Any of the seven backends below is allowed; everything else is rejected at config validation time. Click a backend to expand its semantics.
Redaction posture
discover --json is intentionally redacted. It reports config path, state path, providers,
account names, secret backend names, tags, profiles, and safe command templates. It does not include token material.
What is not claimed
The current public proof is deliberately narrower than the product ambition. These remain research, adapter, or evidence lanes:
- same-thread provider semantic continuity across account boundaries
- mid-turn streaming recovery
- unmanaged bare-Codex daemon hot-swap
- universal provider support
- non-Codex stay-afloat proof
Contribute a provider
New providers travel a schema-modeled to live-proven path. Start by declaring a JSON definition with redacted cassettes, then graduate to live-proven only after operator-scoped QA. The provider-authoring checklist is the canonical source of truth for what the harness will accept.
What we ask of contributors
- link the provider-authoring checklist;
- ask for redacted cassettes, official docs links, and one positive/negative probe case.
Standards baseline
Provider definitions are written against: OAuth 2.1 draft, RFC 9700, RFC 8414, RFC 7591, RFC 9728, RFC 8707, RFC 9449, and the MCP authorization 2025-11-25 profile.
Contact
Three channels cover the public surface today. There is no public security email yet.
- GitHub issues for bugshttps://github.com/Jesssullivan/oauth-mux/issues
- GitHub discussions or issue templates for provider requestshttps://github.com/Jesssullivan/oauth-mux/discussions
- security contact once public security policy exists
No public security@ address is published yet. For sensitive reports today, use a private GitHub issue or wait for the public security policy.