Skip to content

Repository files navigation

Calcifer

CI License: MIT Rust: 1.85+

Calcifer is a pre-alpha, local-first Rust wrapper for running official coding-agent CLIs with isolated account profiles and structured usage visibility.

Warning

Status: functional pre-alpha. Codex profile registration with private provider-identity deduplication, staged same-identity reauthentication, confirmed crash-safe local removal, a private default-disabled trust-domain/pool registry, pinned launches, same-profile resume, and on-demand usage status are implemented on Unix. Linux additionally exposes an explicit experimental supervised exact-resume path through the pinned App Server, typed monitor, official remote TUI, and foreground terminal anchor. It requires one profile and one canonical thread UUID. An optional explicit --failover-pool may traverse one already-enabled same-trust-domain Codex pool after typed exhaustion and fresh structured revalidation, fork the validated rollout into the selected profile, and attach the official TUI. It never replays input or falls back to direct resume. Ordinary run and resume never opt into this behavior. macOS production supervised launch is unsupported because no reviewed public descriptor-exec primitive can bind the launched image to the verified bytes. Verified Windows credential ACLs are not implemented yet.

Calcifer is intended to make routine selection among accounts that you already own or are authorized to use feel boring: authenticate each profile through the provider's official CLI, keep each profile isolated, and start every new CLI process with an explicit profile.

Calcifer does not bypass rate limits, create quota, automate account creation, share credentials, or remove provider login requirements. Initial login and re-authentication may still open a browser.

Why

Logging out of a coding-agent CLI just to select another authorized account can invalidate unrelated sessions or disturb global browser state. A local wrapper can avoid that global mutation by giving each profile its own provider-specific home and always launching the official CLI inside that isolated environment.

official CLI login
        |
        v
profile-specific local credential home
        |
        v
Calcifer selects one immutable profile for a new process
        |
        v
official CLI owns authentication and token refresh

A running process keeps the profile it started with. Switching affects newly started processes only.

What works today

Calcifer manages isolated Codex homes on macOS and Linux, plus provider-managed Claude profiles on Linux:

# Browser authentication is handled by the official Codex CLI.
calcifer auth add codex work
calcifer auth add codex personal
calcifer auth list

# Bind a profile created by an earlier Calcifer release without logging in again.
calcifer auth verify codex@work

# Refresh an existing profile through official browser login. The effective
# account/workspace identity must remain the same.
calcifer auth reauth codex@work

# Change only a local display alias; no browser or provider process is used.
calcifer auth rename codex@work client-a

# Define private user-level routing policy. Every non-empty membership update
# revalidates the current private identity of every member before commit.
calcifer routing domain create codex accounts codex@client-a codex@personal
calcifer routing pool create codex@accounts fallback codex@client-a codex@personal
calcifer routing pool enable codex@fallback
calcifer routing inspect
calcifer --json routing inspect

# Remove one local managed profile after a TTY confirmation, or explicitly.
calcifer auth remove codex@client-a
calcifer auth remove codex@client-a --yes
calcifer --json auth remove codex@client-a --yes

# Read every idle registered profile, or one idle profile, without changing the global login.
calcifer status
calcifer status codex@work
calcifer --json status

# Check this binary's release channel, or select one explicitly.
calcifer update check
calcifer update check --channel stable
calcifer --json update check --channel preview

# Start Codex in one immutable profile.
calcifer run codex@work
calcifer run codex@personal -- --no-alt-screen

# Linux only: opt one exact tracked generation into one enabled pool.
calcifer resume --experimental-supervised --failover-pool codex@fallback \
  codex@work 01900000-0000-7000-8000-000000000001

# Explicitly skip conversation capture when manual recovery is acceptable.
calcifer run --untracked codex@work
calcifer resume --untracked codex@work

# Reopen the newest session in a profile, pin an exact thread, or restore this workspace head.
calcifer resume codex@work
calcifer resume codex@work 01900000-0000-7000-8000-000000000001
calcifer resume

On Linux, the same local lifecycle is available for the reviewed Claude Code adapter. Claude usage/status reporting and automatic routing remain unavailable rather than fabricating a zero quota:

calcifer auth add claude work
calcifer auth list
calcifer auth verify claude@work
calcifer auth reauth claude@work
calcifer auth rename claude@work client-a
calcifer run claude@client-a -- --model sonnet
calcifer auth remove claude@client-a --yes

Each Claude registration owns one private CLAUDE_CONFIG_DIR. The official CLI performs login and refresh; Calcifer never parses or copies its OAuth tokens. Before a launch, Calcifer requires the exact reviewed CLI/status contract and a user-owned, regular, single-link .credentials.json with mode 0600, while removing ambient Claude, Anthropic, AWS, and Vertex credential or provider-selection variables. Reauthentication logs in under a fresh private staging directory and rotates only the credential file through the same journaled visibility boundary used for crash recovery. Because the supported status contract exposes no stable account identifier, Claude aliases are not claimed to deduplicate provider identities.

Each registration gets a private, opaque directory and a complete profile-specific CODEX_HOME. The official CLI writes authentication, project trust, and session state there, so exiting Calcifer does not discard the conversation. Before publication, Calcifer version-gates the installed Codex 0.144.4 adapter and derives an installation-private HMAC fingerprint from the effective ChatGPT account/workspace scope in the provider-owned credential file. A second local alias for the same scope is rejected without displaying or storing the raw scope outside auth.json; different scopes are not claimed to guarantee independent provider quota. Profiles created by earlier releases remain usable for explicit operations and become failover-eligible only after an explicit, non-interactive auth verify succeeds. Calcifer accepts supported Codex project-trust updates semantically while continuing to require profile-local file storage for both Codex account and MCP OAuth credentials and reject profile/provider routing overrides, including MCP OAuth callback URL and port overrides. Managed Codex role configuration is currently unsupported: both a top-level agents table and any auto-discovered CODEX_HOME/agents node fail closed because role files can add indirect complete configuration layers. calcifer resume codex@work remains the explicit official codex resume --last convenience; bare calcifer resume resolves Calcifer's exact tracked workspace thread and never falls back to --last.

