Documentation is currently for moon v2 and latest proto. Documentation for moon v1 has been frozen and can be found here.
Lockfiles
Lockfiles allow you to lock the exact version (and checksum) of every tool installed in a project, ensuring that all machines — your own, your teammates', and CI — resolve and install identical tools, regardless of when they run. If you've used package managers like npm, Cargo, or uv, the concept is the same, just applied to the tools themselves.
Lockfiles are currently unstable. The functionality works as documented, but behavior and the lockfile structure may change between releases. If you run into any issues, please report them!
Enabling lockfiles
Lockfiles are opt-in, and can be enabled with the
settings.unstable-lockfile setting in a .prototools file.
node = "22"
[settings]
unstable-lockfile = true
Once enabled, a .protolock file will be created in the same directory as the .prototools file,
the next time records are written (typically by proto install). This file
should be committed to your version control system, so that other machines can make use of it.
If the setting is later disabled or removed, the sibling .protolock file will automatically be
deleted the next time proto runs.
How it works
Scopev0.60.0
A lockfile is scoped to the .prototools configuration file in which the setting was enabled.
- The lockfile only tracks tools with versions defined in its sibling
.prototoolsfile. Tools installed ad-hoc (without a configured version) are tracked by the closest configuration file. - Versions defined in nested configuration files are not included in a parent's lockfile. To lock a nested configuration, enable the setting in that file as well.
- Multiple lockfiles can exist within a directory tree (for example, one per project in a monorepo), with each operating independently.
Before v0.60, a lockfile locked its directory and all of its subdirectories, similar to how package manager workspaces function, and defining nested lockfiles would trigger an error.
Environment scopesv0.61.0
When an environment mode is active (the PROTO_ENV environment
variable is set), every configuration file owns its own lockfile. A .prototools file is locked to
.protolock, while a .prototools.production file is locked to a sibling .protolock.production
file, instead of the 2 configurations sharing the directory's .protolock.
- Each lockfile only tracks tools with versions defined in its own configuration file. A version
overridden by an environment configuration is tracked in
.protolock.<env>, while versions inherited from.prototoolsremain in.protolock. - Tools installed ad-hoc (without a configured version) are tracked by the base
.prototoolsconfiguration. - Environment configurations inherit the
settings.unstable-lockfilesetting from the.prototoolsfile in the same directory, but can override it. - Lockfiles for environments that are not currently active are never loaded or modified.
.prototools -> .protolock
.prototools.production -> .protolock.production
Records for environment specific versions that were previously written to
.protolockare no longer used, and will be re-created in.protolock.<env>on the next install.
Records
For each tool, the lockfile stores a list of records, with each record representing an installation for a specific version requirement, operating system, and architecture. Records are grouped by the tool identifier using TOML's array of tables syntax.
# Generated by proto. Do not modify!
[[tools.node]]
os = "linux"
arch = "x64"
spec = "22"
version = "22.17.0"
checksum = "sha256:325c377fbb4e547444a5da404e474fda52eb0adb3bbafcd25eee38e5766a1cfa"
[[tools.node]]
os = "macos"
arch = "arm64"
spec = "22"
version = "22.17.0"
checksum = "sha256:29b8fc16b1d1b0b7d19d444b8fac9e6ca4b39ee01bd097756975f92a25deba71"
Each record supports the following fields:
spec- The version requirement that was requested, typically from a.prototoolsfile.version- The exact version that the requirement resolved to.checksum- A checksum of the installed artifact, prefixed with its algorithm. Builds from source and native installations may not have a checksum.os/arch- The operating system and architecture the record applies to. Since pre-built artifacts differ per platform, a record exists for each combination.backend- The backend the tool was installed with, if applicable.metadata- Additional key-value information about the installation, provided by the tool's plugin. v0.61.0
Records are created and updated when a tool is explicitly installed with
proto install, and removed when uninstalled with
proto uninstall. Do not edit this file manually — proto owns it entirely.
Version resolution
When a version requirement is being resolved — during an install, or when detecting versions for
proto run and shims — proto will first look for a matching record in the
applicable lockfile. A record matches when it has the same requirement (spec), backend, operating
system, and architecture. If a match is found, the exact version from the record is used,
instead of re-resolving the requirement against the tool's available versions.
This is what guarantees determinism: a requirement like node = "22" will keep resolving to the
locked 22.17.0, even after newer 22.x versions have been released. To break out of this and
re-resolve, pass --update-lockfile to proto install, which will skip the
lockfile during resolution, and update the record with the newly resolved version.
$ proto install node --update-lockfile
Checksum verification
When installing a version that has a lockfile record, proto will verify the installation against
that record: the checksum of the artifact must match the locked checksum, and the operating
system and architecture must line up. If they don't, the install fails with an error — ensuring
that the artifact installed on another machine (or in CI) is the same one that was originally
locked.
When a record is migrated to a new version (through the commands below), its checksum is removed, and will be re-populated on the next install.
For tools that distribute GPG detached signatures instead of a
hash, the record stores the fingerprint of the signer that was trusted, and a SHA-256 hash of the
verified artifact, in the form gpg:<fingerprint>:sha256:<hash>. v0.61.0
Orphaned recordsv0.61.0
proto install and proto uninstall will prune
orphaned records from the lockfiles they touch. A record is orphaned when its tool has a version
defined in the configuration file that owns the lockfile, but the record's requirement no longer
matches any of that configuration's requirements. This typically happens when a .prototools file
is edited by hand, instead of through proto.
# Was "22", is now "24", so the "22" record is orphaned
node = "24"
- Records for tools that the owning configuration does not define are ad-hoc installs, and are never pruned.
- Only lockfiles that have been loaded are pruned, so lockfiles for inactive environments are left untouched.
- Pruning runs after the install or uninstall, against configuration files as they stand at that
point. Installing an explicit version of a tool that is configured with a different version will
not leave a record behind, unless the version is pinned with
--pin.
This is the same staleness that
proto diagnosereports, so records it warns about are now removed by the next install or uninstall.
Immutable lockfilesv0.61.0
For reproducible installs — think CI, containers, or any environment that should never drift —
proto install supports an --immutable-lockfile flag (and a
PROTO_IMMUTABLE_LOCKFILE environment variable), which treats the lockfile as read-only.
$ proto install --immutable-lockfile
- Versions are resolved only from existing lockfile records, never from the remote registry.
- The lockfile is never created, updated, or pruned.
- If a tool being installed has no matching record, the install fails with an error, instead of resolving a fresh version.
This turns "the lockfile is out of date" into a build failure instead of a silent update. To populate the missing records, run the install again without the flag, then commit the changes.
This flag cannot be combined with
--update-lockfileor--pin, as both would modify the lockfile.
Lockfile-aware commandsv0.60.0
Beyond installation, lockfiles are integrated across the command line.
proto outdatedandproto statusdisplay a "Locked" column, showing the version locked for each configured requirement, and include alocked_versionfield in their--jsonoutput.proto outdated --updateupdates matching records when writing new versions to their respective configuration files, including versions defined in environment scoped configurations. v0.61.0proto pin(andproto install --pin) migrates matching records to the newly pinned requirement, whileproto unpinremoves them entirely. Both always keep the lockfile of the modified configuration in sync, even when another configuration (like an environment configuration) takes precedence for that tool. v0.61.0proto uninstallremoves records for the uninstalled version from all applicable lockfiles, as the version may be pinned in multiple configurations. When uninstalling all versions, the tool is removed from all applicable lockfiles. v0.61.0proto versionslabels versions that have a record with "locked", and includes alockedfield in its--jsonoutput.proto debug configrenders.protolockfiles alongside their sibling configuration files.proto cleandoes not remove records when cleaning stale versions, as the version may still be required by a configuration, or in use on other machines.proto diagnoseaudits the health of lockfiles, reporting warnings for records that are missing a resolved version, duplicate records for the same requirement, and stale records that no longer match a configured version.