Skip to content

Configuration files

Two TOML files, two audiences — ixt.toml for users, registry.toml for short names.


Two files, two audiences

ixt has two user-facing TOML files. 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
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. The registry is covered on its own page.


Mental model — two sources of state

ixt distinguishes two kinds of state, 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, runtime policy).

    apply enforces it · upgrade obeys its bounds.

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

Two 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. 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.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
[tools.ruff]
version    = "==0.5.0"
env_base   = "os-common"
env_allow  = ["*RUFF*"]
env_deny   = { "RUFF_SECRET_TOKEN" = {} }
fs_base    = "app-common"
fs_scratch = ["~/.aws", "~/.ssh"]

See Policy reference for the full semantics. These fields affect runtime shims only; they do not sandbox package installation.

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.
env_base string env-var policy base — "all" (default) / "none" / "os-common". See Env policy.
env_allow list Glob patterns of env vars added on top of env_base.
env_deny table Glob → { except = [...] } mapping, not a list. Deny always wins over allow; except keeps specific variables out of a broad deny rule.
fs_base string filesystem policy base — "all" (default) / "app-common" / "app-minimal" / "none". Requires bwrap. Adding fs_ro/rw/scratch while fs_base is "all" auto-promotes to "app-minimal". See Fs policy.
fs_ro list Extra read-only bind paths added to the fs sandbox.
fs_rw list Extra read-write bind paths added to the fs sandbox.
fs_scratch list Scratch tmpfs paths — real content hidden, writes discarded on exit (typically ~/.aws, ~/.ssh).

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

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

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

ixt tool apply reference