Get started

From zero to a running program in five minutes

Capa is a Python 3.10+ package that transpiles .capa source into Python and executes it with a small runtime. Four ways to install; pick one.

Install

Four paths

pip install if you already have Python 3.10+; the one-line installer if you want capa on your PATH without it; the manual binary if you prefer to verify the asset yourself; from source if you intend to contribute.

Option A: pip install recommended

If you already have Python 3.10+, this is the shortest path. Capa is on PyPI as the distribution capa-language; the import package and the capa command are unchanged. Open a new shell and capa --version should answer.

PyPI
$ pip install capa-language
$ capa --run hello.capa
The distribution on PyPI is capa-language; the import package stays capa, so import capa, python -m capa and the capa command all keep working. The Wasm backend is optional: pip install "capa-language[wasm]" unlocks capa --wasm --run. Published through PyPI Trusted Publishing (OIDC), with no stored API token, and a PEP 740 attestation.

Option B: one-line installer

Downloads the latest pre-built binary into ~/.local/bin/capa (Linux / macOS) or %LOCALAPPDATA%\capa\capa.exe (Windows). Open a new shell and capa --version should answer.

Linux / macOS
# Linux / macOS
$ curl -fsSL https://github.com/nelsonduarte/capa-language/releases/latest/download/install.sh | bash
Windows (PowerShell)
PS> irm https://github.com/nelsonduarte/capa-language/releases/latest/download/install.ps1 | iex
Verify before you run. Each one-liner has a companion .sha256 in the same release. Download both, sha256sum -c install.sh.sha256, read the script, then bash install.sh. Override the location with INSTALL_DIR / CAPA_INSTALL_DIR; the installer is idempotent; rerun to upgrade. It adds the install dir to your PATH automatically (set CAPA_NO_MODIFY_PATH=1 to opt out).

Option C: manual binary download

Each tagged release publishes a standalone PyInstaller binary that bundles the compiler and its own Python interpreter into one file, so it runs on a machine with no Python installed. The language server is bundled too, so capa lsp works straight from it.

Linux · x86_64
$ curl -L -o capa \
   https://github.com/nelsonduarte/capa-language/releases/latest/download/capa-linux-x86_64
$ chmod +x capa
$ ./capa --run hello.capa
macOS · Apple Silicon
$ curl -L -o capa \
   https://github.com/nelsonduarte/capa-language/releases/latest/download/capa-macos-arm64
$ chmod +x capa
$ xattr -d com.apple.quarantine capa   # bypass Gatekeeper
$ ./capa --run hello.capa
Windows · x86_64
PS> Invoke-WebRequest `
      -Uri "https://github.com/nelsonduarte/capa-language/releases/latest/download/capa-windows-x86_64.exe" `
      -OutFile capa.exe
PS> .\capa.exe --run hello.capa
Each binary ships a matching .sha256; verify with sha256sum -c before running. Since 1.18.0 the binaries and the two install scripts also carry SLSA build provenance: gh attestation verify capa-linux-x86_64 --owner nelsonduarte checks who built the file and from what source, which a hash cannot. Assets from 1.17.0 and earlier have no attestation and that command returns 404 for them; they were deliberately not retro-signed, so a 404 on an older asset is history, not tampering. On Linux the binary is a CLI tool, not a desktop app: open a terminal and chmod +x rather than double-clicking. The macOS build is not yet notarised, so remove the quarantine attribute or allow it in System Settings → Privacy & Security.

Option D: from source

Requirements: Python 3.10+ and git. Tested on 3.10, 3.12 and 3.14 across all three platforms. Zero runtime dependencies outside the standard library.

source install
$ git clone https://github.com/nelsonduarte/capa-language
$ cd capa
$ pip install -e .
$ python -m unittest discover tests   # over 4,000 tests, a couple of minutes

The editable install registers the package and adds a capa command on your PATH, so both capa <args> and python -m capa <args> work from any directory. The Wasm backend is optional: pip install -e '.[wasm]' unlocks capa --wasm --run.

Packages

Dependencies in capa.toml

Projects declare dependencies in a capa.toml at the root. The same file pins the source: a git URL with an exact tag, or a relative path. A signed registry index maps short names to git sources and is GPG-verified before any name is trusted.

capa.toml
[package]
name = "my-project"
version = "0.1.0"
capa = ">=0.8.4"

[dependencies]
capa_log = { git = "https://github.com/nelsonduarte/capa_log", tag = "v0.1.2" }

[dev-dependencies]
capa_test = { git = "https://github.com/nelsonduarte/capa_test", tag = "v0.1.1" }
$ capa install        # resolve, fetch deps into ./vendor/, write capa.lock
$ capa --run main.capa
$ capa install        # idempotent: rerun to refresh against capa.lock

The capa.lock pins the resolved git SHA plus a SHA-256 of the fetched tarball, so installs are bit-reproducible. When a dependency carries a verify_key, capa install verifies the GPG tag signature or Sigstore SLSA attestation before unpacking. Since 1.18.1, a missing gh on your PATH is an error rather than a warning for such a dependency: the verifier being absent no longer downgrades to "install it anyway". CAPA_ALLOW_MISSING_GH=1 overrides, and names on stderr every dependency it let through. Full guide in docs/packages.md.

