technical overview
How OVM works
OVM keeps several versions of Claude Code, Codex, and Pi installed at
once and decides which one runs when you type cc. Everything
else — instant switching, rollback that always works, updates that never
make you wait — falls out of one design choice: versions are
immutable directories, and the active one is a pointer. This
page is the whole system, from the symlink under your shell to the
release pipeline that got the bytes there.
- Immutable versions, mutable pointer
- Installing writes a new directory and never touches an existing one. Switching moves a symlink. That is why a switch is instant and a rollback cannot fail halfway.
- The foreground never waits on the network
- Launching reads local state only. Version checks and downloads happen in a detached background process, so a slow or wedged network can't delay the tool you're trying to run.
- OVM manages itself the same way
- OVM is its own product: immutable snapshots, a pointer, an activation probe, and automatic rollback if a new version can't run.
01The layout on disk
Everything lives under ~/.ovm/. Each product gets a
directory of installed versions plus a current pointer, and
~/.ovm/bin/ holds the launchers that go on your
PATH. Nothing is installed system-wide and nothing outside
~/.ovm/ is modified.
storage layout
~/.ovm/
├── bin/ // on your PATH — these are what you run
│ ├── claude → ovm // multi-call launcher: argv[0] picks the product
│ ├── codex → ovm
│ └── ovm // the control plane (a real binary, not a link)
├── products/
│ └── codex/
│ ├── current → versions/rust-v0.145.0 // the pointer
│ └── versions/
│ ├── rust-v0.145.0/release/bin/codex
│ ├── rust-v0.130.0/release/bin/codex
│ └── dev:my-fix/dev/bin/codex // your own build, same treatment
├── self/ // OVM's own versions — see 05
│ ├── current → versions/0.0.3-alpha.7
│ └── versions/…
├── cache/ // version indexes; never authoritative
└── config.json
A version directory is complete or it does not count: each carries a
.complete marker written last, so an install interrupted
by a crash or a lost connection is invisible rather than half-usable.
02Switching versions
A switch is a single atomic symlink replacement. The versions themselves are untouched — the one you leave stays on disk, byte for byte, which is what makes going back an instant, guaranteed operation rather than a re-download and a hope.
ovm use codex rust-v0.145.0
Because ~/.ovm/bin/codex is a link to the OVM binary itself,
the same mechanism handles your own local builds. A patched Codex
installs as dev:my-fix and sits in the list beside the
released versions, so flipping between "what shipped" and "what I'm
hacking on" is the same one-second operation.
03What happens when you launch
The launch path is deliberately boring: resolve the pointer, then
exec the real binary so it inherits your terminal directly —
OVM does not sit between you and the tool while it runs. Anything that
might touch the network is pushed into a detached child that cannot
delay you.
launch path
This split is enforced by a test that points every upstream at a black-hole socket — one that accepts connections and never answers — and fails if a launch takes longer than three seconds. Reintroduce a synchronous fetch on the launch path and it goes red.
04Installing a version
Downloads are treated as untrusted until proven otherwise, and a version becomes visible only at the end, in one atomic step. There is no window where a partly-written version can be selected.
- Resolve. The version index says where the bytes are — a signed CDN binary, an npm tarball, or a GitHub release asset, depending on the product.
- Download into a private staging directory. Redirects are constrained, response sizes are bounded, and the archive is never extracted over a live install.
- Verify. Checksums are enforced, and on macOS the extracted binary's code signature is checked before it is ever executed.
- Publish atomically. The staged directory is moved into place and the
.completemarker is written last. A crash before that leaves an unreferenced directory, which is cleaned up — never a half-installed version. - Switch, if asked. Installing and activating are separate:
ovm installleaves your active version alone.
Concurrency is real here, not theoretical: two shells can install the same version at once. An install holds a lock, and if the owning process is killed mid-download the next one recovers the staging directory and completes it — the tested outcome is exactly one complete install.
05OVM updating itself
A version manager that can strand itself is worse than none. So OVM
treats itself as another managed product, with one addition: a
control plane. The binary on your PATH
(~/.ovm/bin/ovm) is a stable copy that survives any swap; it
hands off to whichever immutable snapshot is currently selected.
atomic self-update, with rollback
06Staying current without waiting
Updates are split across two invocations so that no command ever pays for a download. The first invocation notices work is due and stages the new version in the background; the next one activates it atomically and tells you it happened. If a staging attempt dies mid-download, the retry is immediate rather than waiting out the check window.
update lifecycle
07How a release reaches you
OVM's own releases go through a pipeline built on one rule: the bytes you download are built from the public source tag they claim to come from. Development happens in a private repository, but no private binary is ever copied into a public release — the public build is rebuilt and attested from the public tag, and a release is only published after the artifacts have been verified.
private development → public release
The same discipline decides which upstream versions OVM offers you: releases are held back until they provably run. That system is documented in methodology.
08claudex: Claude Code on GPT-5.6
One feature deserves its own diagram, because it routes your prompts
somewhere unusual. claudex runs the Claude Code interface
against OpenAI models through a local translating proxy, on your own
ChatGPT subscription. It is an unofficial integration and is opt-in.
where a claudex prompt goes
09The properties this buys
Each of these is a consequence of the design above rather than a feature bolted on top, which is why they hold even when things go wrong.
| property | why it holds |
|---|---|
| instant switch | Switching moves a pointer. No download, no rebuild, no reinstall — the other versions were never removed. |
| rollback always works | The previous version is still on disk, complete and immutable. Going back is the same pointer move, in reverse. |
| no launch latency | The foreground reads local state only; every network operation is detached and rate-limited. Enforced by a black-hole-socket test. |
| no half states | Versions are published atomically with a completion marker written last; interrupted work is invisible and reclaimed. |
| self-update is safe | A new OVM must answer a probe before it keeps the pointer; if it can't, the exact prior selection is restored automatically. |
| verifiable downloads | Checksums on every path, macOS signature checks before execution, and public builds attested to the tag they came from. |
| your builds are first-class | A local dev build installs as a normal version and switches identically, so comparing it against a release is one command. |
10Where to go next
Methodology covers the other half of the system: how upstream releases are verified, benchmarked, and published before OVM offers them to you. Releases is the live registry. The source, including the architecture notes this page summarizes, is on GitHub.