nem.toml
The manifest declaring a directory’s tools and environment variables. Meant
to be committed. nem finds it by walking up from the current directory to
the nearest nem.toml; the global twin lives at ~/.nem/nem.toml.
Example
[tools]
go = '1.27.0'
"dev:kubectl" = '1.36.3'
[env]
KUBECONFIG = '$HOME/.kube/config'This is the same shape nem use writes: bare version strings, single
quotes. go has no catalog prefix, so it resolves from the first catalog
(in configuration order) that carries it. "dev:kubectl" pins the package
to the catalog named dev — TOML requires quoting a prefixed key.
[tools]
- A key is
[<catalog>:]<name>. Without a prefix, lookup walks the configured catalogs first-match-wins, in configuration order. With a prefix, lookup uses that catalog only, and no other. - A package name appears at most once, prefixed or not — so
nem unuse <pkg>is never ambiguous about which entry to remove. - Values are the exact version string as it appears in the catalog. There’s
no
"latest"keyword and no ranges. - Parsing is strict: an unknown table or key — a typo like
[tool]— is an error, not a silent skip. - Only direct intent lives here. The resolved closure, including dependencies, lives in nem.lock.
[env]
Names must match ^[A-Za-z_][A-Za-z0-9_]*$ and must not be on nem’s
reserved-name list; a value supports $VAR/${VAR} expansion (unset
expands to empty) and $$ for a literal $, performed by nem itself, not
the shell. See Managing environments
for the full expansion and precedence story.
How versions resolve
nem use, nem unuse, and nem lock re-resolve the whole manifest every
time — there’s no sticky state carried over from the last resolution.
Leaving off a version means the first entry of the package’s catalog
versions list. Across platforms and dependents, one version per package
wins: the highest version required anywhere. A tool you pinned directly is
never silently overridden by a dependency that wants something else — that’s
a pin-conflict error instead. nem sync never resolves; stability between
manifest changes comes from nem.lock, not from the resolver remembering
anything.