auth reauth holds the complete profile lifetime lease, revalidates the current private identity, and runs official codex login in a new owner-private staging CODEX_HOME from Calcifer's neutral working directory. The old credential is never copied into staging. The staged credential is accepted only after the same version gate and opaque identity equality check, then replaces only the profile-local auth.json through a private journaled same-directory transaction. The profile UUID, alias, managed home, configuration, sessions, rollouts, conversation lineage, and identity marker remain unchanged. A different account, cancelled browser flow, unsafe file, or unsupported format leaves the old credential visible. Recovery keeps a new credential once it has become visible and never restores the old backup. --json auth reauth is rejected before provider launch because the official flow owns the terminal.

Profile aliases are mutable local display metadata. auth rename atomically changes only the alias in Calcifer's private registry while holding the same profile lease used by run, resume, and status. The opaque profile ID, managed directory, CODEX_HOME, authentication, provider-identity marker, and session state remain unchanged. Rename is offline: it neither resolves nor starts the provider executable, opens a browser, refreshes a token, nor contacts a network service. If registry durability becomes uncertain after its atomic visibility point, Calcifer reports registry_commit_uncertain; read back auth list instead of retrying blindly.

Routing definitions are stored separately in the private user-level routing.json; repository paths are never a storage or selection input. Trust domains and pools persist immutable profile UUIDs, while codex@alias is only lookup and display metadata. Renaming a profile therefore changes inspection output without rewriting membership. Every pool is created with activation: "disabled". routing pool enable is an explicit user-level policy change that revalidates every current member before committing activation: "enabled"; it does not launch a provider or opt an ordinary profile-pinned run/resume into failover. Only the explicit Linux resume --experimental-supervised --failover-pool ... boundary consumes an enabled pool; there is no ambient default-pool or standalone select command.

Creating a domain, creating a pool, or replacing members revalidates every referenced profile's current private identity in immutable-ID lock order. A missing profile, provider/trust-domain mismatch, legacy unverified profile, identity-key loss, credential drift, duplicate effective provider identity, or busy profile aborts the entire update. Metadata-only rename and removal remain available for cleanup even after a member becomes invalid. Updates use a private lock, revision compare, bounded schema, file fsync, atomic rename, and parent-directory fsync. routing_commit_uncertain means the complete new revision is visible but its directory durability was not confirmed; inspect before retrying.

The full inert definition surface is:

calcifer routing domain create codex <alias> [codex@profile ...]
calcifer routing domain rename <uuid|codex@alias> <new-alias>
calcifer routing domain set-profiles <uuid|codex@alias> [codex@profile ...]
calcifer routing domain remove <uuid|codex@alias>
calcifer routing pool create <domain-uuid|codex@domain> <alias> codex@a codex@b [...]
calcifer routing pool rename <uuid|codex@alias> <new-alias>
calcifer routing pool set-profiles <uuid|codex@alias> codex@a codex@b [...]
calcifer routing pool enable <uuid|codex@alias>
calcifer routing pool disable <uuid|codex@alias>
calcifer routing pool remove <uuid|codex@alias>

Domain membership is a canonical set; pool membership preserves the requested order and requires at least two distinct profiles from its one trust domain. Human and JSON inspection contain only Calcifer-local IDs/aliases, provider, revision, membership, and explicit activation. They never contain provider identity material, account/workspace identifiers, tokens, or reset-credit IDs. See ADR 0004.

auth remove is also entirely local and offline. Without --yes, it requires stdin to be a TTY, displays the local profile ID and deletion scope, and accepts only an explicit yes. Non-TTY use without --yes and JSON use without --yes fail before reading or changing managed state. Removal acquires both profile lifetime leases, so an active run, resume, status probe, verification, or reauthentication operation returns profile_busy without preparing a deletion.

After confirmation, Calcifer validates the exact ownership-marked profile tree, then atomically replaces the stable schema-v1 profiles.json with a bounded, self-contained transient schema-v2 removal barrier. The barrier embeds the expected v1 registry and a path-free tree-manifest proof; it is the first durable transaction state and makes published alpha.4 binaries fail closed instead of writing through an in-progress deletion. Calcifer next persists a matching private sidecar, renames the UUID directory to a same-filesystem opaque tombstone, and atomically publishes a normal schema-v1 registry without the immutable ID. That final registry update is the deletion visibility point: only after readback proves the ID is absent does Calcifer unlink the tombstone through constrained directory descriptors.

