Getting Started
This guide walks through the core ocx workflow — from running your first package to managing multiple versions. It assumes ocx is already installed. Each section builds on the previous one, but they can be read independently.
First-time setup
On a fresh install, your local index is empty — ocx won't find any packages. Pass --remote to query the registry directly. Once a package is installed, commands that operate on it (which, select, env, exec) work without the flag. To populate your local index for browsing and offline use, run ocx index update with the packages you need.
Quick Start
Most tools require a separate install step before you can use them for the first time. You configure a source, install the tool, then run it. For a one-off task, that overhead is wasteful.
ocx package exec collapses that into a single command. If the package is not in the local object store, it is downloaded automatically. Then the command runs inside an isolated environment containing only the package's declared variables — no ambient PATH pollution.
ocx package exec "uv:0.10.0" -- uv --versionThe binary is cached in the object store, so subsequent calls with the same version skip the download. Nothing else persists: no candidate symlink, no current pointer.
TIP
Use ocx package exec for one-off tasks or in CI where you want a reproducible, isolated invocation. For tools you reach for every day, read on.
Running multiple packages in a single invocation
Pass multiple packages before --. Their environments are merged in declaration order so all tools are accessible inside the subprocess.
A real-world example: Bun is a JavaScript runtime that complements Node.js. Bringing both runtimes together with a single command means both node and bun are on PATH without any manual setup:
ocx package exec "nodejs:24.0.0" "bun:1.3.0" -- bun --versionInstalling
Running ocx package exec re-resolves the package on every invocation. For tools you use across multiple sessions — compilers, interpreters, build utilities — you want a persistent, named install that is available offline and at a known, stable path.
ocx package install downloads the package into the content-addressed object store and creates a candidate symlink at ~/.ocx/symlinks/{registry}/{repo}/candidates/{tag}. That path never changes after installation. If two tags resolve to the same binary build, only one object lives on disk.
ocx package install "uv:0.10.0"Once installed, ocx package which --candidate returns the stable candidate path without re-resolving the package — useful for embedding in build scripts, IDE configs, or CI pipelines that need a fixed path pinned to a specific version:
ocx package install "uv:0.10.0"
ocx package which --candidate "uv:0.10.0"Like apt install vs a one-shot download
apt install cmake persists the tool system-wide. A one-shot download script runs it and leaves nothing behind. ocx package install is the former — persistent, versioned, offline-ready — but without root, without a system-wide footprint, and with multiple versions coexisting side by side.
CI workflows: ocx package pull
In CI pipelines, symlink management is unnecessary — you just need the binary at a known path. ocx package pull downloads directly into the object store without creating candidate or current symlinks. To inject resolved environment variables into the runner, use ocx --format json package env for machine-readable output, or ocx package env --shell=sh to emit export statements directly into the runner's environment.
See the object store and install symlinks sections of the user guide for the full three-store architecture behind this.
Switching Versions
Installing a package creates a candidate path that includes the tag: candidates/21. When you upgrade to version 25, the new candidate lands at candidates/25. Any config referencing candidates/21 keeps working — but also stays pinned forever, requiring a manual edit on every upgrade.
ocx package select introduces a second symlink level: current. It is a tag-free, floating pointer to whichever candidate you last declared active. Update it once with select, and every path referencing current picks up the new version automatically.
ocx package install --select combines installation and selection in one step:
ocx package install --select "corretto:21.0.0"
ocx package which --current "corretto:21.0.0"To switch between already-installed versions, or to remove the active pointer without uninstalling, use ocx package select and ocx package deselect:
Same pattern as SDKMAN and nvm
SDKMAN manages Java SDKs with sdk default java 21-tem — a current symlink that re-points to the chosen version without changing $JAVA_HOME. nvm's nvm alias default 20 is the same idea for Node. ocx applies this same pattern to any binary package — including Java itself, as shown above — with no plugin to install and no tool-specific version manager to learn.
Use --current paths in shell profiles and IDE settings
Because current never changes its own path — only its target — you can reference it once and forget it. When you run ocx package install --select corretto:25, current is re-pointed. Your IDE and shell pick up the new version automatically, with no config edits.
See path resolution in the user guide for the full comparison of object-store, candidate, and current paths.
Uninstalling
ocx package uninstall removes the candidate symlink and its back-reference from the object's refs/ directory. The binary itself is kept unless --purge is given and refs/ is empty after the uninstall.
ocx package install "uv:0.10.0"
ocx package uninstall "uv:0.10.0"If the package was also selected, pass --deselect to remove the current pointer in the same step, or run ocx package deselect separately first.
Uninstall removes the symlink, not necessarily the binary
The candidate symlink is removed immediately. The binary object in the object store is preserved unless --purge is given and no other references remain. To remove all unreferenced objects in one pass, use ocx clean.
See the object store section of the user guide for how the refs/ back-reference system makes garbage collection safe.
Environment
Tools are more than binaries. A compiler suite needs CC, CXX, and LD_LIBRARY_PATH. A runtime needs JAVA_HOME or PYTHONHOME. Keeping those variables in sync with installed paths manually is fragile and collision-prone when multiple tools share a shell.
ocx packages declare their environment variables in metadata.json. ocx package env resolves those declarations relative to the installed path and prints them as a JSON document. ocx package exec injects them into a clean child process automatically.
ocx package install "corretto:21.0.0"
ocx package env "corretto:21.0.0"Persistent shell environment
OCX activation is handled by $OCX_HOME/env.sh, which is written by the installer and sourced from your login profile. At shell startup it invokes ocx self activate --shell=sh via the installer-written absolute path, so activation works even before ocx is on PATH. The command emits three blocks: a PATH prepend to the OCX binary directory, shell completions, and an eval "$(ocx --global env --shell=sh)" call that exports your global toolchain's environment. Because the PATH entry points at the current symlink, your profile stays valid across upgrades — no manual edit required.
Composing environments from multiple packages
Pass multiple packages to merge their environments in declaration order. Variables of type path — like PATH — are prepended to any existing value; constant variables replace it entirely. The result is a complete, composed environment with no manual merging.
nodejs:24 contributes its bin/ to PATH; bun:1.3 contributes its own bin/. Both runtimes are available inside the subprocess without any manual export:
ocx package env "nodejs:24.0.0" "bun:1.3.0"See the ocx package env reference for the full flag set, including --shell mode for emitting eval-safe export lines.
Project Toolchain
When a repository needs a fixed set of tools — a compiler, a formatter, a linter — you can declare them once and lock them to exact digests. Every contributor and every CI runner then gets the same binaries automatically.
Three commands set up the toolchain:
ocx init
ocx add "cmake:4.2.0"
ocx run -- cmake --versionocx init writes a minimal ocx.toml. ocx add appends a binding, resolves the tag to per-platform digests, and writes ocx.lock. ocx run reads ocx.lock and spawns the command with the locked toolchain's environment — no manual export or PATH manipulation needed.
See Pin a project's tools in the User Guide for groups, lifecycle commands, and CI setup.
Next Steps
The sections above cover the everyday workflow. For deeper topics:
- User Guide — the three-store architecture, versioning and tag hierarchy, locking strategies, and authentication for private registries.
- Command Reference — full documentation for every flag and option. Covers global flags (
--offline,--remote,--index), output formats, and all subcommands. - Environment Reference — every environment variable ocx reads, including auth configuration for private registries.
Config file
No setup is required — OCX works out of the box without any config file. When you want to set a persistent default (like a private registry hostname), place a config.toml in $OCX_HOME/ (default: ~/.ocx/config.toml):
[registry]
default = "registry.company.com"Environment variables and CLI flags always override config values. For full details, see the Configuration reference.
ocx index catalog and ocx index list let you browse what is available in the registry before installing: