Tool Index

The tool index is the mapping from tool names (rg, python, curl) to the container images boks runs. boks consults a local cache of a global index, plus any overrides in your boks.toml. You rarely need to think about it — but it's the mechanism that makes boks rg resolve to a real image.

How resolution works

When you run boks <tool>, boks resolves the image in this order:

  1. Local boks.toml — the [tools] section (see Tool Mappings)
  2. Cached global index~/.cache/boks/index/index.json
  3. Fallbackboks.sh/<tool>:latest

The first match wins. Overriding a tool from the global index is just a matter of adding it to your local [tools].

The global index

The global index is a single JSON file published at https://index.boks.sh/index.json. It currently maps 236 tools to images across categories: programming languages, network utilities, devops tools, databases, text processing, system monitors, and AI tools (model hubs, agentic coding CLIs, local LLM hosting). Each entry can carry:

FieldPurpose
imageOCI image reference
binBinary to run (for multi-tool images, e.g. dig/nslookup/host from one bind image)
descriptionOne-line synopsis shown by boks --search and boks --info
tagsSearchable keywords
categoryBroad grouping for display
lifecycleruntime (multiple maintained version lines) or rolling (:latest is the primary tag); absent = rolling
versionsSelectable tags for tool@version (e.g. Python's 3.12, 3.13, latest) — any tool may declare them, not just runtimes
capabilitiesRuntime capabilities boks enables automatically (e.g. net, browser, pid)
dotfilesHost paths to mount read-write into the container (e.g. ~/.aws), each declared as a file or a directory so a missing path is bootstrapped correctly on first run
package_rulesPer-package effects keyed on (provider, package): a dotfiles host-path mount and/or an auto-enabled capabilities entry, triggered when a specific package is installed rather than the tool itself (e.g. huggingface_hub under pip mounts ~/.cache/huggingface; requests under pip auto-enables net) — see Package managers

Every container gets HOME=/boks/home, backed by an ephemeral tmpfs: tool-written files like shell history and caches succeed and vanish with the container instead of failing against the read-only image or landing in your project directory. Declared dotfiles are mounted under /boks/home (e.g. ~/.aws/boks/home/.aws), so the tool finds its persisted config via $HOME while everything undeclared stays ephemeral. package_rules dotfiles mounts land the same way, confined to ~/.cache/ or ~/.config/ (where XDG_CACHE_HOME/XDG_CONFIG_HOME already point); their capabilities are applied with the same precedence as any other auto-capability — explicit CLI flags always win.

Using the index

boks --search <query> matches tool names, descriptions, and tags. Local boks.toml entries are listed first:

boks --search dns
boks --search python
boks --search kubernetes

Show details

boks --info <tool> prints the index entry plus — if the image is pulled locally — labels read from the image itself:

boks --info curl

Refresh the index

The index is fetched on demand and cached. To pull the latest version from index.boks.sh/index.json:

boks --update

Version selection

Tools that declare a versions list can be pinned with @version:

boks python@3.12 --version
boks node@22 --version

Tools without a versions list still accept @<tag>, but the tag must be one the image actually publishes.

Lifecycle: runtime vs rolling

Each tool follows one of two versioning lifecycles, shown by boks --info <tool>:

  • runtime — a tool with multiple maintained version lines (python, node). The tag model is the one IMAGE-REQUIREMENTS.md §18 defines: each active upstream maintenance line publishes an exact build-version tag (python 3.14.6, node 22.23.2) plus a major/minor floating tag (and a major-only float for the current line: python 3.14 AND python 3), with latest pointing at the newest/default line only. The floating tags move forward on each rebuild (the 3.12 line's tag advances 3.12.x → newer 3.12.y), which is what keeps a maintained line patched. For bit-for-bit reproducibility, pin a digest in your boks.toml instead (the exact build-version tag for node is immutable; python's is rebuilt within the same version — see §18 item 1). A digest-pinned entry looks like:

    [tools]
    python = { image = "boks.sh/python@sha256:…", command = "python3" }
  • rolling (the default) — :latest is the primary tag and what boks <tool> gives you: always the current build. Versioned tags may exist alongside it and are selected the same way (tool@version).

Old version lines are kept, and they age. A version line that upstream stops maintaining keeps its tag but stops receiving rebuilds: known CVEs in it stay open, permanently. This is deliberate — version history exists so you can reproduce a bug or test against several versions of a tool (especially in CI/CD), not to be a maintained distribution channel. Reach for an old line when you need that version, and expect it to be vulnerable. The vulnerability scan still runs on old lines by default and blocks on CRITICAL findings; a testing setup that accepts the risk opts out per-run with --skip-scan, or globally with security_scan_enabled = false in boks.toml.

Automatic capabilities

Some index entries declare capabilities, which boks enables automatically so you don't have to pass a flag. For example, the system-monitor tools (htop, ps, top) declare capabilities = ["pid"], so boks shares the host PID namespace for them without requiring --cap pid. Likewise, a tool that needs the browser relay can declare capabilities = ["browser"], or a GPU-only tool can declare capabilities = ["gpu"] (podman/docker only — see Security Layers > GPU device passthrough).

Adding a tool to the index

The index source lives in boks-index/tools/*.toml of the boks repo (this repo holds only the documentation), grouped by category. To add a tool, edit the relevant file in a boks checkout:

# boks-index/tools/text.toml

[ripgrep]
image = "boks.sh/rg:latest"
bin = "/usr/bin/rg"
description = "Fast regex search tool"
tags = ["rg", "ripgrep", "search", "grep", "text"]
category = "text"

Then compile and push — the CI workflow (.forgejo/workflows/deploy-index.yml) recompiles index.json and deploys it on any push to main that touches boks-index/. To test locally first:

# in a checkout of the boks repo:
cd boks-index
./compile.py     # the shebang runs python through boks itself
cp index.json ~/.cache/boks/index/index.json
boks --search ripgrep

See Tool Mappings for overriding a tool in your personal config without changing the global index.