The compiler floor, and a manifest that will not parse

The capa key above is the oldest compiler the project says it can be built with. Since 1.19.0 it is enforced. Before that it was parsed and ignored, so a floor had never been tested against a running compiler; if your project declares a floor above the compiler you are running, you now get a refusal where you used to get a build:

terminal
$ capa --check main.capa
capa: /path/capa.toml: this project declares capa = '>=99.0.0', but the
compiler running the build is 1.27.0.
$ echo $?
1

The policy splits by root versus dependency. The root manifest's floor is a hard error (exit 1). A dependency's floor is a warning naming the package, because you cannot satisfy it by editing a manifest you own; that warning is raised by the commands that compose the dependency graph (--check-capabilities, --compose-sbom, --check-policies), not by a plain --check. A missing capa key constrains nothing. CAPA_IGNORE_CAPA_FLOOR=1 downgrades the refusal to a warning that reprints, in full, the refusal it overrode. Before reaching for it, check whether the floor or the compiler is the thing that is out of date.

The same release stopped the compiler ignoring a capa.toml it could not parse. Any parse error, including one lowercase letter in a capability name, is now a refusal:

terminal
$ capa --check main.capa
capa: broken capa.toml: /path/capa.toml: [capabilities].max names unknown
capability(ies): ['stdio']; allowed: ['Clock', 'Db', 'Env', 'Fs', 'Net',
'Proc', 'Random', 'Serve', 'Stdio']
$ echo $?
2

There is deliberately no escape hatch for this half. Ignoring the manifest discards the name-to-directory mapping that makes a declared dependency path authoritative, and on 1.18.1 that was measured compiling an unaudited directory that merely shared the dependency's name, printing ok and exiting 0. Details in the 2026-07-20 advisory.

Seed libraries

The signed registry lists eighteen libraries; the original seed set of eight is below, and the packages page has the full list. Each declares its own capability surface, visible in the SBOM before you read its code. A library marked none declares no capability, so no function in it can perform an effect: the discipline is checked per function, over the code the compiler reads.

PackageWhat it doesCapability
capa_cliCommand-line argument parsing: flags, positionals, subcommandsnone
capa_csvRFC 4180 CSV parser, header view, and writernone
capa_datetimeDate and time values plus formattingnone
capa_hashSHA-256, SHA-224 and HMAC-SHA256, constant-time comparenone
capa_httpHTTP client: request and response handlingNet
capa_logStructured logging with levels and formattersStdio
capa_sbomCycloneDX / SPDX JSON parsing with capability queriesnone
capa_testA tiny assertion library for the capa test runnerStdio
Your first program

Hello, Capa

The fastest path is to let the compiler scaffold one for you.

scaffold
$ capa init my-project
$ cd my-project
$ capa --run main.capa
Hello from Capa!

Or type it yourself; create hello.capa:

hello.capa
// hello.capa
fun main(stdio: Stdio)
    stdio.println("Hello, Capa!")

The stdio: Stdio parameter is the language asking explicitly for the right to write to standard output. The runtime hands one to main, and main can hand it further down, or not. This is the whole language in one line.

The CLI

Pipeline modes and tooling flags

Each pipeline mode subsumes the previous: --run implies --transpile, which implies --check.

FlagWhat it does
init [name]Scaffold a new project: main.capa, README.md, .gitignore, .capa-version.
(none)Tokenize: print the lexer token stream.
--parseParse to AST and print a structured dump.
--checkFull analyzer: name resolution, types, capability discipline. Prints errors or OK.
--transpileEmit equivalent Python 3.10+ source to stdout.
--runTranspile and execute. The everyday flag.
--fmt / --fmt-checkRewrite in canonical style, or verify only.
--docEmit a self-contained HTML page from /// doc comments.
--manifest--provenanceEmit the authority graph in five formats: Capa JSON, CycloneDX 1.5, SPDX 2.3, VEX, SLSA L1 provenance.
--watchRe-run on every change. Implies --run.
lspStart the language server on stdio.
$ capa --check examples/io.capa
OK

$ capa --run examples/grades.capa
=== Roster ===
  Ana: 17.5 (Excellent)
  Bruno: 13.0 (Pass)

Every invocation also works as python -m capa <args> if capa is not on your PATH.

Editor integration

An LSP server in the box

Any LSP-capable editor (Helix, Neovim, Zed, VSCode, JetBrains, Emacs…) gets diagnostics, hover, go-to-definition, find-references, outline, completion, semantic highlighting, rename and Quick Fixes: the server runs the same lexer / parser / analyzer the CLI runs.

Helix · languages.toml
[[language]]
name = "capa"
language-servers = ["capa"]
file-types = ["capa"]

[language-server.capa]
command = "capa"
args = ["lsp"]

The VSCode extension is on the Marketplace, with syntax highlighting, snippets, and a bundled LSP client that auto-connects to capa lsp:

$ code --install-extension nelsonduarte.capa-language
Next

Where to go next