mise.lock reference
mise.lock is a TOML file that mise lock and other lockfile-aware commands write. Generate entries with mise lock rather than writing them by hand. To create and update a lockfile, see Lockfile (mise.lock).
File format
For this request:
[tools]
node = "24"
hk = "latest"mise lock --platform linux-x64,macos-arm64 writes:
# @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html
lockfile_version = 3
[[tools.hk]]
version = "2.5.0"
backend = "packslip:github.com/jdx/hk"
specifiers = ["latest"]
[tools.hk."platforms.linux-x64"]
checksum = "sha256:449b83c35ac3a460f80a4c9a41ad99430add8cf6caa6ceadc27e810feac3647d"
url = "https://github.com/jdx/hk/releases/download/v2.5.0/hk-x86_64-unknown-linux-gnu.tar.gz"
signer = "sigstore-oidc:https://github.com/jdx/hk/.github/workflows/release.yml"
repository_ids = { repository = "922514152" }
[tools.hk."platforms.macos-arm64"]
checksum = "sha256:fabbed3a44e72c601759fa81d56f3c9403ca466853bdf77e87cc69d986cb151a"
url = "https://github.com/jdx/hk/releases/download/v2.5.0/hk-aarch64-apple-darwin.tar.gz"
signer = "sigstore-oidc:https://github.com/jdx/hk/.github/workflows/release.yml"
repository_ids = { repository = "922514152" }
[[tools.node]]
version = "24.21.0"
backend = "core:node"
specifiers = ["24"]
[tools.node."platforms.linux-x64"]
checksum = "sha256:6e1db87ef58b8819e5d5402eff1536491b18edd8eb7bee5ef7897876e88dc5ff"
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-linux-x64.tar.gz"
[tools.node."platforms.macos-arm64"]
checksum = "sha256:bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057"
url = "https://nodejs.org/dist/v24.21.0/node-v24.21.0-darwin-arm64.tar.gz"Each [[tools.<name>]] entry is one locked version of a tool. A tool has several entries when the config requests several versions or option variants. specifiers lists the requests that resolved to the entry, so node = "24" binds to 24.21.0. Platform metadata sits under a quoted key such as [tools.node."platforms.macos-arm64"].
The top level can contain:
| Key | Meaning |
|---|---|
lockfile_version | The format version; absent in version 0 files |
tool-stubs | Tool stubs whose entries the lockfile holds, as paths relative to the lockfile |
[conda-packages.<platform>] | Conda dependency packages, keyed by file name, each with url and checksum; platform entries refer to them through conda_deps |
[[tools.<name>]] | One locked version of a tool |
Format versions
lockfile_version | Adds |
|---|---|
| 0 (no field) | Versions, backends, options and platform metadata |
| 1 | specifiers, which bind each request to the entry it resolved to |
| 2 | npm and PyPI dependency graphs in sidecar directories |
| 3 | repository_ids for packslip tools |
New lockfiles use version 3. mise keeps an existing lockfile at its version during ordinary updates, so teammates on an older mise can still read it. A lockfile older than version 3 omits packslip repository IDs, and mise warns when a release provides them. A mise that is older than a lockfile's format refuses to read it.
Run mise lock --upgrade to move a lockfile to the latest format. It processes every configured tool and cannot be combined with tool arguments. mise lock warns when a lockfile uses an older format. Other lockfile-aware commands warn once a newer format has been out for six months: version 0 lockfiles after 23 February 2027, version 1 after 13 March 2027, and version 2 after 27 March 2027.
Entry fields
| Field | Version | Meaning |
|---|---|---|
version | 0 | The resolved version. mise compares it as an opaque string. |
backend | 0 | The backend that installs it, such as core:node or aqua:jqlang/jq |
options | 0 | Backend options that select the artifact, such as { swift_platform = "ubuntu24.04" }; entries match on options exactly |
specifiers | 1 | The requests that resolve to this version and option variant |
aube | 2 | { path, digest } of an npm tool's embedded-aube sidecar |
uv | 2 | { path, digest } of a PyPI tool's uv sidecar |
A shorthand keeps the backend recorded here after the registry moves the tool to another backend; see switching to a new registry backend.
Platform fields
| Field | Meaning |
|---|---|
url | Download URL of the artifact |
url_api | API URL of the same asset, for sources that need an authenticated API request to download it |
checksum | <algorithm>:<hex>, usually sha256:; blake3: when mise hashes the download itself, or the algorithm the aqua registry declares, such as sha512: |
provenance | The verification mise used: slsa, cosign, minisign or github-attestations; SLSA can carry its file URL as { slsa = { url = "…" } } |
install | "source" when the entry describes a source build (Node.js, Erlang) |
additional_artifacts | Further release assets extracted into the same installation, from additional_asset_patterns, each with its own url and checksum |
conda_deps | File names of the tool's dependency packages in [conda-packages] |
signer | packslip: the signer the project committed to, as scheme:identity |
attested_by | packslip: "repackager" when a repackager signed the packslip instead of the vendor |
repository_ids | packslip, version 3: the forge's repository ID from the signing certificate, such as { repository = "922514152" }; see renamed repositories |
mise still reads some fields that it no longer writes. size is ignored. provenance_verified is kept unchanged on unchanged artifacts but no longer read. github_attestations = "unavailable" is ignored, so mise probes for attestations again. An owner key in repository_ids is ignored and kept while the repository ID stays the same.
Option variants
A tool can have several entries for one version when its artifact depends on more than the platform key. Swift publishes a different Linux tarball per distro, so its entries record the distro:
[[tools.swift]]
version = "6.3.1"
backend = "core:swift"
options = { swift_platform = "ubuntu24.04" }
[[tools.swift]]
version = "6.3.1"
backend = "core:swift"
options = { swift_platform = "fedora39" }Because entries match on options exactly, a machine verifies only against the entry written for its own distro. Set swift.platform to make every Linux machine resolve the same artifact, and commit the entry it produces. A platform for which the vendor publishes no artifact is reported as skipped rather than locked; Swift 5.10 has no arm64 build for centos7, for example.
Build revisions in URLs
A platform url can pin a build revision of the same version. Precompiled Ruby keeps version = "3.3.11" and pins revision 3.3.11-1 in the URL:
url = "https://github.com/jdx/ruby/releases/download/3.3.11-1/ruby-3.3.11.x86_64_linux.tar.gz"See Ruby precompiled build revisions for why revisions exist and how to update older lockfiles.
Platform keys
A platform key is os-arch, optionally followed by a qualifier. mise lock writes these keys for a new lockfile, plus the current platform's: linux-x64, linux-x64-musl, linux-arm64, linux-arm64-musl, macos-x64, macos-arm64 and windows-x64. Choose others with lockfile_platforms or mise lock --platform.
- On a musl Linux system, the current platform's key ends in
-musl. - Bun adds
-baselineon x64 CPUs without AVX2, such aslinux-x64-baselineormacos-x64-baseline, and-musl-baselineon musl x64 without AVX2. - The deprecated
ubibackend appends itsexeandmatchingoptions and the downloaded file name to the key.
--platform accepts the qualifiers gnu, glibc, musl, msvc, baseline and musl-baseline.
Native dependency sidecars
Format version 2 and later store the dependency graphs of npm and PyPI tools in the package manager's own files, one directory per graph:
mise.lock
.mise/locks/pypi-black/24.10.0/pyproject.toml
.mise/locks/pypi-black/24.10.0/uv.lock
.mise/locks/npm-prettier/3.3.3/package.json
.mise/locks/npm-prettier/3.3.3/aube-lock.yamlThe entry records the directory, relative to the lockfile, and a SHA-256 digest of the native lockfile. Line endings are normalized to LF before hashing, so a Windows checkout with core.autocrlf=true verifies against the committed file:
[[tools."pypi:black"]]
version = "24.10.0"
backend = "pypi:black"
uv = { path = ".mise/locks/pypi-black/24.10.0", digest = "sha256:…" }Directory names follow the tool's spelling: pypi:black uses pypi-black/ and pipx:black uses pipx-black/. A tool with several option variants gets a hash suffix on the version directory, such as 1.0.0~1a2b3c4d/, and a recorded path does not change when another variant is added. Python graphs keep wheels for every platform, so one sidecar serves every machine.
mise lock and generate-mode automatic locking delete sidecar directories that no entry references. Merge-mode automatic locking writes sidecars but never deletes them, and cleanup never removes another lockfile's sidecars.
Sidecar locations
| Lockfile | Sidecar directory |
|---|---|
mise.lock or .mise/mise.lock | .mise/locks/ |
.config/mise.lock or .config/mise/mise.lock | .config/mise/locks/ |
~/.config/mise/mise.lock (global) | ~/.config/mise/locks/ |
mise.local.lock | .mise/locks/mise.local/ |
mise.test.lock | .mise/locks/mise.test/ |
Any lockfile not named mise.lock gets its own subdirectory named after it. If you ignore mise.local.lock in Git, ignore .mise/locks/mise.local/ too. In a monorepo with root lockfiles, sidecars follow the root lockfile.
If mise.lock is a symlink, mise resolves sidecar paths relative to the symlink's target and writes new sidecars beside it. A dotfiles repository that links each file individually therefore keeps new sidecars in the repository without extra symlinks.
Listing sidecars
mise lock --sidecars prints each existing lockfile's sidecar directory and the sidecars it references, without resolving tools or writing files:
mise lock --sidecarsmise.lock (sidecars in .mise/locks)
npm:prettier@3.9.9 aube .mise/locks/npm-prettier/3.9.9Add --json for scripts, such as a bot that must commit sidecars with mise.lock. Each lockfile reports lockfile, root and sidecars, and each sidecar has tool, version, graph (aube or uv), path, digest and exists (whether the native lockfile is on disk). Paths inside the current directory are relative; sidecars of a symlinked lockfile can be absolute paths outside it. Use --local or --global to choose lockfiles, as with mise lock.
Inspecting and editing sidecars
Tools that read pyproject.toml and uv.lock, such as Renovate, can inspect the Python sidecars if their scope includes the sidecar directories. aube-lock.yaml is aube's own format, not npm's package-lock.json, so scanners may not recognize its transitive dependencies.
After editing a sidecar, run mise lock to validate it and record the new digest, then run mise install --locked. Apart from line endings, any change, including formatting, changes the digest and the installation identity.
A plain mise install also accepts a valid edit when it needs to install the tool, and updates the digest through automatic locking. If the recorded installation already exists, it skips graph validation and only warns when the graph file is missing. mise install --locked rejects a digest that does not match.
To regenerate a missing or unreadable sidecar, run mise lock; the tool must be configured and its installer available. A locked install fails when a required sidecar is missing.
Complete lockfile generation
The default merge mode updates entries as tools are installed. The generate mode of lockfile_mode instead rebuilds the whole lockfile from the current requests and reuses unchanged artifacts:
[settings]
lockfile_mode = "generate"lockfile_mode does not turn on lockfile creation: run mise lock first, or also set lockfile = true.
During installation, a lock progress bar tracks background metadata downloads, hashing and verification. The first run can download artifacts for other platforms; later runs reuse unchanged entries, which reduces lockfile churn between platforms.
Set MISE_LOCKFILE_MODE=generate to try it for one command, and lockfile_mode = "merge" to switch back; no format migration is needed. generate is a trial: share feedback in Discussions, since the default may change in a later release.
Shared lockfiles
A full mise lock run removes entries for tools that the active configuration no longer declares. For a committed lockfile shared by profiles that declare different tools, turn that off with lockfile_auto_prune:
[settings]
lockfile_auto_prune = falseIt applies in both modes, including to mise lock --global, and it keeps only tools outside the active configuration. Configured tools are still refreshed, and their stale versions, option variants and dependency graphs are still removed. mise lock node already keeps tools outside its filter.
Which lock entry a command reads
mise matches each configured request against the entries of the lockfile that belongs to the config declaring the tool, comparing backend and options as well as the request. Supported backends then verify downloads against the recorded checksums.
If both ~/.config/mise/config.toml and the project's mise.toml declare hk, the project's definition wins and mise uses the project's lock entry. If the project's lockfile has no matching entry, mise resolves hk normally; it does not fall back to the global pin. If the project does not declare hk, the global definition and its lockfile apply.
In locked mode, a missing matching entry is an error, even for read-only lookups such as mise which hk --tool hk@latest and even when the tool is installed. Commands that bypass lockfiles on purpose, such as mise exec hk@latest, still do.
A command-line request does not change the config it overrides. For a project with node = "22", mise exec node@24 does not replace the project's pin; use mise use or mise upgrade to change it.
mise which --tool warns when the definition in effect has no matching pin but an overridden definition's lockfile does, whether that definition is global, in a parent project or environment-specific. A matching entry in an unrelated lockfile is not enough: the overridden config must declare the tool.