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.
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.
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 resumeOn 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 --yesEach 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.
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-000000000001To 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-000000000001Both 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.
"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
exhaustedstate permits selecting another profile. A rounded display value of0% 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.
| 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.
Calcifer is a local profile manager and process wrapper, not a credential broker or sandbox.
Core invariants for implemented and future paths are:
- One process uses one immutable profile identity for its entire lifetime.
- Calcifer never copies managed credentials into global
~/.codexor global Claude state. - Only official CLI authentication and refresh mechanisms are used.
- Secrets and opaque reset-credit identifiers never enter Calcifer logs, command arguments, diagnostics, telemetry, or real test fixtures.
- Unknown quota state and authentication errors never authorize a switch.
- State changes are permission-checked, atomic, bounded, and recoverable.
- Old rotated credentials are never restored over newer credentials.
- Credential-bearing environments are passed only to the selected adapter's validated executable, never to an arbitrary user-supplied command.
- 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.
- 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.
- Resume restores persisted history but never replays an interrupted prompt or tool action.
- 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.
- 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.
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 -- doctorInstall the current pre-alpha binary locally:
make install-local
calcifer --json doctorThe 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.
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 previewThe 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.
rustup toolchain install 1.85.0 --profile minimal
make fmt
make lint
make test
make supervisor-msrv
make checkThe 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.
The current and next slices keep Codex profile isolation with no shared runtime home:
- Implemented: private Unix registry, profile-name validation, ownership markers, and atomic metadata writes.
- Implemented:
auth add/list/verify/remove, private Codex identity binding,run, same-profileresume, profile leases, and structured on-demand status. - Implemented: exact same-profile thread capture, crash reconciliation, no-argument cold restore, and journaled local profile removal. Safe reauth/re-key flows remain.
- Implemented: bounded observation caching, active monitoring, and authoritative post-failure revalidation without aggressive polling.
- Implemented experimentally on Linux: explicit same-trust-domain pools and fail-closed one-pass selection behind
--experimental-supervised --failover-pool. - 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.
- Add Claude only through provider-supported authentication and usage-observation surfaces.
Detailed gates and non-goals are tracked in docs/roadmap.md.
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.
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.
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.
Calcifer is licensed under the MIT License.