# mise resolves "24" to a moving target unless you lock it

> mise is a Rust CLI that installs development tools, loads project environment variables and runs tasks from a committed mise.toml, so a shell, an editor and CI all get the same setup. What the front page does not dwell on is that loose version strings are the default, that the Rust library target is explicitly not a public API, and that releases ship on consecutive days.

**jdx/mise** — dev tools, env vars, task runner. mise prepares your development environment before each command runs.

- Repository: https://github.com/jdx/mise
- Website: https://mise.en.dev
- Stars: 34,342 · Forks: 1,448
- Language: Rust
- License: MIT
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/jdx-mise

## A version like "24" selects a series, not a release

The single most important thing to understand about a mise.toml is what a version string means, and the answer is that it is a request for a series rather than a release. The quickstart puts it plainly: version requests such as "24" select a release in that series, and exact pins or a lockfile are what you reach for when you need everyone to use the same resolved version. A minimal project file looks like this:

```toml
[tools]
node = "24"

[env]
NODE_ENV = "development"

[tasks.hello]
description = "Print the project's Node.js version and environment"
run = '''node -e "console.log(process.version, process.env.NODE_ENV)"'''
```

So the comfortable default, node = "24", is the one that does not pin. Two people on the same commit can resolve to different patch releases, and CI can resolve to a third, and nothing in the file records which. Adding a tool later is `mise use python@3.14` from the project directory, while `mise use --global` sets a personal default, so the same loose-string policy applies to both scopes. If reproducibility matters to your build, the pinning is a step you have to add on top, not something the config format does for you.

## Releases are date-stamped and land on consecutive days

The release history explains a lot about how this project moves. v2026.9.15 was published on 27 September 2026, v2026.9.16 on 28 September, and v2026.9.17 on 29 September, each carrying a descriptive title: vfox tools in OCI images with faster shell prompts and safer dotfiles pattern matching, then per-tool libc for aqua tools with monorepo task path aliases and packslip pins that survive repo renames, then a self-update that waits 24 hours for new releases and verifies signed packslips. The crate version in Cargo.toml is already 2026.9.18 while the newest tag is v2026.9.17, so the manifest runs ahead of the last release. There is no long-lived branch or extended support line in that list, which means you either track the current tag or you vendor your own. The self-update note is worth internalising: upgrades are deliberately delayed by a day and gated on signature verification, so an unattended mise that updates itself will not move the instant a release appears, and the tool is checking signatures before it does.

## The library target is marked not a public API

mise is a Rust workspace with a binary and a library, and the manifest is explicit about which of the two is for you. The lib section points at src/lib.rs, sets doc = false, and carries the comment that it is not a public API, with runnable examples still getting checked. The supported surface is the command line. If your plan is to embed mise in a Rust program, call into its config resolution, or reuse its shim logic as a library dependency, the manifest is telling you that path is not on offer and that the internals may change. The workspace is split into twelve member crates, and their names say what the CLI actually does: mise-shim, mise-bootstrap, mise-cache-core, mise-sigstore, mise-settings, mise-agent-env, mise-interactive-config, mise-util, plus vfox and aqua-registry for tool sources and two brew crates. None of that is a stable interface. Treat mise as a program you invoke, pin its version, and do not build your own tooling on the crate.

## Shell activation is optional, and skipping it fails quietly

The design deliberately separates installing tools from putting them on your PATH. `mise exec` runs a tool for one command with no shell activation required, which is why the quickstart can demonstrate the whole thing with:

```sh
mise exec node@24 -- node --version
```

The trade is that nothing changes your shell until you add a line yourself. For an install from mise.run, activation means adding one of these to the matching shell config:

```bash
# ~/.bashrc
eval "$(~/.local/bin/mise activate bash)"
```

Then restart the shell and `node --version` works inside a project directory. The failure mode is well named in the docs: if `mise exec` works but `node --version` does not, the problem is shell activation, and `mise doctor` is the command to check it. That diagnostic exists because the broken state is easy to create and easy to misread, since every mise command still works perfectly while bare tool invocations fail. If a script in your project calls a tool directly rather than through `mise exec` or `mise run`, that script is the thing that breaks first.

## The Docker image bakes in node@lts and python@latest at build time