The next profile-registry operation, including auth list, recovers an unambiguous interruption to either the manifest-complete old state before visibility or the complete removed state afterward. Completed state remains schema-v1 and readable by alpha.4. Ambiguous or mismatched barriers and sidecars, replaced or missing registries and roots, traversable or replaced directories, hard-linked regular files, unexpected owners, group/other-writable directories or regular files, mount crossings, and malformed tombstones fail closed without recursive deletion. On macOS, every removal-tree entry must be free of extended ACL entries, and managed directories and regular files must also have supported file flags. Calcifer resolves the deepest existing prefix of its configured Unix storage root to a physical path once, stores that path, and passes it unchanged to coordinator and guardian helpers. Later managed operations reject every symlink ancestor and require each real ancestor to be root/current-user-owned and non-replaceable by ordinary mode checks. On macOS, Calcifer binds type, owner, mode, flags, ACL, and inode identity for each acceptance decision to one no-follow descriptor and compares it with the visible pathname. It rejects a parent ACL that could grant, inherit, or block child deletion, and rejects append, immutable, XNU-inherited, and unknown parent flags. A new private file is cleared and read back through the same open descriptor as ACL-free and safely flagged before credential bytes are written. An already ACL-authorized different OS principal that actively mutates the namespace during or after validation remains outside the guarantee because the official Codex CLI accepts CODEX_HOME only as a pathname. On Linux, removal and its recovery require kernel 5.8 or newer so statx mount IDs and openat2 constraints are both available; Calcifer never falls back to st_dev or an unconstrained openat. macOS compares descriptor-derived fstatfs mount identities. Mount identity tokens remain ephemeral in memory and are never persisted or logged.

Provider-created symlinks, Unix sockets, FIFOs, and other non-directory leaves are recorded in the manifest but never opened or traversed. Cleanup unlinks only their names relative to an already constrained parent directory, so an absolute or dangling symlink target remains untouched. Regular files must be single-link, and every traversed directory must remain owner-readable, owner-writable, and owner-searchable; ambiguous replacements still fail closed. The ownership marker and lifetime-lock names are control-plane state, not provider leaves, and must remain private single-link regular files: replacing either with a symlink or hard link always blocks removal before a transaction is prepared. Managed lock files and the removal sidecar are opened with no-follow semantics and their opened descriptors are matched to the visible inode before any lock, read, or durability operation.

Removal does not start Codex, open a browser, contact a provider endpoint, revoke tokens, change global ~/.codex, delete conversation lineage metadata, or remove Calcifer's installation identity key. Reusing the old alias creates a fresh profile UUID; an existing conversation remains bound to the now-missing old UUID and cannot silently move to the replacement account. Local unlinking is not guaranteed secure erasure from backups, snapshots, journaled filesystems, or SSD wear leveling. Use the provider's revocation controls when credential invalidation is required.

Before interactive run and resume, Calcifer canonicalizes the working directory and checks every repository-local .codex layer from the nearest real .git root to that directory. Any .codex/agents filesystem node fails closed even when config.toml is absent; otherwise only a Codex 0.144.4-scoped set of repository settings that do not own managed authentication, provider routing, dynamic features, or state locations is accepted. Unknown keys, ambiguous filesystem nodes, invalid TOML, and files larger than 1 MiB fail before Codex starts. In a linked worktree, Codex 0.144.4 can additionally merge only hooks from the primary checkout; Calcifer does not resolve that external hook source, and repository hooks remain outside its sandbox guarantee. This preflight protects Calcifer's account-routing boundary, but it does not make repository hooks, plugins, tools, or code safe.

Account-only operations do not need repository context. auth add and status therefore run the official CLI from a private runtime directory with its own .git boundary, while retaining the selected profile-specific CODEX_HOME. This remains isolated even when CALCIFER_HOME itself is stored inside a Git repository with local Codex configuration.

For supported Codex 0.144.4 sessions, Calcifer captures the immutable {profile ID, canonical cwd, thread ID} binding in a separate private conversations.json. Bare calcifer resume validates that exact rollout under its source-profile lease and invokes codex resume <exact-uuid> without a prompt. A clean wrapper restart therefore restores the tracked history without an account selector or thread lookup. Interrupted and uncertain crash boundaries show a warning before reopening; missing, archived, incompatible, cross-profile, cross-cwd, corrupt, or ambiguous state stops before provider launch. Resume restores persisted history, not a dead process or in-flight tool call, and never resends the last prompt, approval answer, command, or tool call.

Normal run and profile-specific resume remain fail-closed when Calcifer cannot prove a complete capture inventory. --untracked is the explicit manual escape hatch for run or profile-specific resume --last: it performs no App Server inventory, refuses an unresolved pending launch in the workspace, durably marks the workspace as requiring selection before spawning Codex, retains a metadata-only in-flight ownership record until the official child exits, and prints a warning. That ownership prevents a concurrent exact resume under another profile from restoring a stale automatic head; an exact process that started first also cannot refresh over a later untracked marker. Bare calcifer resume remains disabled afterward until calcifer resume codex@<alias> <exact-thread-id> validates and restores a tracked head. The flag cannot be combined with an exact thread ID or bare resume; a provider argument named --untracked must follow the -- separator as usual.

status starts the installed official codex app-server inside each idle profile and calls the structured account/rateLimits/read method. Before that read, it requires the tested Codex 0.144.4 initialize contract and verifies that the server reports the selected canonical CODEX_HOME. Untested versions, changed initialize data, a different home, or a changed usage schema fail closed as unknown; Calcifer does not send the usage request after an initialize-gate rejection. It displays all returned limit buckets, primary and secondary used/remaining percentages, reset times, workspace credit state, monthly spend control when present, and rate-limit reset-credit count and expirations. It does not scrape the interactive /status screen or read token values from auth.json.

An active run or resume holds a split exclusive lease because a second Codex process could race credential refresh and session writes. A launch coordinator owns one half and a provider guardian owns the other; either process surviving a selective crash keeps the profile busy until the exact provider exits. Consequently, status for that active profile is currently profile_busy / unknown; a list query inspects profiles serially with a per-profile timeout. The experimental Linux supervisor maintains a bounded active observation cache and emits a typed exhaustion outcome, but a switch is considered only when an explicitly selected enabled pool is present and a new structured usage read under the stopped source profile independently confirms exhaustion. Provider identity is revalidated under the same exclusive lease before selecting a target; a changed or externally replaced login fails closed instead of silently rebinding the local alias.

