Every function declares what it is allowed to do.

Pythonic syntax, with the discipline checked statically: a function that does not list Fs or Net in its signature cannot touch the filesystem or the network. From those same signatures the compiler emits a machine-verifiable supply-chain SBOM by construction: a manifest that matches the code, not a scanner's approximation of it.

v1.27.0· MIT or Apache-2.0· over 4,000 tests· hand-written compiler in Python
summarise.capa
// the signature IS the contract
fun summarise(stdio: Stdio, fs: Fs, path: String) -> Result<Unit, IoError>
    let body = fs.read(path)?
    let first = match body.split("\n").get(0)
        Some(line) -> line
        None -> "(empty)"
    stdio.println("first line: ${first}")
    return Ok(())
the authorities a function can hold
Fs NetStdioEnv ClockDbProc RandomServeUnsafe
The problem

What Capa is for

Every dependency you install today runs with the full authority of the program that pulled it in. A logging library can read your environment. A date parser can open a socket. Nothing in the language stops it: you read the README and you trust.

Capa puts the authority surface in the type system. A function that needs the filesystem says so in its signature; one that does not cannot reach for it, no matter what its body tries to do. The compiler reads the source and emits a manifest of which functions hold which authorities, automatically.

In 30 seconds

From a signature to an audit artefact

There is no global Stdio or ambient filesystem: you cannot reference what is not in the signature. The compiler reads the same source three ways.

01 Declare nothing

A pure helper holds no authority

No capability in the signature means the compiler forbids any effect in the body. It is provably pure.

classify.capa
fun classify(score: Float) -> String
    if score >= 9.5
        return "Excellent"
    if score >= 6.5
        return "Pass"
    return "Fail"
02 Ask for what you need

Reading files requires Fs

To open a file the function must name Fs in its signature. The capability is the contract, and it is passed in, never ambient.

summarise.capa
fun summarise(stdio: Stdio, fs: Fs, path: String) -> Result<Unit, IoError>
    let body = fs.read(path)?
    stdio.println("read ${body.length()} bytes")
    return Ok(())
03 Get the manifest free

The compiler emits the proof

capa --manifest lists every function with the capabilities it declared and the ones it can provably never reach. Same input feeds --cyclonedx, --spdx, --vex and --provenance.

manifest.json
{
  "name": "summarise",
  "declared_capabilities": ["Stdio", "Fs"],
  "provably_excluded_capabilities": [
    "Clock", "Db", "Env", "Net",
    "Proc", "Random", "Serve", "Unsafe"
  ]
}
Two more guarantees

Narrow what a function holds, and prove where its data goes

Capabilities say which effects a function may use. Attenuation shrinks them on the way down; information-flow control tracks where labelled data is allowed to travel.

Attenuation

Capabilities can only be narrowed

fs.restrict_to("data/") hands a callee an Fs that only sees one directory, and the narrowing is monotonic by construction: you can never widen. The full story, including runtime path canonicalisation, is in Why Capa.

attenuate.capa
fun handler(fs: Fs)
    let scoped = fs.restrict_to("data/")
    load_records(scoped)
    # scoped.restrict_to(".."): cannot widen
Information-flow control

Where your data can go

Annotate a value @secret and the compiler propagates the label, then flags any secret that reaches a public sink: a warning by default, a hard error under @strict_ifc(). env.get is secret by default. The one auditable bridge is declassify(value, reason: "…"); every use is recorded in the SBOM as declassification_sites, so the disclosure ships with the manifest, not in a code-review thread. What the check does and does not catch.

leak.capa
@strict_ifc()
fun main(stdio: Stdio, env: Env)
    let key = env.get("API_KEY").unwrap_or("")  # @secret
    stdio.println(key)  # error: secret → public sink
Showcase

Real programs written in Capa

Each lives in its own repository, declares its dependencies in capa.toml, and runs through capa install && capa --run …

capa_authgate

