RFD-0025 — Toolchain architecture — oxup argv[0] dispatch; single-root state; rustup-style
Question
How does the Argon toolchain ship — what binary is installed, how do users switch between versions, where does state live, and how does CI integrate?
Decision
Single Rust binary with argv[0] dispatch. oxup is the toolchain manager binary. Proxy binaries (ox, oxc, ox-lsp) are symlinks to the installed oxup. When invoked as ox, oxup dispatches to the actual ox binary in the active toolchain; same for oxc and ox-lsp. oxup init creates the proxies post-install.
Single root for managed state: ~/.argon/. Toolchain installs go to ~/.argon/toolchains/<channel>-<version>/. The active channel symlinks via ~/.argon/toolchains/active. Cache and component state co-locate.
Rustup-style channel resolution. ox-toolchain.toml resolves walking up from CWD; the closest toolchain manifest wins. Two channels in v0: stable and nightly.
Toolchain tarball contents:
- Universal Mach-O binaries (lipo of arm64 + x86_64) for macOS; per-arch binaries for Linux.
stdsource bundle (RFD-0014).- Tree-sitter shared library.
- Shell completions.
- Manifest describing the contents.
Required components: core (oxc, ox, ox-lsp, std, tree-sitter, completions). Required components are always bundled; optional components (future) extend the manifest.
Auth strategy: gh CLI first. No oxup-managed credentials in v0; release pipeline auth flows through standard GitHub mechanisms.
Distribution: Homebrew tap at sharpe-dev/homebrew-tap (path corrected from earlier homebrew-ontolog which now redirects).
Release pipeline: argon-v* tag push on the monorepo triggers signing + notarization (macOS) + cross-platform tarball upload + tap auto-bump.
Diagnostics via oxup doctor: categorized checks (binary, filesystem, toolchain, auth, editor, configuration).
VS Code extension versioned lockstep with the toolchain. Extension and toolchain ship together; no version-skew compatibility matrix.
Telemetry: three levels — none, minimal (default), full. User-controllable; minimal collects anonymous usage signal only.
Offline mode: --offline flag and [network] offline = true setting in ox-toolchain.toml.
Nightly garbage collection: nightlies older than 30 days are deleted automatically.
Uninstallation: oxup uninstall removes everything oxup created — directory, rc-file edits (idempotent reverse), VS Code extension. Clean.
Shell integration: bounded edit region in user’s rc file with guard comments; idempotent; reverses cleanly on uninstall.
Rationale
argv[0] dispatch keeps the install surface small. One binary, three or four symlinks. No per-tool installer, no per-tool update mechanism, no version drift between ox and oxc.
Single root for state. All state under ~/.argon/. Backups, sandboxes, container-mounts handle one tree. Anti-pattern: state split across ~/.config/, ~/.cache/, ~/.local/, etc.
Rustup-style channel. Argon’s audience overlaps with Rust developers; the convention is familiar. Walking up from CWD lets per-project toolchain pinning work without environment variables.
Universal Mach-O. Apple Silicon and Intel Macs both work without per-arch downloads; the binary is bigger but the user experience is “one tarball just works.”
gh CLI for auth. GitHub’s auth machinery is mature, supports SSO and 2FA, and most contributors already have it. Reusing it avoids inventing parallel auth.
Homebrew tap. Mac users install via brew install. Linux gets the same tarballs from GitHub Releases.
30-day nightly retention. Long enough to bisect a regression; short enough that the cache doesn’t unboundedly grow.
Consequences
oxupowns: install / update / uninstall / channel switch / component management / doctor / proxy binary creation.- Release pipeline at
.github/workflows/argon-release.ymlruns onargon-v*tag push. - Tap repository at
sharpe-dev/homebrew-tap; auto-bumped on release. ~/.argon/toolchains/<channel>-<version>/for installed toolchains;~/.argon/toolchains/activefor the active symlink.- Shell integration touches the user’s rc file inside guard comments only.