Canonfig
Explanation

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.json

The 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.tools entries 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:

ReasonCause
(prose)A command appears in Markdown prose, not in a code block. Canonfig never treats prose as an install command.
incomplete-declarationA canonfig.tools entry is missing its ecosystem or name. The missing fields are listed.
ambiguous-executablePATH 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 hasWhat the plan does
The tool is missinginstall-tool with the pinned version
The tool is present, and its installer reports the pinned versionLeaves it in place
The tool is present, and its installer reports another versioninstall-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:

  1. The directories where this tool's recipes install on this platform:

    MethodDirectories searched
    uv$XDG_BIN_HOME, then $XDG_DATA_HOME/../bin
    npmthe npm prefix bin and the Node directory; also %APPDATA%\npm on Windows
    pnpm$PNPM_HOME
    bun~/.bun/bin
    cargo~/.cargo/bin
    brewthe Homebrew prefix bin
    winget%LOCALAPPDATA%\Microsoft\WinGet\Links
  2. ~/.local/bin.

  3. PATH. In a scheduled run, that is the native job's PATH.

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

  • uv never downloads a Python interpreter for Canonfig, even when your environment allows it. Install Python on the Follower Machine first.
  • Canonfig uses https://pypi.org/simple unless the recipe declares an indexPolicy with a full HTTPS simple-index URL (no credentials in it), plus reviewedBy and reviewedAt.
  • 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-Python range, 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

MessageCauseFix
"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.

On this page