Configuration files¶
Keep the roles separate —
ixt.tomlis human intent,ixt.lockis the generated exact resolution,registry.tomlmaps 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 (ripgrep → BurntSushi/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).
applyenforces it ·upgradeobeys 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 writecreates it ·lock checkvalidates it ·apply --locked --strictreplays 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
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.ixt.lockfreezes a resolution only when you ask for it.ixt lock writeregenerates it fromixt.toml;ixt tool apply --locked --strictrefuses stale locks before changing the machine.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.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 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]
# 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¶
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).
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:
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¶
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.
Resolves the manifest once and writes exact versions and binary assets to ixt.lock.