How jrt picks a runtime

jrt makes two independent decisions: one for the project you are in, one for each tool you run. Understanding both explains everything else.

Project runtimes

When you run jrt exec -- <command> (or a shimmed node, python, …), jrt walks up from the current directory to the nearest .jrt.toml and uses the runtime pinned there. There is no global “active version” and nothing changes when you cd — the file decides.

For Node projects, package.json#packageManager is honoured too: jrt exec -- pnpm install runs that exact pnpm version under the pinned Node, and a mismatched package manager is refused so lockfiles are not rewritten by accident.

Machine tools

A machine tool is a CLI you register once with jrt tool add <alias> <source> and use from anywhere. At registration jrt resolves latest to an exact version and locks it in the registry. Each time you jrt run <alias>, it selects a runtime for that tool using the tool’s policy.

Policies

PolicyHow the runtime is chosen
auto (default) 1. a known-good compatibility rule for this package version, then
2. the project runtime, if the tool’s declared requirement accepts it, then
3. the highest installed runtime that satisfies the declared requirement.
strictThe compatible project runtime only. If the project runtime does not satisfy the tool, jrt fails instead of silently falling back.
pinnedExactly the runtime you set with jrt tool pin.
shell
jrt tool policy black strict       # only ever use the project runtime
jrt tool policy black auto         # back to the default
jrt tool pin black python@3.12     # always use exactly this runtime (policy: pinned)

Where requirements come from

jrt reads each ecosystem’s own metadata with that ecosystem’s own rules:

  • npm / Bun / Deno: engines.node with semver ranges.
  • PyPI: Requires-Python with PEP 440 semantics.
  • Go: the module’s go directive.
  • Cargo: the crate’s rust-version.
  • NuGet: the tool package’s target framework.
  • Maven: the class-file version inside the executable JAR.
  • RubyGems: required_ruby_version, including ~>.

Declared requirements are often too loose (“works on any Node ≥ 14”) or missing. Known-good rules — set locally with jrt compat set or distributed as signed bundles — take priority. See Compatibility rules.

Asking jrt why

jrt why <alias> prints the locked version, the policy, the selected runtime and the reason for it.

shell
jrt why black
# Tool: black
# Requested: pypi:black@latest
# Resolved: pypi:black@26.1.0
# Policy: auto
# Selected: python@3.13
# Reason: known-good compatibility override (local-user)
# Project pin: python@3.11

Isolation

Every tool is materialized separately for each package version × runtime pair: a dedicated virtualenv for Python, a separate install prefix for npm, a separately built binary for Go and Cargo, and so on. Downloads are shared through caches under JRT_HOME, but installed files are never shared between runtimes, so native add-ons built for one runtime never leak into another.