The public Linux failover path reserves a revalidated target and retains its A+B lifetime authority through usage validation, fork, reconciliation, durable generation commit, and production-supervisor bootstrap. It transfers the exact locked open-file descriptions once to the sealed coordinator, then transfers B once to the guardian; each receiver validates the managed lock identity and must ACK on the same private SCM_RIGHTS channel before the sender releases its copy. There is no unlock/reacquire window, pathname retry, or second provider start. Ordinary run, resume, and status remain unchanged; macOS exposes the underlying transfer primitive but not the Linux-only production supervisor.

Issue #54 also connects the previously synthetic process/PTY kernel to the pinned Codex 0.144.4 App Server, typed monitor, readiness relay, and official remote TUI. The production graph is compiled by default, and Linux exposes it only through resume --experimental-supervised with an exact same-profile thread. The coordinator holds lease A, the guardian holds lease B, and App Server, TUI, shell tools, and unrelated children inherit neither lease nor supervisor control descriptors. The persistent shell-facing anchor accepts only one exact eight-byte terminal record followed by EOF. CFCMP\x01\r\n carries provider-release proof only: it is never owner, session, anchor, or shell success by itself, and cannot release an owner without the independently required exact waits and exact frame-plus-EOF checks. The guardian cannot publish it until it consumes a move-only proof that the App Server never started or that its exact direct child was sent the one allowed SIGTERM and then exited with code zero. The distinct versioned CFRET\x01\r\n record carries no reason, identity, or provider data and means only that the guardian has reached an unrecoverable retained boundary. Exact record plus kernel EOF makes the anchor retain its direct child, immutable tty snapshot, and completion endpoint and park; it is never a nonzero or successful shell disposition. For the internal package owner, the same anonymous endpoint also carries one fixed reverse-direction recovery request. Its fixed reason and the transit endpoint's path-free device/inode identity bind the frame to the generation; the guardian accepts only its own identity, while markers and PIDs remain observation-only. A malformed or cross-wired request grants no authority and cannot initiate cleanup. A valid request or independently observed exact peer EOF, including EOF after rejected bytes, may enter the existing typed owner-loss cleanup; that authority comes from the EOF rather than the rejected frame and never directly grants success, release, reap, or numeric-PID signalling. Only an eligible retained deadline/cleanup state may retry once. Recovery racing an already-written lifecycle control may drain one state-valid command without minting its ACK, proof, or normal disposition. At a nonrecoverable retained state, a recovery-transport failure, or a second retention after that sole retry, the guardian consumes the completion endpoint, attempts the retained record and write-half shutdown once, and parks the exact typed provider/terminal owner. The parked guardian deliberately keeps that exact typed owner reachable in its non-returning park loop for the remaining process lifetime; this terminal state is not retryable after the sole retry is consumed. Publication failure such as EPIPE does not release that owner and cannot mint CFCMP. Missing or trailing completion data and early-exit, nonzero, signalled, timeout, second-signal, or forced-kill outcomes retain the relevant authority and park; an accidentally dropped ambiguous App owner aborts without sending another signal.

This recovery capability is live and generation-local. It exists only on the anonymous endpoint retained by that running owner/guardian generation, is never persisted, and does not survive loss of both authorities or a machine restart. It is separate from Calcifer's existing cold conversation resume, which reopens persisted history but does not recover a dead process or in-flight operation.

That graceful-drain proof is deliberately narrow. It records the reviewed direct-child behavior of Codex 0.144.4; it does not prove that every arbitrary descendant which called setsid(2) has disappeared. Issue #55's zero-residue scope is Calcifer-owned direct children and recorded known process groups plus identity-checked runtime-directory, FD, and socket evidence. The separate package smoke is intended to show that the official shell-command path's detached probe inherits none of Calcifer's eight live supervisor authority/control descriptors or denied supervisor/authentication environment, not that Calcifer can enumerate or reap all possible non-child descendants. Containment and accounting for escaped setsid(2) descendants is tracked by issue #56 and is not claimed by #55.

Two independent official-package scenarios are configured. On 2026-07-20, the final Issue #54 candidate source passed the normal-session scenario twice and the retained-recovery scenario once on Apple silicon; the required Ubuntu 24.04/macOS matrix readback remains pending. The normal-session scenario runs the official App Server and remote TUI through the production coordinator, guardian, provider-session, PTY, input-gate, and job-control implementations under a test- owned outer-terminal harness. Its acceptance checks cover initial and resumed gates, resize, group-wide stop/continue, terminal restore, exact child waits, scoped runtime cleanup, and provider-release-gated completion. The retained- recovery scenario is separately designed to stop at RetainedCleanupPending, prove that the checkpoint itself grants no recovery authority, send the one generation-bound recovery request, and require the same provider-release and cleanup gates. The package parent is designed to create the completion endpoint, pass it across real parent-to-coordinator and coordinator- to-guardian exec boundaries, and accept only the exact completion frame followed by EOF. The guardian helper enters the shared production guardian- bootstrap core, but its post-admission loopback rewrite and fixed observation root, the package role dispatcher, and the outer terminal remain test-specific. The test-specific dispatcher does not execute the production CALCIFER_INTERNAL_CODEX_SUPERVISOR_ROLE dispatcher/parser or persistent shell-anchor role and makes no parser coverage claim.

