Tool Index
The tool index is the mapping from tool names (rg, python, curl) to the container images kapsl runs. kapsl consults a local cache of a global index, plus any overrides in your kapsl.toml. You rarely need to think about it — but it's the mechanism that makes kapsl rg resolve to a real image.
How resolution works
When you run kapsl <tool>, kapsl resolves the image in this order:
- Local
kapsl.toml— the[tools]section (see Tool Mappings) - Cached global index —
~/.cache/kapsl/index/index.json - Fallback —
kapsl.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.kapsl.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:
| Field | Purpose |
|---|---|
image | OCI image reference |
bin | Binary to run (for multi-tool images, e.g. dig/nslookup/host from one bind image) |
description | One-line synopsis shown by kapsl --search and kapsl --info |
tags | Searchable keywords |
category | Broad grouping for display |
lifecycle | runtime (multiple maintained version lines) or rolling (:latest is the primary tag); absent = rolling |
versions | Selectable tags for tool@version (e.g. Python's 3.12, 3.13, latest) — any tool may declare them, not just runtimes |
capabilities | Runtime capabilities kapsl enables automatically (e.g. net, browser, pid) |
dotfiles | Host 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_rules | Per-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=/kapsl/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 /kapsl/home (e.g. ~/.aws → /kapsl/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
Search
kapsl --search <query> matches tool names, descriptions, and tags. Local kapsl.toml entries are listed first:
kapsl --search dns
kapsl --search python
kapsl --search kubernetesShow details
kapsl --info <tool> prints the index entry plus — if the image is pulled locally — labels read from the image itself:
kapsl --info curlRefresh the index
The index is cached and checked automatically on a schedule (see
Freshness). To check it
now — and download a new version if index.kapsl.sh/index.json has one:
kapsl --updateVersion selection
Tools that declare a versions list can be pinned with @version:
kapsl [email protected] --version
kapsl 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 kapsl --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.14ANDpython 3), withlatestpointing at the newest/default line only. The floating tags move forward on each rebuild (the3.12line'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 yourkapsl.tomlinstead (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 = "kapsl.sh/python@sha256:…", command = "python3" } -
rolling(the default) —:latestis the primary tag and whatkapsl <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 kapsl.toml.
Automatic capabilities
Some index entries declare capabilities, which kapsl enables automatically so you don't have to pass a flag. For example, the system-monitor tools (htop, ps, top) declare capabilities = ["pid"], so kapsl 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 kapsl-index/tools/*.toml of the kapsl repo (this repo holds only the documentation), grouped by category. To add a tool, edit the relevant file in a kapsl checkout:
# kapsl-index/tools/text.toml
[ripgrep]
image = "kapsl.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 kapsl-index/. To test locally first:
# in a checkout of the kapsl repo:
cd kapsl-index
./compile.py # the shebang runs python through kapsl itself
cp index.json ~/.cache/kapsl/index/index.json
kapsl --search ripgrep
See Tool Mappings for overriding a tool in your personal config without changing the global index.