the whole system
How OVM works
Three parts, one rule. The client keeps every version installed and switches by moving a pointer. The verification decides which upstream releases are safe to offer. The machine is where that proof actually runs. The rule that binds them: silence is never success — a check that could not run must say so.
The client
Every installed version is an immutable directory under
~/.ovm/; the active one is a symlink. Switching moves
the symlink — instant, and rollback can't fail because nothing was
deleted. Launching reads local state only; all network work happens
in a detached background process, so a dead network can never delay
the tool you typed. OVM updates itself the same way, with an
automatic rollback if the new version can't answer a probe.
ovm hatch sets a machine up from nothing; your own
local builds install as dev:<label> and behave
like any other version.
The verification
Installing cleanly proves nothing — Codex 0.144.0 installed, launched, and couldn't execute a single command. So every new release is installed on real hardware and driven through real checks (a live tool call, sidecar presence, version swap, downgrade behavior) before the registry offers it. Fail and it's withheld; pass and it's benchmarked in the same pass and published to a feed. If it runs, it ships — and every objection is published, never silently dropped.
The machine
One always-on Mac mini carries the authenticated lane: signed-in accounts, a lived-in home, real macOS Gatekeeper verdicts, plus a Linux container leg. Only schedules and maintainer dispatches can reach it — no pull request, no outside code. It heartbeats every five minutes and is judged from elsewhere, so a dark machine can't look like a quiet week.
Want the mechanics, the gate ledger, and the trust model? Switch to in depth above.
IThe client
Everything lives under ~/.ovm/. Each product has a
directory of installed versions plus a current
pointer; ~/.ovm/bin/ holds the launchers on your PATH.
Nothing outside ~/.ovm/ is touched.
~/.ovm/ ├── bin/ // on your PATH │ ├── claude → ovm // multi-call launcher: argv[0] picks the product │ └── ovm // the control plane (a real binary, not a link) ├── products/codex/ │ ├── current → versions/rust-v0.145.0 // the pointer │ └── versions/ // immutable, all kept │ ├── rust-v0.145.0/… │ └── dev:my-fix/… // your own build, same treatment ├── self/ // OVM's own versions, same shape ├── cache/ // version indexes; never authoritative └── config.json
Switching is one atomic symlink replacement. The
version you leave stays on disk byte for byte, which is why going
back is guaranteed rather than a re-download and a hope. Your own
builds install as dev:<label> and switch
identically.
Launching resolves the pointer and hands your terminal straight to the real binary — nothing piped or rewritten; OVM waits and exits with the tool's own code. Anything that might touch the network runs in a detached child. A test points every upstream at a black-hole socket and fails if a launch takes over three seconds, so a synchronous fetch can't sneak back in.
Installing is a transaction: download to a private
staging directory, verify (declared lengths everywhere; publisher
SHA-256 / npm integrity where upstream ships them; Apple code
signatures checked on macOS before anything executes), then one
atomic move with a .complete marker written last.
Interrupted installs are invisible and reclaimed; two shells
installing the same version resolve to exactly one complete copy.
Install and activate are separate steps.
First run is ovm hatch: it installs
Claude Code, Codex and claudex, skipping anything already managed,
and offers to hatch you a companion — a one-off launch of Claude
Code 2.1.96, the last release that still answers
/buddy, leaving your current selection untouched. A
machine that already has these tools is not a fresh start:
ovm adopt <product> imports an existing install
as a managed version and leaves the original binary where it is.
ovm statusline puts Echo — the companion, the model,
context left, session spend — in Claude Code's status line, keeping
a copy of whatever status line it replaces. (ovm hatch
is the cautious one: it leaves an existing status line alone unless
you say otherwise.)
Your own builds are first-class, not a side door.
ovm install codex dev --dev mypatch --binary
target/release/codex imports a local build as
dev:mypatch; it lists, switches, launches and rolls
back exactly like a release, and auto-update never touches it.
--bundle imports a directory rather than one binary —
which is what Codex 0.144.0 and later need, since a bare
codex without its codex-code-mode-host
sidecar installs perfectly and then fails every shell command.
--link points at your build tree instead of copying
it, so each rebuild is immediately what dev:mypatch
runs.
Self-update treats OVM as its own product plus a
stable control plane at ~/.ovm/bin/ovm. A swap
snapshots state, moves the pointer, and probes the new plane — a
failed probe restores the exact prior selection, fault-injected at
every mutation stage in tests. Updates stage in the background on
one invocation and activate on the next, so no command ever pays
for a download.
| property | why it holds |
|---|---|
| instant switch | A pointer moves. Nothing is downloaded, rebuilt, or removed. |
| rollback always works | The previous version is still on disk, complete and immutable. |
| no launch latency | Foreground reads local state only; enforced by the black-hole-socket test. |
| no half states | Atomic publish, completion marker last; interrupted work is invisible. |
| self-update is safe | A new OVM must answer a probe or the prior selection is restored. |
| dev builds are first-class | Your local Codex build sits beside releases and switches the same way. |
IIThe verification
The premise: installed is not works. Codex 0.144.0
moved execution into a sidecar the installer didn't ship —
--version passed, login passed, every real tool call
died. So no release is offered until it proves it runs. The gate is
a growing ledger, each check added in response to a real failure:
| check | why it exists |
|---|---|
| install + launch | The baseline: invisible to clients until it installs and launches, per platform. |
| tool-use probe | A real session must round-trip echo <nonce> | rev. A fail vetoes. (The 0.144.0 lesson.) |
| strict sidecars | Every helper binary the release ships must be present and spawnable. |
| version swap | ovm use must flip active versions both ways. |
| schema diff | Codex state migrations are diffed between tags; breaking changes flagged before data is touched. |
| behavioral downgrade | The previous binary runs against state the new one migrated. Compatible behavioral evidence admits; failures veto; a static-only disagreement is published as a review note rather than holding a release that provably runs. |
| signature drift | Apple can revoke certs with no release attached, so every supported macOS binary is re-inspected daily (spctl/codesign, never launched). A real revocation moved the Codex floor to rust-v0.132.0. |
Evidence has three states — ok, fail, indeterminate — because "the probe could not run" is neither a pass nor a failure. Indeterminate blocks a new admission but never revokes a published version. Lanes of evidence combine with AND, never last-writer-wins: a pass is admission evidence, not an answer to another lane's recorded failure. And the registry defends itself: hostile-shaped upstream responses are refused, a mass-retirement circuit breaker bounds truncation damage, and no advertised tag may name a version the file doesn't publish.
Evidence reaches the client, not just the build.
A schema-skew guard compiled into a release only knows the Codex
migrations that existed the day it shipped, so every Codex stable
after that reported "indeterminate" until the next OVM release. The
deep run now publishes api/codex-skew.json beside the
version registry: the synced migration manifest plus the ladder's
observed downgrade and recovery verdicts. A served manifest extends
the compiled one, so a stale build keeps classifying new
migrations, and an observed verdict outranks the static guess — a
flagged migration the ladder actually passed stays quiet, and a
regression the ladder saw warns even when nothing looks breaking.
Indeterminate checks never interrupt a launch;
ovm doctor codex explains them, including which
observation applied and how fresh it is.
The benchmark rides the same pass, only on
versions that cleared the canary: spawn, interactive startup, and
time-to-first-response (with and without MCP), plus memory and
install footprint. A model identifier an old harness can't use is
recorded as incompatible, never as a slow sample. The
macOS lane runs authenticated against a lived-in store; the Linux
lane runs the same suite in a container; charts label what each
lane could and couldn't measure. Every verdict is also broadcast on
an Atom feed (/versions.atom) with WebSub push.
OVM's own releases run the same discipline in reverse: private tag → build + real-tool-use canary on the machine below → allowlisted source export → public rebuild with provenance attestation → draft → validation from a clean home → one irreversible publish. Published releases are immutable; moving "latest" is a separate, deliberate step. Everything is reproducible: public tests, tagged canary pairs, and every benchmark point carries the runner and run URL that produced it.
IIIThe machine
The authenticated lane runs on one always-on Apple Silicon Mac mini, because three things can't be faked on throwaway CI: signed-in sessions (real model answers on a real subscription), a lived-in home (cold starts on an empty runner flatter every tool), and macOS reality (Gatekeeper verdicts and cert revocations only exist on a real policy engine).
| job | fires | what it does |
|---|---|---|
| version watch | every 5 min | Resident daemon polls upstreams; a hit dispatches the deep pass; every tick pushes a heartbeat with the machine's vitals. |
| benchmark deep | new release · nightly | macOS native leg + arm64 Linux container leg: install, probe, benchmark; verdicts feed the gate; ledgers commit; site republishes. |
| gatekeeper watch | daily | Re-inspects every supported macOS binary for cert revocation — inspection only, never launched. |
| alpha canary | each OVM release | Release candidates run in a throwaway home; the token-bearing verdict job runs elsewhere. |
| status / prune / VM | manual dispatch | Housekeeping. Ledgers are never pruned; the Linux VM is disposable by design. |
Trust model: schedule and maintainer dispatch only. No pull request, fork, or outside code has any path onto the machine — the public repo's CI uses GitHub-hosted runners exclusively. Untrusted artifacts are quarantined from credentials, and the one irreducible exposure — executing real upstream releases with real auth, which is the product — is mitigated by scoped accounts, signature assessment before launch, and the container boundary.
Watching the watcher: the mini heartbeats every poll; an hourly job on GitHub-hosted infrastructure — deliberately not on the mini — judges pulse and vitals and opens an issue on staleness or degradation. A dark machine must not look like a quiet week, nor a crippled one like a healthy one.
The mini is a real computer, not a cleanroom — so every ledger row records host model, cores, memory, and load, and every published number carries the runner and CI run that produced it. The orchestration code stays private (runbooks for a standing machine help nobody but attackers); every job, trigger, and number is public and accounted for.
IVSupported products
Every tool OVM installs, switches, and launches — and, next to each one, exactly how far we have verified it. The second half matters as much as the first: coverage differs by product, and a blank space is not a score.
| Product | What it is | Source | Install | Versions | Latest | Coverage |
|---|---|---|---|---|---|---|
| Claude Code | Anthropic's agentic coding CLI | npm + native | ovm install claude | — | — | benchmarked |
| Codex | OpenAI's agentic coding CLI | GitHub Releases | ovm install codex | — | — | benchmarked |
| Pi | Earendil's coding agent | GitHub Releases | ovm install pi | — | — | install verified |
Version counts and latest versions are read live from /api/registry.json. A dash means the registry has not been reached, not that the product has no versions.
OVM also manages itself: ovm self install,
ovm self use, and ovm self update pin and
hot-swap OVM's own versions through the same immutable, attested
release pipeline as the products above. We still rate the
self-management surface alpha —
it is the newest part of the tool and earns its tier the same way
everything else here does.
What the coverage tiers mean
Benchmarked. Everything below, plus timed performance runs: a real authenticated session on dedicated hardware, driven to a real model response, recorded per version and per platform. These are the numbers on the benchmarks page.
Install verified. Every published version is tracked in the registry, and each new release is installed on real hardware and made to report its own version through the OVM launcher before it is admitted. A release that fails is withheld from the registry rather than offered to you.
Install verification proves the artifact downloads, unpacks with all its required parts, and starts. It does not prove every subcommand works. We label it as what it is rather than rounding it up to "verified".
An empty benchmark cell means not measured — never measured and slow. Absence of a number is absence of a test, and the two should never be confused when the missing number would flatter or damage a product.
Why Pi carries no benchmark numbers
Pi is not attached to a single model provider — you bring your own model and keys. Our benchmark lane deliberately runs on each provider's own subscription (OAuth) account, one per product, so every number reflects the session a real subscriber gets. There is no equivalent account to hold Pi to, so we do not benchmark it for now — we may in the future. Adding a column of numbers gathered a different way would make the comparison worse, not better.
How a version reaches you
Every product above shares the same pipeline: upstream publishes, we
discover it, verify it on real hardware at that product's coverage
tier, and only then admit it to the registry that
ovm install reads. The
verification section describes that in
detail, the client covers your side, and
/api/registry.json is the registry
itself.
Benchmarks is the data · releases are on GitHub · dev log is the write-ups · claudex is the unusual one.