A separate non-ignored deterministic package fixture covers all seven closed production recovery checkpoints: startup queued, ready, active, suspended, retained quiescing, retained restore pending, and retained cleanup pending. It is designed to use the exact production coordinator, guardian, and session graph while replacing only official compatibility/provider behavior through a sealed cfg(test) capability seam and a strict owner-private wrapper. The fixture is credential-free and loopback-only; production builds do not parse its selector or compatibility override. A checkpoint is observation only: the test must first prove that it neither completes nor terminates the generation, then send the sole CFRCR request. The first four checkpoints are expected to end as failed-clean and the three retained checkpoints as completed-clean. This fixture is deterministic recovery evidence, not Codex-version compatibility evidence, and its fourth namespace proof also requires the identity-checked private compatibility stage parent to be empty. All seven cases passed three consecutive local runs from that candidate source; the cross-platform CI readback remains pending.

If the cfg(test) package harness observes exact retained evidence or otherwise cannot complete the four-proof cleanup gate, it emits one fixed, redacted failure subtype and terminates the libtest process with a fixed nonzero _exit-equivalent status while the Rust owners are still live. That test-only terminal failure runs no destructors, produces no signal-driven core dump, and closes the libtest descriptor table without running an unproved coordinator TERM/KILL fallback, setting a completion proof, deleting scratch, or reporting cleanup success. It replaces the former unbounded package-test park so hosted CI cannot hide the failure behind its job timeout; it is not production retained-owner behavior and grants no authority over descendants in another session. The regression test launches both an exiting helper and a deliberately parked helper behind a readiness handshake and bounded exact-child wait, then kills and reaps only that helper if the bound expires. Production guardian/anchor retained owners continue to park their concrete typed authority. A failed recovery-request attempt is reported only as a consumed attempt with an unknown transport boundary; shutdown failure is not described as a confirmed write-half close.

Inference count is a closed scenario expectation. Early deterministic checkpoints require zero model requests; retained deterministic checkpoints and the normal live-turn flow are designed to require exactly one bounded JSON POST /v1/responses with the synthetic model, stream=true, the pinned JSON/SSE media headers, and no authorization or ChatGPT account header. The typed call- count observation is joined as required harness evidence. A missing request when one is required, a duplicate request, or any malformed or credential-bearing request fails closed without logging a body or token. Usage/reset-credit requests retain their separate synthetic credential check.

The package harness records its internal cleanup fence when the generation starts. Every operation-phase wait is capped at one fixed recovery start, so drip progress cannot renew a per-phase timeout or consume the reserved cleanup budget. The harness then asks the guardian to recover before any exact-child termination fallback. Scratch is deleted only after four independent proofs: exact coordinator-child wait; the exact provider-release-only CFCMP\x01\r\n record followed by EOF, which is not session or shell success; absence of every reported known process group; and an identity-checked empty runtime with zero retained FD and socket references. The CI workflow runs contracts, official-tui-normal, and official-tui-recovery as independent Ubuntu 24.04 scenarios behind one stable aggregate gate. Package download, checksum verification, compilation, and exact libtest discovery finish before execution. Every one of the six exact probes then gets a fresh loopback-only network namespace with no native fallback after the runner proves an exact environment allowlist, no inherited socket FD, no supplementary groups, zero capabilities, and NoNewPrivs, and revalidates the frozen libtest, Codex, and launcher identities across the privilege boundary. This confinement covers Codex 0.144.4's unconditional announcement prewarm: the request may be attempted, but no DNS or non-loopback route exists and remote content cannot affect compatibility success. A separate macOS matrix entry explicitly reports all three hermetic scenarios as unsupported; it does not download Codex or run a native-network substitute. Historical native Apple-silicon runs remain functional evidence only. The watchdog bounds its direct command group, while descendants that deliberately create another session remain an explicit ephemeral-runner teardown boundary rather than a claimed process-tree cleanup.

Example human output:

codex@work [available]
  Codex
    primary: 41% used · 59% remaining (display) · 300m window · resets 2027-01-15T08:00:00Z
    secondary: 70% used · 30% remaining (display) · 10080m window · resets 2027-01-20T08:00:00Z
  reset credits: 2 available
    codexRateLimits · available · expires 2027-02-01T08:00:00Z
  observed 2026-07-15T12:34:56Z · fresh · codex_app_server
  compatibility compatible · Codex 0.144.4 · tested 0.144.4 · adapter 0.1.0-alpha.5

Stable JSON adds codex_version, adapter_version, and a compatibility object for every profile. The object reports compatible, incompatible, or unverified, the protocol name, and Calcifer's explicit tested-version set. Only compatible observations can contain authoritative usage; every failure still has availability: "unknown" and cannot authorize future failover.

The remaining percentage is explicitly display-only. Codex rounds the upstream used percentage, so displayed 0% remaining is not by itself proof that the provider rejected the account. Calcifer reports exhausted only when the structured response contains a recognized rateLimitReachedType; otherwise a rounded 100% result is unknown for failover purposes.

doctor remains credential-free. It checks the host and whether executables named codex and claude are discoverable on PATH; it does not execute them or read provider state.

update check is also credential-free and never opens the profile registry, provider configuration, or an authentication store. It reads only the public kazu-42/calcifer GitHub Releases API through fixed HTTPS hosts, selects the highest strict SemVer in exactly one stable or preview channel, and requires an immutable release. The command selects only the archive for the binary's exact Rust compile target; an unsupported target succeeds as target_unsupported instead of substituting a different ABI. It downloads only the bounded v1 manifest and SHA256SUMS, verifies their local bytes against the release-asset digests and each other, and does not download or claim to verify the archive itself. Network, schema, redirect, pagination, and integrity failures are non-zero; an absent channel succeeds as no_release_in_channel.

Example JSON envelope:

{
  "schema_version": 1,
  "command": "doctor",
  "calcifer_version": "0.1.0-alpha.5",
  "ok": true,
  "status": "warn",
  "checks": []
}

