Skip to content

Runtimes and Ecosystem Tools ​

This page covers Node.js, Python, Java/JRE, Go, Rust, Maven, Gradle, and Kotlin. See JavaScript Package Managers for package managers and Sources and Supply-chain Security for arbitrary GitHub Release tools.

Common command forms ​

text
osdk install TOOL[@VERSION]... [-o|--opt KEY=VALUE ...]
osdk lock [TOOL[@VERSION] ...] [-o|--opt KEY=VALUE ...]
osdk upgrade [TOOL[@VERSION] ...] [-o|--opt KEY=VALUE ...]
osdk use|u TOOL[@VERSION] [-g|--global] [-o|--opt KEY=VALUE ...]
osdk exec (-t|--tool TOOL[@VERSION])... -- COMMAND [ARG ...]
osdk list|ls [TOOL]
osdk list-remote|lsr TOOL [FILTER]
osdk current [TOOL]
osdk where TOOL[@VERSION]
osdk uninstall|rm TOOL@VERSION

-o/--opt is repeatable and must be KEY=VALUE. It applies to every tool in the invocation; do not pass an option specific to one backend in a mixed-backend command.

When an existing structured tool is excluded from the current platform by when, osdk use TOOL@VERSION updates only its version and preserves when, lazy, and backend options. It does not bypass the platform condition to install the tool or write a lock entry for the current platform. -o/--opt cannot be changed in this case; update and verify backend options on a matching platform.

An inline option block is written tool[key=value,...]@selector, with the block before the @. Quote the whole operand on PowerShell: it treats an unquoted comma inside an argument as an array separator and splits one expression into two.

bash
osdk install 'npm:esbuild[installer=pnpm,allow_builds=true]@0.21'

Quote the value as well when it contains commas: allow_builds="a,b". When an unquoted operand does get split, osdk recognizes it and prints the correctly quoted command instead of complaining about an unterminated bracket.

Backend overview ​

BackendTool aliasesNative version filesInstall options
nodenodejs.nvmrc, .node-version, package.jsonarch, corepack
pythonpy, cpython.python-versionvariant, tag
javajdk, openjdk.java-version, .sdkmanrcdistribution, package-type
gogolanggo.mod, .go-versionnone
rustrustuprust-toolchain.toml, rust-toolchainprofile, components, targets
mavenmvn.mvn-versionnone
gradle—.gradle-versionnone
kotlinkotlinc.kotlin-versionnone
zig—.zig-versionnone

See Project version discovery for the full precedence and the current native-file boundary of no-argument commands.

Node.js ​