An HTTP auth-token service, and the clearest statement of what capability typing buys. The serving process declares exactly {Serve, Env, Clock}, and Serve is inbound-only: it has no method that dials out, so the process holds the authority to be reached and nothing that reaches out. The function that actually touches your token holds less than that. verify_token's manifest is empty, and authgate.capa is byte-for-byte the file that shipped at v0.1.0, before the HTTP front-end existed, because a verifier forced to take now as an Int was already the shape of a request handler.

It ships two compiler-rejected negatives, and the second is the honest one. A closure can capture a capability into a handler whose type is still Fun(Request) -> Response, and that compiles: the type system does not stop it, and the information-flow warning it raises is the same one the innocent version gets. What refuses it is the package capability ceiling one level up, capa --check-capabilities, which fails and names the offending authority, Net.

HTTP service · Serve · v0.2.1

capa_claimdesk

An enterprise expense-reimbursement engine that exercises nearly the whole language at once: the claim lifecycle is a typestate, the payment authorization is a linear use-once token, the IBAN is held under information-flow control and reaches the audit ledger only through an audited declassify, and the policy engine dispatches over a List<Rule> via traits and generics. Runs byte-identically on both backends and ships its guarantees as proofs in the SBOM.

widest-coverage showcase

audit-trail-reporter

AML compliance toolkit. Four detection rules (threshold, watchlist, structuring, velocity), four report sinks, attenuated read+write Fs split.

~1100 lines · 9 files

capa_paymentguard

Payment-security core. Information-flow control proves card data cannot leave unmasked: the only path to a sink is an audited declassify, recorded in the SBOM.

IFC core

sbom-watch

SBOM operationaliser. Cross-references a CycloneDX SBOM against a CVE database and a policy file. CI-friendly exit code.

~700 lines · 5 files

policy-eval

JSON-encoded policy-as-code engine. Tree-walk interpreter over a recursive Condition AST.

~700 lines · 5 files

capa_dataguard

Data-governance pipeline. Every path from PII to output runs through an audited declassify, and the compiler-rejected negative shows what happens when one does not.

IFC governance

capa_configbroker

Capability-secured config and secrets resolver. Secrets reach a log line only through an audited declassify, and its Net capability is narrowed to a single host, which currently bounds the host addressed rather than the host reached.

capability-scoped

capa_ci_pipeline

A CI/release orchestrator built as a multi-package product. Four untrusted third-party actions run as sandbox-confined typed foreign Wasm components; the pure core holds no authority; the whole product's capability surface composes into one SBOM. A compromised build action that silently gains Net is caught twice over: by the signed authority diff and by an organization exclusion policy.

supply-chain product
Install

Up and running in one line

pip install capa-language if you have Python 3.10+, a one-line installer for Linux, macOS and Windows, a self-contained binary with .sha256 verification, or a source install: everything lives on the Get started page.

Get started
Release · 1.27.0

Stable, on SemVer, and on PyPI

1.27.0 is the current release, and Capa is now on the Python Package Index: pip install capa-language. It is published through PyPI Trusted Publishing (OIDC), with no stored API token, and carries a PEP 740 attestation. The distribution name is capa-language; the import package and the capa command are unchanged.

The first stable release shipped on 2026-06-03 as 1.0.0. Minor releases since added byte-reproducible SBOMs and attestations via SOURCE_DATE_EPOCH, the capa test runner, selective import, and a security-hardening line that closed further soundness and supply-chain findings.

1.19.0 (2026-07-20) makes two previously-succeeding builds fail on purpose. A capa.toml the compiler cannot parse is now a refusal (capa: broken capa.toml: …, exit 2) instead of a warning followed by a build that ignored the file, and the capa = ">=X.Y.Z" compiler floor is enforced for the first time after being parsed and ignored since 2026-05-19. What that means for your project. 1.18.0 attached SLSA build provenance to the release binaries and the install scripts; 1.18.1 fixed capa test under a released binary and closed a fail-open in capa install.

Surfaces listed in STABILITY.md follow SemVer. The full inventory is in the CHANGELOG and on the roadmap.

Next

Where to go next