For structured doctor, auth list, auth verify, auth rename, auth remove --yes, status, and update check results, --json emits one JSON document on stdout. Rename reports action: "rename", whether the alias changed, the old and new local references, and the existing non-secret profile record. Remove reports action: "remove", removed: true, and the removed non-secret profile record; JSON removal is accepted only with explicit --yes. Interactive auth add, run, and resume reject --json because the official provider owns the terminal and mixing its stream with a Calcifer JSON document would break the contract. Identity JSON contains only Calcifer-local profile metadata and never the private fingerprint, identity-key ID, or provider account scope. Update JSON separates immutable-release and manifest-declared attestation publication evidence from locally verified manifest/checksum bytes, and always marks the un-downloaded archive not_downloaded. Usage and update failures emit one redacted JSON document on stderr with a non-zero exit code. Clap's standard --help and --version output remains text even when --json is present. Within schema version 1, existing field names and meanings will remain stable; new fields may be added.

Supervised exact resume and guarded failover

Linux can opt into the production supervisor for one existing thread. Without a pool, this is an exact same-profile resume only:

calcifer resume --experimental-supervised codex@work 01900000-0000-7000-8000-000000000001

To opt this invocation into guarded failover, name one enabled pool explicitly:

calcifer resume --experimental-supervised --failover-pool codex@fallback \
  codex@work 01900000-0000-7000-8000-000000000001

Both forms require a foreground TTY and reject --json, --last, implicit workspace selection, and every argument after --. A failure never retries through direct resume. When the monitored exact thread reports a typed usageLimitExceeded, the supervisor stops and reaps the exact process tree and restores the terminal before returning its private exhaustion disposition. The pool path then marks cached state as requiring revalidation and performs a new structured usage read under the same source-profile lease. Numeric exit code 75 from an ordinary provider exit, rounded 100%, stale data, authentication, network, timeout, malformed, or provider failures cannot authorize selection.

Each invocation walks the configured order at most once from the current profile, skips fresh authoritative exhaustion and profiles already used since the invocation's starting generation, and freshly revalidates identity and usage while reserving every candidate. A successful handoff preserves the Calcifer conversation ID, creates a new target-profile thread from the validated immutable source rollout with the same effective model, provider, approval, and sandbox settings, commits the new generation durably, and starts the official TUI without replaying the failed turn. A crash leaves a bounded transition journal; rerun the same explicit command and starting profile/thread to reconcile or attach that transition without minting an unbounded fork retry. Exhausted, unknown, busy, or cooldown-only pools stop actionably without looping.

Normal and single-wrapper-failure paths restore the terminal before they return. If both restoration authorities are forcibly killed while raw mode is active, the invoking shell or terminal emulator may need an explicit reset; reverting or restarting Calcifer cannot restore a dead process's terminal state.

Arguments after -- are arguments to the provider adapter's resolved, permission-checked codex executable; users do not supply an arbitrary executable. Account/provider-routing flags such as -c, --profile, --oss, --local-provider, and remote-routing options are rejected, as are -C/--cd, dynamic --enable/--disable feature overrides, and non-UTF-8 arguments that cannot be mediated safely. Calcifer forces profile-local file storage for both CLI and MCP OAuth credentials on every managed invocation. Existing pre-alpha profiles with the previous exact managed config remain usable because the per-invocation overrides are authoritative; new profiles persist both settings. Calcifer does not yet cryptographically verify binary provenance, so users remain responsible for installing the official CLI on a trusted PATH. Unimplemented commands fail as unknown commands rather than pretending to succeed.

Guarded failover semantics

"Token limit" can refer to different things. Calcifer's selection logic concerns a provider-reported usage allowance or quota window, not a model context window.

Failover follows conservative semantics:

  • It is disabled by default and limited to a user-created pool of explicitly authorized profiles.
  • A pool cannot cross provider or configured trust-domain boundaries.
  • Only authoritative, fresh exhausted state permits selecting another profile. A rounded display value of 0% remaining, authentication failure, provider error, network failure, unknown output, or stale status cannot authorize a switch.
  • A pool is traversed at most once per invocation and uses cooldown state to prevent loops.
  • Calcifer never hot-swaps credentials in a running process.
  • After the old child has stopped, the supervisor continues the same user-visible conversation under the selected profile. Internally, the handoff forks the validated source rollout into a new profile-local Codex thread, so the logical conversation stays stable while the provider thread ID changes. Calcifer never automatically replays the last command or prompt; a partially completed turn may already have changed files or external systems.
  • Before launch, Calcifer shows the local profile alias, provider, trust domain, and selection reason without exposing provider account identifiers.

Same-profile resume delegates the final operation directly to the official CLI in the selected home. Calcifer uses the pinned stable thread/list and thread/read(includeTurns=false) App Server projections only to capture and validate the opaque thread key; it never persists transcript content. Cross-profile continuation remains explicitly experimental because its upstream import field is experimental and stable Codex thread lookup is scoped to one CODEX_HOME. The guarded path uses a separate version-gated target-profile App Server to fork a validated source rollout into a new target-profile thread, then attaches the official TUI through the production supervisor. The handoff stays inside one configured trust domain, preserves one writer per rollout, and restores history without resubmitting a turn. See ADR 0001.

Provider direction