The published image is a two-stage build pinned to digest references, and its runtime stage is where the details are. It sets MISE_DATA_DIR, MISE_CONFIG_DIR and MISE_CACHE_DIR all under /mise, puts /mise/shims on the PATH, and sets MISE_CACHE_PRUNE_AGE to 10y, which keeps tool downloads inside the image layer for ten years unless you change it. It installs jq, python3-full and python3-pip as system packages, then runs `mise use -g node@lts python@latest` during the build, so the image ships with a global Node and Python baked in at whatever was latest when that image was built. The entrypoint is mise and the default command is --help. One more detail in the builder stage: the build runs `cargo build --release --ignore-rust-version`, while the manifest declares rust-version = "1.95". The image therefore does not enforce the project's own minimum Rust version, which is convenient for building and worth knowing if you derive your own image from it.

## Three version numbers live in one repository, and npm test is a stub

Auditing versions inside this repository means reading three different files, because the numbers do not correspond. The crate is version 2026.9.18 on edition 2024 with a minimum Rust of 1.95. The tags are v2026.9.15 through v2026.9.17. And package.json declares version 1.0.0, which belongs to the documentation site rather than to the binary: its scripts run vitepress for docs:dev, docs:build and docs:preview, plus showreel frame and video generation and a typecheck. The npm test script is a placeholder that prints an error and exits 1, so the real test surface is the Rust workspace with its e2e/ and e2e-win/ directories. The Node side is also where the pinned tooling lives: esbuild is pinned through resolutions at 0.28.2, typescript is on 7.x, and playwright-core is present for the docs imagery. None of that ships with the binary, so if you are pinning mise in a build, pin the tag or the crate, not the npm version.

## A committed mise.toml can set machine state, not just project state

mise has four surfaces, and they are not equally safe to inherit from a repository you just cloned. Tools install language runtimes with per-project versions. Environments set project environment variables and load .env files. Tasks run build, test and other commands with the tools and environment they need. Bootstrap declares machine setup, and the things it covers are system packages, dotfiles and services. The first three are project state. Bootstrap is machine state, and it is the one where a shared file reaches past the repository. The intended posture is incremental, since the docs say to use the parts you need and to start with one tool or task and add more to the same config, but a file you did not write is still a file that runs when you enter the directory, and the PATH shims mean a bare `node` may already be intercepted before you read anything. Read a repository's config before you cd into it, and know which of the four sections it touches.

## Conclusion

mise is the right tool when a repository's toolchain and environment should be declared once and shared by shells, editors and CI, and when you are willing to add pins or a lockfile to get reproducibility. It is the wrong tool when you need a long support window, when you need it as a Rust library rather than a CLI, or when a teammate's committed config touching bootstrap is not something you want running as you change directory. Before you commit, read the version resolution note, run mise doctor whenever a tool works through mise exec but not directly, and check whether your team's config already pins versions.

## FAQ

### What is mise used for?

mise manages development tools, environment variables and project tasks. You declare them in mise.toml, commit the file, and get the same setup in your shell, your editor and CI: tools with different versions per project, project env vars and .env loading, tasks that run with the tools and environment they need, and bootstrap for system packages, dotfiles and services.

### What does Mise toml do?

It is the file you commit to declare a project's tools, environment and tasks. A minimal one sets node = "24" under [tools], sets NODE_ENV under [env], and defines a named task with a description and a run line, after which `mise run hello` installs the tool if needed, loads the variable and runs the command.

### How do I activate Mise?

Activation is optional and per shell. For an install from mise.run you add one line to the matching config, for example eval "$(~/.local/bin/mise activate bash)" in ~/.bashrc, then restart the shell. If `mise exec` works but `node --version` does not, the docs point you at `mise doctor`.

### Can I install mise globally?

`mise use --global` sets personal defaults, as against `mise use` from a project directory, which adds the tool to that project's configuration. On macOS and Linux the install itself is `curl https://mise.run | sh`, and on Windows it is `winget install jdx.mise`.

### what is jdx mise

jdx is Jeff Dickey, the author named in Cargo.toml, and mise is the Rust CLI he maintains for dev tools, env vars and tasks, MIT licensed with its documentation at mise.jdx.dev. The workspace is split into member crates including mise-shim, mise-bootstrap, mise-cache-core, mise-sigstore and vfox.

## Sources

- [Official documentation](https://mise.en.dev)
- [Official README](https://github.com/jdx/mise#readme)
- [Project repository](https://github.com/jdx/mise)
- [Release notes](https://github.com/jdx/mise/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jdx-mise
