Skip to main content

What a cap actually is

A cap is a two-sided artifact. Neither side works alone.

Python side — cpython-ext/_<name>_cap/

Lives in this repo under cpython-ext/. Contains:

  • _<name>_capmodule.c — a CPython C extension. Calls generated wit-bindgen bindings to route Python calls through the cap's WIT interface. This gets statically linked into python.wasm at CPython build time.
  • wit/*.wit — a small world declaring import of the WIT interface(s) this extension consumes. This is what wit-bindgen sees at binding-generation time.
  • gen/ — wit-bindgen-c output: _import.c, _import.h, _import_component_type.o. Regenerate via the gen target in each cap's makefile pattern; checked in so the CPython cross-build does not need wit-bindgen on the host.
  • pyforge-pkg.toml — the manifest. Names the module, its C file, its WIT dir, its [[capabilities.required]] block (interface
    • version), and optional [[provides]] blocks that ship user-facing Python shim modules into Lib/.
  • Optional Python facade — e.g. duckdb.py beside _duckdb_capability_module.c. Ships to Lib/duckdb.py. Presents the DB-API surface a user expects (import duckdb), routes it through the underscored C ext (_duckdb_cap). See naming conventions.

Wasm side — ~/git/<name>-wasm/

A separate sibling repo, one per cap. Owns:

  • The vendor library (miniz, OpenSSL, SQLite, DuckDB, Zstandard, ...) built for wasm32-wasip2 or wasm32-wasip1.
  • The Rust (or C, or wit-bindgen-guest for any component-model language) implementation wrapping that vendor library and exporting the WIT interface.
  • The WIT source (or a copy synced from wit/ in this repo — the interface is the contract).

The contract between them

The WIT interface is the entire contract. As long as the sibling repo's component exports openssl:component/tls@0.2.0 (or whatever) and this repo's _ssl_capability imports the same, wac plug composes them and the runtime sees a satisfied import graph.

This is why the capability layer is consumer-agnostic: MicroPython's _ssl_cap imports the same openssl:component/tls. The same openssl-component.wasm satisfies both consumers.