Capability Status Direction
Read-only environment diagnostics Implemented No credential access
Credential-free update check Implemented Strict stable/preview SemVer, exact compile target, immutable v1 manifest and checksum verification; no archive download
Codex profile isolation Implemented on Unix One CODEX_HOME per profile; official Codex login and refresh
Same-profile Codex resume Implemented on Unix for Codex 0.144.4 Tracked workspace head, explicit exact thread ID, or official --last; no prompt replay
Private Codex identity binding Implemented for 0.144.4 ChatGPT auth HMAC equality only; duplicate aliases and credential drift fail closed
Codex usage observation Implemented with bounded idle refresh, active monitoring, and post-stop revalidation Structured App Server response, explicit freshness/compatibility, disposable private cache; selection requires a typed active signal followed by a fresh supported read
Reset-credit visibility Implemented read-only Count and safe expiry/status detail; opaque IDs are redacted
Pinned supervised Codex integration Experimental exact resume and explicit guarded pool failover are public on Linux; Ubuntu 24.04 runs every exact 0.144.4 package probe inside a fresh loopback-only namespace and executes verified native provider bytes from a sealed close-on-exec memfd. macOS hermetic and descriptor-backed execution are explicitly unsupported resume --experimental-supervised enters the sealed production anchor with one explicit profile and canonical thread UUID; --failover-pool additionally enables one bounded pool traversal after typed exhaustion and fresh revalidation. Linux has no native-network or pathname-exec fallback; macOS runs no weaker substitute
Opt-in profile pools Private default-disabled registry on Unix; explicitly consumed by Linux supervised failover Immutable profile IDs, same provider and trust domain, live whole-pool identity validation, bounded atomic updates, one-pass traversal and cooldown
Cross-profile conversation handoff Experimental public Linux path Version-gated fork-by-path, durable lineage/transition journal, bounded crash reconciliation, source preservation, exact settings validation, and no prompt replay
Claude provider-managed profiles Linux lifecycle implemented auth add/list/verify/reauth/rename/remove and run use the exact 2.1.227 status schema, an isolated CLAUDE_CONFIG_DIR, conflicting environment removal, 0600 single-link credential validation, and journaled atomic rotation; Windows awaits ACL proof and macOS awaits a documented config-scoped Keychain isolation contract
Claude setup-token ingestion Deferred The official command prints but does not store the inference-only token; Calcifer requires a reviewed OS credential broker and no-echo recovery path first
Claude subscription OAuth replication Not planned No token copying, undocumented OAuth endpoint, or Keychain-name emulation
Mid-session account hot-swap or command replay Non-goal Unsafe side-effect semantics

Calcifer will prefer documented provider interfaces and official CLI behavior. Provider compatibility can break when a CLI or credential format changes; unsupported or ambiguous states must stop rather than guess.

The Linux, macOS, and Windows CI matrix still compiles and tests the portable surface. Managed registration is currently enabled only on Unix, where private directory/file modes are enforced. Windows registration fails closed until current-user-only ACL creation and recovery are verified.

Security model

Calcifer is a local profile manager and process wrapper, not a credential broker or sandbox.

Core invariants for implemented and future paths are:

  1. One process uses one immutable profile identity for its entire lifetime.
  2. Calcifer never copies managed credentials into global ~/.codex or global Claude state.
  3. Only official CLI authentication and refresh mechanisms are used.
  4. Secrets and opaque reset-credit identifiers never enter Calcifer logs, command arguments, diagnostics, telemetry, or real test fixtures.
  5. Unknown quota state and authentication errors never authorize a switch.
  6. State changes are permission-checked, atomic, bounded, and recoverable.
  7. Old rotated credentials are never restored over newer credentials.
  8. Credential-bearing environments are passed only to the selected adapter's validated executable, never to an arbitrary user-supplied command.
  9. A credential profile and a logical conversation have independent lifecycles; a handoff may move the conversation only between stopped processes in one explicit trust domain.
  10. Routing definitions are user-level, private, bounded, and default-disabled. Repository-local files cannot select a pool, and definition updates never launch a provider workload.
  11. Resume restores persisted history but never replays an interrupted prompt or tool action.
  12. Ambient Codex credentials, authentication/provider endpoints, alternate managed config/state paths, remote execution and connector credentials, test hooks, and transcript/trace paths cannot override a selected Calcifer profile.
  13. Repository-local Codex configuration cannot replace managed authentication, provider routing, dynamic feature policy, project-root discovery, or state locations; unknown future settings fail closed until reviewed.

File-based Codex credentials remain readable by the current OS user and the official Codex CLI; Calcifer is not an encrypted vault. Calcifer also does not sandbox the wrapped CLI, its hooks, or commands executed from the current repository.

See Architecture, ADR 0001: cross-profile conversation handoff, ADR 0003: supervised Codex session, ADR 0004: private routing registry, Provider compatibility, Security model, and Security policy before contributing to authentication, storage, process execution, or failover behavior.

Build from source

Prerequisites:

  • Rust 1.85 or newer
  • Git
  • The official Codex CLI for profile registration, launch, resume, or status
git clone https://github.com/kazu-42/calcifer.git
cd calcifer
cargo test --all-targets --all-features --locked -- --test-threads=1
cargo run -- doctor

Install the current pre-alpha binary locally:

make install-local
calcifer --json doctor

The default install prefix is ~/.local. Override it with make install-local PREFIX=/your/prefix. If ~/.local/bin is not on PATH, run ~/.local/bin/calcifer --json doctor or add that directory to PATH.

Binary releases

Starting with v0.1.0-alpha.3, Calcifer publishes pre-release archives for Linux glibc 2.35+ on x86-64/ARM64, macOS Intel/Apple silicon, and Windows x86-64 on the GitHub Releases page. Every release includes SHA-256 checksums and GitHub artifact attestations minted by the release workflow over the assembled release assets. Starting with v0.1.0-alpha.5, every platform archive also has a Minisign sidecar made inside the protected release-signing environment. The binaries are not yet code-signed or notarized.

