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 value and --flag=value forms.
  • KAPSL_ARGS supplies 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.0

Flags

The complete flag reference, in the order kapsl --help prints them. The table mirrors that output flag-for-flag.

FlagWhat it does
-h, --helpFull 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, --listList 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.
--statusWhat's on disk: the cache store and the decisions store, by category. Read-only.
--dry-runShow the changes a run would make (pulls, builds) without committing them.
-y, --yesSkip the confirmation --clean cache/all/decisions would ask.
--updateRefresh the cached catalogue from the index.
--completions <SHELL>Shell completions (bash, zsh, fish, powershell).
-i, --installInstall 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-interactiveNo prompts; a required review fails closed.
-v, --verboseShow milestones + progress.
-q, --quietSuppress 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.
--privilegedDisable ALL capability restrictions and seccomp filtering.
--skip-scanSkip the vulnerability scan for this run.
--force-scanRe-scan now, even if the verdict cache is fresh.
--offlineUse 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.
--k8sRun 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.