Skip to content

Configuration files

Keep the roles separate — ixt.toml is human intent, ixt.lock is the generated exact resolution, registry.toml maps short names.


Files and audiences

ixt has one user manifest, one generated lockfile, and one registry config. Keep them straight:

File Audience Purpose Where it lives
ixt.toml Users — the person installing tools Declarative list of tools to keep installed Project root, or $IXT_HOME/config/ixt.toml
ixt.lock Users + CI Exact resolved versions and binary assets generated from ixt.toml Next to the effective ixt.toml
registry.toml ixt maintainers + power users Map short names to repos (ripgrepBurntSushi/ripgrep) Built-in + $IXT_HOME/config/registry.toml + IXT_REGISTRY

The rest of this page focuses on ixt.toml and ixt.lock. The registry is covered on its own page.


Mental model — intent, lock, snapshot

ixt distinguishes three state surfaces, and you should keep them straight.

  • ixt.toml — the direction


    Declarative source of truth, checked into your repo, edited by humans. Describes which tools should exist and under what constraints (version ranges, backend, expose rules, injects).

    apply enforces it · upgrade obeys its bounds.

  • ixt.lock — the exact replay plan


    Machine-written lockfile generated from ixt.toml. Records exact package versions, binary release assets, and the manifest fingerprint.

    lock write creates it · lock check validates it · apply --locked --strict replays it.

  • envs/<installed-id>/ixt.json — the snapshot


    Per-tool metadata, written by ixt at install/upgrade time. Reflects exactly what is installed right now: resolved version, backend, injected packages, exposed bins.

    Treat it like a quasi-lockfile: ephemeral, machine-authored, regenerable.

Three rules follow from this split

  1. ixt.toml wins over the metadata. When they disagree, apply makes the metadata match the toml (install, reinstall, inject, uninject, reexpose, remove), never the other way around.
  2. ixt.lock freezes a resolution only when you ask for it. ixt lock write regenerates it from ixt.toml; ixt tool apply --locked --strict refuses stale locks before changing the machine.
  3. upgrade stays within the toml's bounds. If the toml pins with ==, upgrade refuses (and shows the latest as an FYI). If the toml declares a range, upgrade asks the backend to resolve within that range.

tool export is the one-shot bridge: it reads the current metadata and prints a toml a human can commit, preserving the user's original version intent (ranges, pins, or unpinned) per tool. Generated output uses explicit backend prefixes (@pypi:, @npm:, @gh:, @gl:); handwritten config may still use the shorter accepted forms.


ixt.lock

ixt.lock is generated TOML. It lives beside the ixt.toml that produced it: project manifests get a project ixt.lock, and the global manifest gets $IXT_HOME/config/ixt.lock.

ixt lock write
ixt lock check
ixt lock diff
ixt tool apply --locked --strict

ixt lock write resolves the effective ixt.toml without installing tools. The lock records a manifest fingerprint, exact versions, exposure and injection policy, and binary release asset metadata when a GitHub/GitLab binary tool is declared.

Use ixt lock check or ixt lock diff before CI materialization. Use ixt tool apply --locked --strict when the machine must match the committed lock exactly. Do not hand-edit ixt.lock; change ixt.toml, then regenerate the lock. Checks and strict preflight are local and stable when an upstream publishes a newer version; ixt lock write is the explicit network-resolving operation used to refresh the recorded versions and assets.


ixt.toml

ixt.toml is the declarative configuration file for managing tools. It can live in your project root or in $IXT_HOME/config/.

[tools]
"@pypi:ruff" = {}
"@pypi:mypy" = {}
"@npm:@biomejs/biome" = {}
"@gh:BurntSushi/ripgrep" = {}
[tools]
# Version pinning
"@pypi:ruff" = { version = "==0.5.0" }
"@gh:sharkdp/fd" = { version = "==10.2.0" }

# Exposure control
"@pypi:httpie" = { expose = ["http", "https"] }
"@gh:BurntSushi/ripgrep" = { expose = ["rg:ripgrep"] }