The Linux binary can run on the supported glibc baseline, but destructive auth remove and interrupted-removal recovery additionally require Linux kernel 5.8 or newer. On an older kernel those operations stop before mutation; other non-destructive commands do not inherit this kernel requirement.

Download only the archive for your operating system and architecture, verify it before installation, and keep in mind that Calcifer is still pre-alpha. See the release and rollback runbook for exact checksum, attestation, install, uninstall, and recovery commands.

After the first immutable manifest-v1 release is published, inspect the exact channel and compile-target result before downloading an archive:

calcifer update check
calcifer --json update check --channel preview

The checker validates release metadata plus the downloaded manifest and checksum bytes. It intentionally leaves archive download, archive-byte digest verification, and installation as separate explicit operations.

The supported package-manager paths are intentionally explicit and do not create profile state during installation:

brew install kazu-42/tap/calcifer-preview
cargo binstall --only-signed --no-discover-github-token --disable-telemetry \
  --disable-strategies quick-install,compile calcifer@=<exact-version>

Use an exact preview version with cargo-binstall. Upgrades and rollbacks add --force; neither binary rollback nor uninstall rolls back profile state.

Development

rustup toolchain install 1.85.0 --profile minimal
make fmt
make lint
make test
make supervisor-msrv
make check

The CI workflow is configured for checksum-pinned GitHub Actions linting, formatting and Clippy on Rust 1.96, the stable Linux/macOS/Windows all-feature test matrix run serially because process, signal, environment, and PTY tests share process-global and kernel-mediated state, deterministic archive-package tests, an MSRV compile check, and the full library unit suite plus tests/supervisor.rs, run serially twice on Linux and macOS at Rust 1.85. Three Ubuntu 24.04 package jobs additionally download the architecture-specific official Codex 0.144.4 archive, verify its pinned SHA-256 digest and single binary, and run independently budgeted scenarios. contracts runs the complete #28 handoff probe plus the #54 live-turn one-SIGTERM App drain, setsid(2) descriptor/environment-isolation, and typed-monitor success/redacted-error probes. official-tui-normal is designed to exercise the production coordinator/guardian session, PTY, input gate, resize, and stop/resume path with the official remote TUI. official-tui-recovery independently targets #55's retained-cleanup recovery and four-proof deletion gate. Each official scenario has its own outer watchdog, and one stable aggregate job requires every matrix entry. Package retrieval and build happen before execution; each exact test executes in its own fresh loopback-only Linux network namespace with zero capabilities, no supplementary groups, no inherited socket, and no native fallback. macOS has no reviewed public containment primitive here, so its matrix entry reports the three hermetic scenarios as unsupported and runs no provider probe. Their completion endpoint is designed to cross real package-parent-to-coordinator and coordinator-to-guardian exec boundaries, with the parent configured to accept only the provider-release-gated exact frame followed by EOF. The guardian helper enters the shared production guardian-bootstrap core, but the test-only role dispatcher does not execute the production CALCIFER_INTERNAL_CODEX_SUPERVISOR_ROLE dispatcher/parser or persistent shell- anchor role. These are version-specific compatibility and recovery checks, not a sandbox or proof that arbitrary detached descendants are absent. Local Apple-silicon runs remain historical native functional evidence and are not a hermetic acceptance gate. See CONTRIBUTING.md for security-sensitive review expectations.

Roadmap

The current and next slices keep Codex profile isolation with no shared runtime home:

  1. Implemented: private Unix registry, profile-name validation, ownership markers, and atomic metadata writes.
  2. Implemented: auth add/list/verify/remove, private Codex identity binding, run, same-profile resume, profile leases, and structured on-demand status.
  3. Implemented: exact same-profile thread capture, crash reconciliation, no-argument cold restore, and journaled local profile removal. Safe reauth/re-key flows remain.
  4. Implemented: bounded observation caching, active monitoring, and authoritative post-failure revalidation without aggressive polling.
  5. Implemented experimentally on Linux: explicit same-trust-domain pools and fail-closed one-pass selection behind --experimental-supervised --failover-pool.
  6. Implemented experimentally on Linux: version-gated cross-profile fork, durable logical-conversation generations, bounded crash reconciliation, and official-TUI continuation without turn replay. Preserve one profile-local writer per lineage generation while hardening the opt-in path before any stable/default promotion.
  7. Add Claude only through provider-supported authentication and usage-observation surfaces.

Detailed gates and non-goals are tracked in docs/roadmap.md.

Contributing and security

Issues and focused pull requests are welcome. Please read CONTRIBUTING.md and the Code of Conduct.

Do not put credentials, tokens, auth.json, .credentials.json, full environments, account identifiers, or raw debug logs in a public issue. Report security vulnerabilities privately as described in SECURITY.md.

Acknowledgements

Calcifer's profile-isolation direction was inspired in part by Orca, an MIT-licensed project by Lovecast Inc. Calcifer's initial scaffold is an independent implementation and does not currently copy Orca source code. If upstream code is adapted later, its source revision and MIT notice will be recorded alongside the adapted code.

Independence and trademarks

Calcifer is an independent project and is not affiliated with, endorsed by, or sponsored by OpenAI, Anthropic, or the Orca project. Codex, Claude, Claude Code, OpenAI, Anthropic, and Orca are names or trademarks of their respective owners.

Users are responsible for complying with provider terms, organization policy, account-sharing rules, and local law. Calcifer must only be used with profiles the user owns or is explicitly authorized to use.

License

Calcifer is licensed under the MIT License.

About

Pre-alpha local-first Rust wrapper for isolated coding-agent CLI profiles; usage-aware failover is planned.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages