Configuration files¶
Two TOML files, two audiences —
ixt.tomlfor users,registry.tomlfor 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 (ripgrep → BurntSushi/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).
applyenforces it ·upgradeobeys 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
ixt.tomlwins over the metadata. When they disagree,applymakes the metadata match the toml (install, reinstall, inject, uninject, reexpose, remove), never the other way around.upgradestays within the toml's bounds. If the toml pins with==,upgraderefuses (and shows the latest as an FYI). If the toml declares a range,upgradeasks 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]
# 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¶
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.
rgfor 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:
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¶
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.