bash
osdk install node@20
osdk install node@20.19.0 -o corepack=true
osdk lock node@20 -o arch=arm64
OptionValuesEffect
archx64, arm64, x86, armSelect the Node artifact architecture; default is the host
corepack`true1

Without an explicit corepack option, [settings.node].corepack applies and defaults to false. Failure removes the incomplete installation. The Node shim owns node and corepack; independent npm or the matching Node installation provides coordinated npm/npx routing. The runtime environment supplies shared npm_config_cache only when the user has not set it.

arch can generate another architecture's lock section, but osdk has no download-only cross-architecture mode; actual installation rejects a Node artifact that cannot execute on the host.

Node merges version indexes from every reachable source while retaining the higher-ranked source's LTS metadata for duplicates. A responsive but stale mirror therefore cannot hide a release present on a later source; the archive download then verifies each SHASUMS256.txt candidate in the same order.

Node also supports global-package migration:

text
osdk node migrate-packages --from VERSION --to VERSION [--apply]
bash
# Plan only
osdk node migrate-packages --from 20.19.0 --to 22.17.0

# Apply the plan
osdk node migrate-packages --from 20.19.0 --to 22.17.0 --apply

Both versions must be installed, managed Node versions that contain npm. osdk enumerates the source with npm ls -g --depth=0 --json --long, skips npm itself, packages already present on the target, and packages declaring hasInstallScript=true or gypfile=true. --apply installs exact versions; on failure, it restores the target's original global-package set.

Python ​

python@3.14 is shorthand for default CPython. The complete request form is python@IMPLEMENTATION-VERSION+VARIANT:

bash
osdk install python@3.14
osdk install python@cpython-3.14+freethreaded
osdk install python@cpython-3.14+debug
osdk install python@cpython-3.14+freethreaded+debug
osdk install python@pypy-3.11
osdk install python@graalpy-3.12
osdk install python@pyodide-3.14
ImplementationSupported variantsIdentity examples
cpythondefault, freethreaded, debug, freethreaded+debug3.14.7, cpython-3.14.7+debug
pypydefaultpypy-3.11.x
graalpydefaultgraalpy-3.12.x
pyodidedefaultpyodide-3.14.x
OptionValueEffect
variantA variant aboveEquivalent to request suffix +VARIANT; the explicit option wins
tagA python-build-standalone date such as 20240224Select a historical PBS release for classic CPython

Implementation, exact Python version, variant, and catalog artifact are locked, and distinct identities can coexist. Standard CPython uses the embedded python-build-standalone index; multiple implementations, variants, and pre-releases use an embedded verified catalog. A custom catalog needs its exact SHA-256:

toml
[settings.python]
catalog_url = "/approved/python-catalog.json"
catalog_sha256 = "0123456789abcdef..."

catalog_url accepts HTTP(S) or a local path. Only a valid digest, schema, and checksum on every artifact can replace last-good. Refresh failure tries last-good and then the embedded catalog. See Pre-releases.

For classic CPython, osdk merges SHA256SUMS from every reachable source for the same release tag, preserving the higher-ranked entry for duplicate filenames. A first mirror missing one platform artifact can therefore be completed by a later source and enter normal verified download failover.

Interpreter discovery uses:

text
osdk python find [REQUEST]
bash
osdk python find
osdk python find pypy-3.11
osdk python find 3.14+freethreaded

Managed results are filtered by the optional request. osdk then scans PATH and system candidates, deduplicates them, and labels each as managed, PATH, or system. It returns an error if nothing is found.

Java JDK and JRE ​

bash
osdk install java@21
osdk install java@zulu-17.0.1
osdk install java@21 -o distribution=zulu -o package-type=jdk
osdk install java@21 -o package-type=jre
OptionValuesDefault
distributionA Foojay distribution ID such as temurin or zulutemurin
package-typejdk or jrejdk

The distribution can also be inline, as in java@temurin-21. Foojay lookup filters distribution, OS, architecture, archive type, JDK/JRE, and Linux libc. A JRE uses identity jre-<resolved-version> and can coexist with the matching JDK. Execution exports JAVA_HOME.

Temurin versions carry a build number (such as 21.0.12+8), and a PSU adds a fourth segment (such as 21.0.12.1+1). You may omit the build number in the request: java@21.0.12 matches the same-core 21.0.12+8, and only falls back to a four-part PSU when no same-core release exists.

The embedded Temurin LTS catalog covers 8, 11, 17, 21, and 25, allowing resolution with an empty metadata cache. A locked artifact remains installable when Foojay is unavailable. Configure a Foojay-compatible /packages endpoint or static mirror with:

toml
[settings.java]
catalog_url = "https://mirror.example/disco/v3.0/packages"

Unless that single explicit catalog is set, Java queries each ranked Foojay-compatible source. A reachable endpoint that lacks the requested distribution/JDK/JRE combination falls through, and checksum detail lookup uses the same order before the vendor archive enters the shared verified pipeline.

JVM tools ​

bash
osdk install maven@3.9.16
osdk install "gradle@=9.3.1"
osdk install kotlin@2.4.10

Gradle resolves from the upstream version index, so any historical release is installable: osdk list-remote gradle lists every finished release (179 measured on 2026-09-14), and each version's download URL and SHA-256 come from the index rather than being hardcoded per version. Nightlies, -rc-N and -milestone-N entries are classified as unstable, so latest only ever lands on a finished release; ask for a preview by name. An entry that publishes no checksum is refused rather than installed unverified.

Maven and Kotlin have no comparable machine-readable index upstream, so they stay a fixed catalog: Maven 3.9.16 with SHA-512 and Kotlin 2.4.10 with SHA-256; other versions fail. All three have their own installation directory and shims; Kotlin also has a GitHub proxy download candidate. Maven and Kotlin use the ranked effective source URLs. Gradle tries each source index and rebases the index's absolute distribution URL across all ranked download roots, so a source with a readable index but a missing target zip can still fall through.

Relationship to the Gradle wrapper

When a project has gradle/wrapper/gradle-wrapper.properties, the wrapper remains authoritative -- its distributionSha256Sum is a stronger guarantee than a version pin. An osdk gradle pin is for the cases without a wrapper, such as a new project or invoking gradle directly. The two agree on the digest: the index's checksum for 9.3.1 is byte-identical to the distributionSha256Sum a wrapper pins for it.

Go ​

bash
osdk install go@1.22
osdk use -g golang@1.23

Go has no backend-specific -o. osdk selects the host OS/architecture archive from the go.dev JSON index and verifies its SHA-256. Download candidates include go.dev, Aliyun, and golang.google.cn. Execution exports GOROOT and exposes go and gofmt.

Rust ​

The Rust backend delegates to rustup inside osdk's isolated directories:

bash
osdk install rust@stable
osdk install rust@nightly -o profile=minimal \
  -o components=clippy,rustfmt \
  -o targets=wasm32-unknown-unknown,x86_64-pc-windows-gnu
OptionValueDefault
profileAny profile accepted by rustupdefault
componentsComma-separated rustup componentsempty
targetsComma-separated rustup targetsempty

latest, lts, and system map to stable; stable, beta, nightly, and exact toolchains are otherwise passed through to isolated rustup. The rustup bootstrap itself uses minimal and installs no default toolchain. Runtime exports RUSTUP_HOME=<data>/rustup and CARGO_HOME=<data>/cargo. The lock preserves those floating channel names too, so reinstalling stable, beta, or nightly later may yield a newer toolchain. Use an explicit or dated toolchain when the result must be immutable.

The source probe ranks candidates using a generic stable manifest; it does not prove that a mirror has synchronized the requested exact toolchain, component, or target. Every downloading rustup operation runs the complete command against each ranked source in turn. A 404, transfer failure, or rustup rejection falls through until the command itself succeeds.

Exposed commands and cargo install ​

On install and on osdk reshim, osdk generates shims for every executable in the active toolchain bin and the isolated CARGO_HOME/bin, not just the five core launchers rustc, cargo, rustup, rustfmt, and clippy-driver. rustup proxies such as rustdoc, rust-analyzer, and cargo-miri, together with any CLI installed later through the managed cargo, are exposed as well. These shims still inject the isolated RUSTUP_HOME/CARGO_HOME at run time, so they can never reach a system rustup.

Third-party tools installed with the managed cargo install land in <data>/cargo/bin (not the system ~/.cargo); run osdk reshim once afterwards to make a new command directly callable from your shell. The cargo <subcommand> form (for example cargo tauri) needs no reshim, because cargo locates that launcher in the isolated CARGO_HOME/bin itself.

Management boundary versus a system rustup ​

osdk drives only the isolated rustup under its data directory and never takes over a rustup already installed on PATH:

  • Before osdk install rust has run, the isolated rustup does not exist; osdk rust * fails with an explicit message pointing at the install command instead of falling through to the system rustup.
  • osdk source pin rust <source> and the one-shot --source <source> change only the RUSTUP_DIST_SERVER injected when osdk drives the managed rustup. Neither edits an external rustup, its environment, or its configuration; when no managed Rust exists the command additionally prints this scope note. To mirror a system rustup, set RUSTUP_DIST_SERVER, RUSTUP_UPDATE_ROOT, and related variables yourself.
  • Every managed operation that downloads (osdk install rust, osdk rust component add, osdk rust target add) shares one source selection, so a pinned source also applies when you add a component or target later. A pin only puts that source first; an unavailable target still falls through to the remaining candidates.
  • The managed rustup always uses the source osdk selected: a RUSTUP_DIST_SERVER or RUSTUP_UPDATE_ROOT already exported in your shell does not affect managed operations, so an external mirror cannot override osdk's choice. To use a different source for one run, pass --source <source> instead of exporting those variables.

Components and targets ​

text
osdk rust component add NAME [--toolchain TOOLCHAIN]
osdk rust component remove NAME [--toolchain TOOLCHAIN]
osdk rust component list [--toolchain TOOLCHAIN]
osdk rust target add NAME [--toolchain TOOLCHAIN]
osdk rust target remove NAME [--toolchain TOOLCHAIN]
osdk rust target list [--toolchain TOOLCHAIN]

--toolchain defaults to stable; all commands act on isolated rustup.

bash
osdk rust component add rustfmt --toolchain stable
osdk rust target add wasm32-unknown-unknown --toolchain stable

Status, overrides, and local toolchains ​

text
osdk rust check [--repair]
osdk rust override import [PATH]
osdk rust override export [PATH]
osdk rust toolchain link NAME PATH
  • check runs isolated rustup check; --repair creates missing markers for real toolchain directories and removes markers with no toolchain.
  • override import reads isolated rustup's override for PATH (default current directory) and writes it to the nearest project osdk.toml.
  • override export writes the active osdk Rust pin for that directory as an isolated rustup directory override.
  • toolchain link requires a canonicalizable PATH containing bin/; NAME cannot contain whitespace or slashes or equal . or ...

A linked toolchain can run through shims and activation, but it is a machine-local path and osdk lock rejects it as a reproducible artifact.

Zig ​

bash
osdk install zig@latest
osdk use zig@0.16
osdk exec --tool zig -- zig version

Versions come from ziglang.org/download/index.json, which lists every release with its archive URL and SHA-256 together, so each install is checksum-verified and can be locked. Zig's GitHub releases carry only source and bootstrap archives, so the generic github: backend cannot install it. Although the index contains absolute tarball URLs, osdk rebases their relative release path across the ranked source download roots. A mirror whose index works but whose target archive is missing, corrupt, or not extractable therefore falls through.

master is a rolling nightly, not a release. It is treated as a prerelease, so zig@latest always selects a tagged version; request it explicitly with osdk install zig@master under a permissive prerelease policy.

Zig as a C and C++ cross compiler ​

zig cc and zig c++ are Clang drivers that ship their own libc: musl, several glibc versions, mingw-w64 and wasi-libc are all bundled. One install therefore cross-compiles to many targets with no per-target sysroot, which is exactly what the Android NDK cannot do outside Android.

bash
# Same source, no extra downloads, no sysroot to point at.
osdk exec --tool zig -- zig cc -target aarch64-linux-musl -o hello hello.c
osdk exec --tool zig -- zig cc -target x86_64-windows-gnu -o hello.exe hello.c

Verified on a Windows x86-64 host with a source that includes <stdio.h>; every target below produced a binary for the right machine:

-targetOutput
x86_64-linux-gnuELF, x86-64
aarch64-linux-gnuELF, aarch64
x86_64-linux-muslELF, x86-64, static
aarch64-linux-muslELF, aarch64, static
riscv64-linux-muslELF, riscv
x86_64-windows-gnuPE
wasm32-wasiwasm

Zig can also stand in for a C compiler in other build systems by pointing CC at it, which is useful for Rust crates with C dependencies:

bash
osdk exec --tool zig -- cargo build   # with CC="zig cc" in the environment

ZIG_GLOBAL_CACHE_DIR is set to a directory inside osdk's cache so build artifacts do not accumulate in your home directory. An existing value is left untouched.

Released under the MIT License