# Explicit backend
"@npm:prettier" = {}
"@gl:gitlab-org/cli" = {} # binary backend, GitLab host

Fields

Field Type Description
install string Underlying install spec when the TOML key is a declarative slot (e.g. ruff-old = { install = "@pypi:ruff" }). Omit it when the key itself is the install spec. Lets apply rebuild side-by-side installs of the same tool — see Recipes → side-by-side versions.
version string Version constraint. Exact pin ("==0.5.0") blocks upgrade; ranges (">=0.5,<1.0", "~=1.2") let upgrade bump within bounds and trigger an apply reinstall only if the installed version leaves the range.
expose list Exposure rules (see below)
inject list Extra packages injected into the tool's env
node_shim bool npm only. Set to false to disable the #!node → bun shebang rewrite that otherwise kicks in when the host has no node on PATH. Opt out if the tool really needs a real Node.js runtime.
runtime string npm only. Set to "node" to install with npm/Node instead of bun when ixt tool apply materializes the manifest. Omit it for the default bun path.
asset_pattern string GitHub/GitLab only. Forces the release asset selection, bypassing ixt's heuristic scoring. Placeholders: {version}, {tag}, {os}, {arch}. ixt tool export emits this field only for tools that were installed with a user-authored pattern (--asset-pattern) — patterns auto-derived from the resolved asset name bake in OS/arch and are deliberately dropped to keep the exported toml portable across machines.

Exposure rules

Exposure controls which binaries a tool makes available in $IXT_HOME/installed/bin/.

Built-in rules

Rule Description
__main__ The primary binary declared by the backend (console_scripts / bin entry). Default for most tools.
__eponym__ The binary whose name matches the tool name.
__all__ All binaries in the tool's environment (except runtime internals like python, pip).

Fallback chain

When you pass a single dunder keyword and it resolves to nothing, ixt falls back automatically:

  • __main____eponym____all__
  • __eponym____main____all__

This is why ixt tool install some-tool usually Just Works without any expose option — __main__ is the default, and the chain ensures at least one binary gets linked.

Explicit binaries

[tools]
httpie = { expose = ["http", "https"] }

Aliases

Rename a binary at link time with the syntax <binary-in-env>:<shim-on-PATH>:

  • Left side — the real file name inside the tool's isolated env (must match an actual binary that the package ships, e.g. rg for ripgrep).
  • Right side — the name of the shim created in $IXT_HOME/installed/bin/ (what the user will type on the command line).

Both sides must be portable basenames, not paths. ixt rejects separators, absolute paths, ./.., control characters, and reserved Windows device names before creating, replacing, or removing any shim.

For example, BurntSushi/ripgrep ships a binary called rg. The rule "rg:ripgrep" picks up that rg binary and exposes it as ripgrep on your PATH:

[tools]
"@gh:BurntSushi/ripgrep" = { expose = ["rg:ripgrep"] }

Left side must match a real binary

If the left side does not match any file shipped by the package, the rule is silently skipped — no shim is created. Always check the binary names inside $IXT_HOME/installed/envs/<installed-id>/ (or in the tool's release archive) before writing the alias.


Workflow: export → lock → commit → apply

ixt tool export > ixt.toml

Captures current state, preserving the original version intent you gave at install (range, exact pin, or no constraint) plus backend info for each tool.

Local installs are excluded

Tools installed via ixt tool install --from <path> are deliberately skipped — their source is a local directory that doesn't exist on other machines, so listing them in ixt.toml would break ixt tool apply everywhere else.

ixt lock write
ixt lock check

Resolves the manifest once and writes exact versions and binary assets to ixt.lock.

git add ixt.toml ixt.lock
git commit -m "pin dev toolchain"
# On another machine or in CI
ixt tool apply --locked --strict              # installs exactly what's locked
ixt tool apply --locked --strict --remove --yes # also removes anything not listed (CI)

ixt tool apply reference