CLI Reference
One binary, one grammar. Everything you type is either a flag for kapsl or an argument for the tool — and the line between the two is drawn at the first non-flag argument.
Grammar
kapsl [FLAGS] TOOL [ARGS]...
- Flags come before the tool.
kapsl --skip-scan --port 8080:80 curl -s example.com - The first non-flag argument is the tool name. Everything after it is passed to the tool verbatim — kapsl never interprets, reorders, or "fixes" your tool arguments.
--ends option parsing explicitly, which is what you need when the tool's first argument looks like a flag:
$ echo '{"a":1}' | kapsl -- jq '.a'
1
- Flags take values in both
--flag valueand--flag=valueforms. KAPSL_ARGSsupplies extra flags before argv; on a conflict, argv wins (see Environment Variables).
kapsl -h is the one-screen reference; kapsl -h <topic> goes deeper. What it prints:
$ kapsl -h
kapsl — signed tools, sandboxes and environments, one binary
USAGE
kapsl [FLAGS] TOOL [ARGS]... run a tool, sandboxed
kapsl -e TOOL PKG=VER [TOOL2 ...] compose with an inline env
kapsl -e NAME@VERSION [ARGS]... compose with a prebuilt env
VERBS
--search <q> find a tool (local — no pull, no scan, no exec)
--info <tool> summary + sandbox declaration before you run it
--install install shims --list list them
--pull [IMAGE] pull + scan, don't run --update refresh the cached catalogue
--watch [FILE] rebuild the env when a file changes (default: .kapsl-env.json)
--status what's on disk --clean free it up --dry-run preview
-y/--yes skip confirmations
man <tool> render that tool's man page (its real upstream page)
ESCALATION (printed when used — see `kapsl -h capabilities`)
--cap <caps> override tool caps (net, rw, ro, nomount, ...)
--privileged no caps, no seccomp --skip-scan skip the vuln scan
FLAGS
-e <SPEC> compose with an inline env (repeatable)
-E [VAR] pass a host env var (or set one)
--port publish a port --offline cached local state only
--non-interactive no prompts; a required review fails closed
-v/-q progress / quiet -h this screen; --help full reference
TOPICS kapsl -h <topic> — capabilities, environments, catalogue, scanning, output
kapsl v0.1.0Flags
The complete flag reference, in the order kapsl --help prints them. The table mirrors that output flag-for-flag.
| Flag | What it does |
|---|---|
-h, --help | Full reference: usage, verbs, escalation, topics. kapsl -h is the short screen. |
-s, --search <QUERY> | Search the local catalogue by name, description, or keyword. No network, no tool runs, no pulls, no scans. |
--info <TOOL> | Summary + sandbox declaration (image, digest, capabilities, seccomp class) — inspect before you run it. |
-l, --list | List installed shims (name, target, path). |
--clean [TARGET] | Reclaim disk: scratch, images, cache, all, or decisions (default: scratch,images). cache, all and decisions ask first; decisions is never part of all. An unknown target exits 2. |
--status | What's on disk: the cache store and the decisions store, by category. Read-only. |
--dry-run | Show the changes a run would make (pulls, builds) without committing them. |
-y, --yes | Skip the confirmation --clean cache/all/decisions would ask. |
--update | Refresh the cached catalogue from the index. |
--completions <SHELL> | Shell completions (bash, zsh, fish, powershell). |
-i, --install | Install shims so plain tool names work (git, curl, rg …). See Shims. |
--pull [IMAGE] | Pull a tool image and scan it without running it — pre-warm for CI. See CI/CD. |
--watch [FILE] | Rebuild the environment when a file changes (default: .kapsl-env.json). See Package Providers. |
--non-interactive | No prompts; a required review fails closed. |
-v, --verbose | Show milestones + progress. |
-q, --quiet | Suppress info lines; errors still print. |
-e, --env <SPEC> | Compose a tool with an inline env, or a prebuilt -e name@version. Repeatable. |
-E [VAR] | Pass a host env var into the run (-E VAR), or set one (-E VAR=value). Repeatable. |
--cap <CAPS> | Override a tool's capabilities — net, rw, ro, nomount, rwimg, browser, clipboard, pid, gpu, netraw, or the negations nonet, nobrowser, noclipboard. Comma- or space-separated; repeat the flag. See Capabilities. |
--privileged | Disable ALL capability restrictions and seccomp filtering. |
--skip-scan | Skip the vulnerability scan for this run. |
--force-scan | Re-scan now, even if the verdict cache is fresh. |
--offline | Use cached local state only — no pulls, no freshness refresh, no index fetch. Anything not already cached fails with a clear error. See Offline Mode. |
-p, --port <BINDINGS> | Publish a container port, e.g. '8080:80' or '127.0.0.1:3000:3000'. |
--platform <PLATFORM> | Override the target platform (e.g. linux/amd64 to run x86 tools on an ARM host). |
--runtime <ENGINE> | podman | docker | container | k8s. |
--k8s | Run in a Kubernetes pod (kubeconfig from KUBECONFIG or ~/.kube/config). A later feature. |
--node <NODE> | Pin the pod to a node. A later feature. |
-n, --namespace <NS> | Kubernetes namespace (default: from [global] k8s_namespace or default). A later feature. |
--pvc <NAME> | Attach a persistent volume (read-write). A later feature. |
Positional arguments: TOOL is the tool to run (or a verb such as man <tool>); ARGS — everything after the tool — is passed to it verbatim.
Verbs
kapsl man <tool>
Renders the tool's man page — the tool's real upstream page, or a page embedded in the tool image at /kapsl/man.md. It runs the man tool against the target, so it is itself a sandboxed run:
$ kapsl man jq
jq — commandline JSON processor [version 1.7.1]
SYNOPSIS
jq [options] (jq-filter) [file...]
...
kapsl man with no tool is an error: usage: kapsl man <tool> (exit 1).
kapsl --search <query>
Search is local — it runs against the cached catalogue, so no pull, no scan, no execution, and it works offline once the catalogue is cached. Results are ranked, capped by [global] search_limit (default 50); your own [tools] entries are never capped, because they are your declarations, not index noise.
$ kapsl --search python
TOOL IMAGE DESCRIPTION
------------------ --------------------------------------------- ------------------------------
python ghcr.io/kapsl-sh/python:latest The Python programming language interpreter
bandit ghcr.io/kapsl-sh/bandit:latest Scan Python source for common security issues
black ghcr.io/kapsl-sh/black:latest Reformat Python source files to a single deterministic style
...kapsl --info <tool>
The summary plus the sandbox declaration — the boundary you will get before the run, not after. A read of this is cheaper than a run you did not mean to grant:
$ kapsl --info jq
Command-line JSON processor
IMAGE ghcr.io/kapsl-sh/jq:latest
digest sha256:6388…6948 1 MB · 1 layers
LINES RESOLVES TO
▸ @latest 1.8.1 in use
lines float — kapsl carries security updates onto them
pin one with jq@<version> · a digest never moves
CAPS ro
SECCOMP net-listen (no class declared) (tier: default)
SOURCE https://jqlang.org/
RUN kapsl jq SHIM kapsl -i jq
CAPS is what the tool declared (or the image's own default); SECCOMP is the live filter class — today every live payload is the default tier. To see which tools declare which capabilities at a glance, the index lists them per tool at index.kapsl.sh.
kapsl --pull [image]
Pulls a tool image and runs its vulnerability scan, without executing the tool. This is the CI pre-warm: the job's first kapsl invocation starts from a warm cache. --pull combined with --offline is rejected as contradictory. See CI/CD.
kapsl --install and kapsl --list
--install puts a shim in ~/.local/bin so the plain tool name works anywhere on your PATH; --list shows what is installed (name, target, path). Shims dispatch by argv[0] and reproduce the tool's exit code exactly. See Shims.
kapsl --watch [file]
Sits on a compose spec (default .kapsl-env.json) and rebuilds the environment when the file changes. The dev loop for Package Providers.
kapsl --update
Refreshes the cached catalogue from the index. Normally you do not need it — freshness is automatic — but it is the escape hatch when a tool you expected is missing from the cache. See Always Current.
kapsl --status and kapsl --clean
--status is the read-only view of what kapsl holds on disk — the cache store and the decisions store, by category:
$ kapsl --status
kapsl storage
cache /Users/q/.cache/kapsl
decisions /Users/q/.local/share/kapsl
scanner 15 MB 6 entries
grype database 0 B 0 entries
tool catalogue 65 KB 2 entries
freshness stamps 2.8 KB 1 entry
run scratch 0 B 0 entries
decisions 223 MB 6 entries
total 238 MB
not written by kapsl (never removed automatically):
/Users/q/.local/share/kapsl/accepted-capabilities.json
/Users/q/.local/share/kapsl/attestations
not written by kapsl (never removed automatically):
--clean reclaims it. Targets: scratch, images, cache, all, decisions (default: scratch,images). cache, all and decisions ask first; decisions is never part of all (it is your trust ledger). --dry-run previews; --yes skips the confirmation.
$ kapsl --clean cache
▍ ■ INFO 10 items · 15 MB to remove
▍ 12 MB cache/scanner /Users/q/.cache/kapsl/scanner/attestations
▍ 2.9 MB cache/scanner /Users/q/.cache/kapsl/scanner/scan-cache.json
▍ ■ ERROR --clean cache needs confirmation; pass --yes
$ kapsl --clean bogus
▍ ■ ERROR unknown --clean target 'bogus' (expected: scratch, images,
▍ cache, all, decisions)
The unknown target exits 2; a refused confirmation exits 1. See Troubleshooting → Disk.
kapsl --completions <shell>
Prints the completion script for bash, zsh, fish, or powershell — the usual eval "$(kapsl --completions zsh)" wiring.
Escalation flags
The escalation flags announce themselves, because they change the boundary you are running in:
--privileged disables all capability restrictions and seccomp filtering
--skip-scan skips vulnerability scanning for this run
--cap gpu passes GPU device access to the container — a real attack-surface
expansion, not just a namespace flag
--cap nest lets tools in this container re-run kapsl — each nested tool gets
its own sandbox, but anything running here can start one
Each also points you at kapsl -h capabilities, which explains what the capability actually does. See Capabilities.
Exit codes and pipelines
The tool's exit code is the exit code of the invocation. No wrapper, no translation:
$ kapsl python -c "import sys; sys.exit(3)"
$ echo $?
3
A warm, successful run prints nothing and exits 0, like running the tool directly; the tool's stdout and stderr are its own, unmodified. Scripts with set -e behave as if they called the tool directly, and a shimmed git diff --exit-code in a script works because the code survives the shim too.
The exceptions are kapsl's own failures, which are its exit codes, not the tool's: most are 1 (runtime missing, scan blocked, build failed, bad config), and an unknown --clean target is 2. Signal deaths follow the shell convention — 128 + signal number (SIGTERM → 143, SIGKILL → 137). The full table, with what each failure looks like, is Exit Codes.