How Canonfig installs and verifies tools
Understand how Canonfig finds, installs, and verifies tools, so your Installation Recipes behave the same on every Follower Machine.
A tool resource tells every Follower Machine which command-line tool it needs
and how to install it on each platform. Canonfig installs the exact version you
pin, leaves a correct installation alone, and checks the result with the
verification you declare.
Tool installs are not rolled back. A package that a run installed stays installed even when a later action in the same run fails. See the transaction boundary.
Find tools with source scan
canonfig source scan is the only discovery command, and it finds tools only.
Write file, directory, config, and skill resources in the profile file
yourself; see
Author file, config, and skill resources.
canonfig source scan --file ~/.codex/AGENTS.md --file ~/.claude.jsonThe scan reads only the files you name, and runs nothing: no package manager, no shim, and no discovered tool. It looks for:
- shell code blocks in agent instruction Markdown;
- hook and MCP server configuration;
- JSON, JSONC, TOML, shell, Brewfile, winget, and package metadata files;
canonfig.toolsentries in package metadata;- executables found on
PATH.
A proposed tool needs an upstream URL and at least one way to verify it. When
the evidence is not enough, the scan marks the tool needs-review and says why:
| Reason | Cause |
|---|---|
| (prose) | A command appears in Markdown prose, not in a code block. Canonfig never treats prose as an install command. |
incomplete-declaration | A canonfig.tools entry is missing its ecosystem or name. The missing fields are listed. |
ambiguous-executable | PATH holds several different copies of one executable. Each is listed with its path, version, modification time, and digest. |
uvx <package>, uvx --from <package> <command>, uv tool install, and
uv tool run with a pinned package (== or @) become uv recipes. MCP
servers get executable-present verification, so checking one never starts the
server.
Review the Profile Change Proposal, then publish it with
canonfig source publish --proposal.
Write a recipe for each platform
A tool resource lists one Installation Recipe per platform it supports:
{
"id": "ripgrep",
"kind": "tool",
"target": "rg",
"spec": {
"kind": "tool",
"toolId": "ripgrep",
"recipes": [
{ "platform": "linux", "method": "apt", "package": "ripgrep", "version": "14.1.0-1" },
{ "platform": "macos", "method": "brew", "package": "ripgrep", "version": "14.1.1" },
{ "platform": "windows", "method": "winget", "package": "BurntSushi.ripgrep.MSVC", "version": "14.1.1" }
],
"login": { "required": false }
},
"verify": {
"method": "command",
"command": ["rg", "--version"],
"expectContains": "ripgrep 14.1"
}
}Package names differ between package managers. Look up the real name for each platform; do not reuse an npm package name as a winget ID, or a Homebrew formula name as an apt package.
Every automatic method (npm, pnpm, bun, brew, apt, winget, uv,
cargo) needs an exact version. Publishing refuses a recipe without one, so
you find the mistake on the Source Machine instead of on each Follower Machine:
installer uv requires an exact "version" so every follower installs the same build
A Follower Machine with no recipe for its platform cannot install the tool.
The resource needs Human Action Required, unless the tool declares
agentInstall and the Follower Machine's agent policy is agent-apply; see
Resource kinds.
When Canonfig installs, replaces, or leaves a tool
target names the installed tool: a command name or the path to its entry
file. Planning checks that target, not the first word of the verification
command, which can be a shared runtime such as node or python.
| What the Follower Machine has | What the plan does |
|---|---|
| The tool is missing | install-tool with the pinned version |
| The tool is present, and its installer reports the pinned version | Leaves it in place |
| The tool is present, and its installer reports another version | install-tool with replaces set to the installed version. Apply installs the pin, downgrading if needed. |
| The tool is present, but its installer does not list it (for example it came from somewhere else, or Homebrew) | Leaves it in place |
To learn the installed version, planning only reads installer state, for example
uv tool list --offline, npm ls --global --json, winget list --id <id> --exact, or dpkg-query. Planning never runs your verification command.
After an install, the installer must report the pinned version. Otherwise the action fails:
installer
<method>exited successfully but reports<package><installed>installed, not the pinned<version>
Presence alone is not proof that a tool works. After apply, Canonfig runs the declared verification on every tool, including ones it left in place.
Verification lookup
A bare command name in a verifier, such as ruff, is looked up in this order:
-
The directories where this tool's recipes install on this platform:
Method Directories searched uv$XDG_BIN_HOME, then$XDG_DATA_HOME/../binnpmthe npm prefix binand the Node directory; also%APPDATA%\npmon Windowspnpm$PNPM_HOMEbun~/.bun/bincargo~/.cargo/binbrewthe Homebrew prefix binwinget%LOCALAPPDATA%\Microsoft\WinGet\Links -
~/.local/bin. -
PATH. In a scheduled run, that is the native job'sPATH.
apt installs into system directories that every PATH already contains.
So a tool that uv installed into ~/.local/bin verifies even when your shell
or the scheduler does not have that directory on PATH. When the executable is
not found, verification fails and lists every directory it searched:
verification failed for resource
<id>: verification executable ruff was not found (searched: …)
uv recipes
uvnever downloads a Python interpreter for Canonfig, even when your environment allows it. Install Python on the Follower Machine first.- Canonfig uses
https://pypi.org/simpleunless the recipe declares anindexPolicywith a full HTTPS simple-index URL (no credentials in it), plusreviewedByandreviewedAt. - Canonfig ignores your uv configuration files and active virtual environments, and installs only prebuilt packages under the default build policy.
- Canonfig checks the package's full
Requires-Pythonrange, including upper bounds, before installing. An incompatible package fails with uv's message and no tool is installed.
Install tools during canonfig setup
canonfig setup plan --role <source|follower> --file <path> runs the same
discovery on the files you name. An accepted recipe becomes a plan item only
when its package manager is present on the machine.
setup plan changes nothing. It does not let Corepack or rustup download a
toolchain. A cargo that rustup provides is reported unverified until rustup
has a default toolchain, and setup apply fails that optional item with
"run rustup default stable".
After canonfig setup approve, canonfig setup apply runs the approved
recipes, verifies each one, and records the result. A failed optional recipe
does not block unrelated required work. Unknown or ambiguous recipes stay
excluded for you to review; setup does not hand them to an agent.
Tools that need a login
A recipe can declare that the tool needs a login, with instructions:
"login": { "required": true, "howTo": "Run gh auth login and choose GitHub.com" }Canonfig never copies login credentials from the Source Machine and never
treats a login as something it can complete. When the Follower Machine has no
usable local credential, the resource needs Human Action Required and shows
your howTo text.
Never put credentials in a recipe
Recipe arguments, verification commands, and profile content are published
to every Follower Machine. Keep tokens, passwords, private keys, cookies, and
invitation files out of them. Use a credential resource or opt-in shared
secrets instead.
Tools without a usable recipe
When the evidence does not select one safe recipe, discovery creates an Agent Task with the upstream URL and the evidence. A Configuration Agent can propose a recipe, but cannot publish it. You review and publish the change yourself.
If something fails
| Message | Cause | Fix |
|---|---|---|
"installer <method> requires an exact "version" so every follower installs the same build" | A recipe has no version. | Add the exact version and publish again. |
"installer <method> exited with <code>; its last output lines: …" | The package manager failed. Up to 20 lines of its output follow, with secrets removed. | Fix the cause the output names, then run canonfig sync --apply again. |
"installer <method> exited successfully but reports <package> <installed> installed, not the pinned <version>" | The package manager installed a different version. | Check that the pinned version exists for that package manager. |
"verification executable <name> was not found (searched: …)" | The tool is not in any searched directory. | Check the recipe's install location, or verify with an absolute path. |
"run rustup default stable" | rustup has no default toolchain, so cargo cannot run. | Run rustup default stable, then run canonfig setup apply again. |
On Windows, .cmd and .bat shims such as npm.cmd cannot run without a
shell. Bind the package manager to its JavaScript entry point with
canonfig installer set; see
Windows package managers.