An honest inventory
What works, what is coming, and what is explicitly out of scope. Capa is released at v1.27.0: the surface is stable under SemVer, the test suite of over 4,000 tests is green on three operating systems, the discipline and information-flow control are enforced end-to-end, the package manager ships, all five audit artefacts are emitted by the compiler, and the Wasm Component Model backend is fully functional for real programs.
The boundary between real and roadmap is sharp
Capa compiles, type-checks, transpiles to Python, lowers to WebAssembly (core and Component Model), and runs. Every example on this site is executable.
Language core shipped
- Lexer + parser + analyzer + transpiler: 4 stages, ~5K lines of Python.
- Full type system: primitives, tuples, List/Map/Set, Option/Result, structs, sum types, generics with inference, pattern matching, closures, the ? operator.
- Built-in capabilities (Stdio, Fs, Net, Env, Clock, Random, Db, Proc, Serve, Unsafe) plus user-defined ones, with monotonic restrict_to attenuation and the consume qualifier. Serve is the one inbound authority: every other capability reaches out, this one is reached.
- Module system (import, cycle detection, pub visibility, CAPA_PATH) and a package manager (capa.toml + capa install + capa.lock).
Information-flow control shipped
- Two-point lattice (@public below @secret) on types, parameters and fields, with automatic label propagation by join.
- env.get is the secret-by-default source; secret-to-public sinks are warn-then-enforce, hard errors under @strict_ifc(). Serve.recv is the one inbound source and is @public: the lattice models confidentiality, not integrity or taint.
- declassify(value, reason) as the single auditable bridge, recorded in the SBOM as declassification_sites.
- Flagship example: capa_paymentguard.
Tooling shipped
- A 12-feature language server (diagnostics, hover, definition, references, outline, rename, type-aware completion, semantic tokens, folding, formatting).
- AST round-trip formatter with comment preservation, watch mode, doc-comment HTML generator, capa init scaffolding, the capa test runner, and a REPL with persistent history.
- VSCode extension on the Marketplace with a bundled LSP client.
Supply-chain artefacts shipped
- Five emitters: --manifest, --cyclonedx 1.5, --spdx 2.3, --vex (per-function exploitability), --provenance (SLSA L1).
- Product composition. --compose-sbom rolls the per-function facts up the dependency tree into a per-product capability SBOM over a lattice with a distinguished authority-unknown top: an unanalyzable subtree (native, unvendored, or Unsafe-crossing) composes as authority-unknown and fails closed, never reads as clean. --check-capabilities is the per-package ceiling CI gate against capa.toml [capabilities].
- Signed authority diffs. --capability-diff old.json new.json with --fail-on-widening: a canonical, signable release-over-release changelog that flags capability widening (the node-ipc 2022 signal, a build step holding Fs that silently gains Net).
- Compliance policies. An organization capa-policy.toml over the composed graph (exclusion, product-subset, purity, forbid-capability, forbid-dependency, no-unresolved-dependencies, plus the declassification-aware no-declassification and no-secret-egress), fail-closed on authority-unknown: --check-policies gates and --conformance-report emits signable evidence.
- --manifest-digest emits the canonical, content-addressable manifest wrapped in a content-integrity envelope: a byte-stable sha256 over the canonical bytes with an empty detached-signature slot (SLSA L1; the compiler holds no keys and never signs in-band).
- All eighteen registry packages carry three-layer hardening: lockfile SHA pin + GPG tag signature + a signed SLSA build-provenance attestation in the public Sigstore Rekor log. Checked on 2026-07-19 across all eighteen at their registry latest tag: every release tag is annotated and GPG-verified, and every release tarball verifies with gh attestation verify <file> --owner nelsonduarte, returning a slsa.dev/provenance/v1 predicate built by a GitHub-hosted workflow. That is the SLSA Build L2 shape (hosted builder, signed provenance); the level itself is GitHub's characterisation of its attestations, not something measured here.
- The manifest is load-bearing (1.19.0). A capa.toml the compiler cannot parse is refused (capa: broken capa.toml: <path>: <reason>, exit 2) rather than warned about and ignored, and the declared capa = ">=X.Y.Z" compiler floor is enforced: a hard error for the root manifest, a per-package warning for a dependency's floor, with CAPA_IGNORE_CAPA_FLOOR=1 as a loud escape. See Get started.
- SBOM tooling written in Capa: diff, capability audit, SPDX and CycloneDX parsers.
Wasm backend shipped
- Component Model backend: capa --wasm --component --output app.wasm produces a standalone artifact with the WIT spec embedded.
- Generics and trait dispatch are implemented on both backends, and every capability except Serve and Unsafe runs on Wasm, with attenuation enforced at the host boundary. Backend parity is a goal, not a guarantee. The toolchain ships differential tooling for it (capa test --both diffs the two backends per test file, and the suite carries a Wasm parity module), but that coverage is not exhaustive, so treat the Python backend as the reference semantics and do not derive a security conclusion from a Wasm run alone. One class of divergence has been closed: 1.20.0 made the Wasm hosts bind main's capability slots by declared type in declaration order, carried where ordinary tooling cannot strip it, so fun main(conn: Net, stdio: Stdio) narrowing conn and asking allows("example.com") now answers the same under --run and --run --wasm, where earlier it printed true then false.
- --prefer-wasm tries Wasm first with silent fallback. Unsafe and Serve are loudly rejected, and permanently: Unsafe has no sandboxed Wasm equivalent, and Serve would need wasi:sockets, which is neither vendored in the compiler nor reachable from the wasmtime bindings the hosts are built on. Both capa --wasm and capa --wit refuse a program whose signatures reach Serve, before any lowering, listing every offending site.
- Typed foreign components. extern component <Name> from "x.wasm" runs an untrusted, separately-compiled Wasm component confined by the sandbox to exactly its declared capabilities: for the capability subset it can carry, static-declared equals runtime-permitted (a child importing an ungranted interface is refused at instantiation, before its code runs). A resource ceiling bounds it too, --foreign-fuel (child CPU), --foreign-memory-cap (child memory), and --foreign-result-cap (a peak bound on the host-side buffer a result crossing allocates). Unsafe stays Python-only and composes as authority-unknown.
- Experimental --wasi mode. Compiles to a component that runs on stock WASI Preview 2 runtimes (for example wasmtime), instead of the default --component, which targets the dedicated capa:host. It provides mitigation Levels 1 and 2 (a runtime ceiling via --preopen and environment, plus the guest-side proof) but not Level 3, which needs capa:host: a slightly weaker guarantee tier in exchange for running on any stock WASI runtime. Literal filesystem paths pass the static ceiling; dynamic paths (for example from argv) require an operator grant --preopen <dir>, recorded in the SBOM as operator_declared_grants. capa --wasi-surface <file> shows which argv arguments reach Fs or Net sinks. Networking goes through wasi:http; note that wasmtime's wasi:http is allow-all at the host level (its C-API exposes no per-host allowed-hosts control), so the defence (fail-closed on non-2xx responses, anti-SSRF) is done guest-side. A dynamic (argv-derived) URL likewise needs an operator grant --allow-host <host> (append :get or :post to grant read or write only), the Net analogue of --preopen, recorded in the SBOM as an operator-declared grant. Example: capa --wasm --component --wasi --run programa.capa --preopen . -- <args>.
Quality & proofs shipped
- Agda mechanisation of all four λcap soundness theorems in proofs/.
- A gated-variant λcap proof (proofs/CapaManifestExact.agda, --safe) closes the introduction dimension of the Capa-vs-λcap gap: in a well-typed program every capability literal is introduced only at the top level, never under a lambda binder. It is introduction confinement only. It does not prove the manifest equals the declared set (the manifest stays a sound upper bound), gated preservation is false by design, and the surface-to-calculus translation is argued informally.
- Property-based testing through Phase 4 (50k+ generated programs) lifting wasm_runtime_classes ⊆ manifest_classes through the Wasm backend.
- Over 4,000 unit + end-to-end tests, green across three OSes and three Python versions; a published soundness-hardening advisory (1.2.0).
What is still open
Most of the original design promises have shipped: generic attenuation, the native module system, the package manager, selective import, doc comments, raw strings, named arguments, the LSP, the formatter, and the full SBOM/VEX/provenance triangle are all done. What remains is deliberately small.
- Refinement types P3. A way to express properties like "this string is non-empty" or "this list is sorted" in the type system. Research-grade; explicitly not in scope for 1.x.
- A full hosted registry with search and a web publish flow. The signed index covers name resolution and supply-chain anchoring today; the hosted experience is future.
- Package-manager polish: --frozen and richer transitive resolution remain for future iterations.
- LSP v2 polish (deferred until a real-user session surfaces a need): signatureHelp, inlayHint, workspace/symbol, codeLens, selectionRange.
- A dedicated native/LLVM backend is deliberately deferred, with no date: the Wasm Component Model backend emits a component that the runtime (for example wasmtime) can AOT-compile, which is distinct from a dedicated native backend.
Help shape the next milestone
Capa is a personal project, and an open one. The fastest way to influence the roadmap is to show up with a real use case.