Three real examples
Each example covers a different linkage-mode choice.
_duckdb_capability — compose-time static, big vendor library
DuckDB is ~40 MB compiled to wasm and holds prepared-statement /
connection handles across many Python calls. Handles cross the WIT
boundary as resource types (conn, prepared-stmt, appender),
which require the compose-time path.
cpython-ext/_duckdb_capability/
├── _duckdb_capability_module.c # DB-API-shaped C ext, ~700 lines
├── duckdb.py # Facade shim → Lib/duckdb.py
├── wit/
│ └── duckdb-import.wit # world duckdb-import { import duckdb:component/database; }
├── gen/ # wit-bindgen-c output (checked in)
│ ├── duckdb_import.c
│ ├── duckdb_import.h
│ └── duckdb_import_component_type.o
└── pyforge-pkg.toml
Sibling: ~/git/duckdb-wasm/ — the Rust cargo-component crate that
builds ducklink_core.wasm and exports duckdb:component/database.
Composition: plugged in by scripts/compose-python-component.sh at
the DUCKDB_COMPONENT slot.
_crc32c_dynlink_cap — runtime dynlink, function-library shape
Same C-ext shape as any other cap, but the C ext imports
compose:dynlink/linker instead of crc32c:.... At PyInit, it
calls linker.resolve_by_id("crc32c") once and caches the handle.
Every crc32c() call becomes instance.invoke("compute", data).
cpython-ext/_crc32c_dynlink_cap/
├── _crc32c_dynlink_capmodule.c
├── wit/ # world { import compose:dynlink/linker; }
├── gen/
└── pyforge-pkg.toml
Provider: packages-wasm/crc32c-dynlink-provider/ (Rust component
exporting compose:dynlink/endpoint). Registered in
build-pylon-dynlink-plan.py's DYNLINK_PROVIDERS list.
Adding an alternate CRC32C provider (say, a faster SIMD one) requires
building a new provider component, adding it to DYNLINK_PROVIDERS,
regenerating the plan. pylon.wasm does not change.
_id_mux_cap — shared-interface multiplexer
Some caps share a single WIT interface with a scheme-parameter (ULID,
NanoID, KSUID all route through
tegmentum:id-multiplexer/id-dispatcher). The multiplexer component
(~/git/id-multiplexer/) is one wasm blob; the cap C ext is one
module; the Python-side facade dispatches to per-scheme sub-modules.
cpython-ext/_id_mux_cap/
├── _id_mux_capmodule.c # thin C ext exposing dispatch()
├── id_mux.py # Facade → Lib/id_mux.py, offers
│ # generate("ulid"), generate("ksuid"), ...
├── wit/
├── gen/
└── pyforge-pkg.toml
The multiplexer pattern is worth reaching for whenever three-plus
sibling caps would otherwise be three-plus components importing
three-near-identical interfaces. It keeps pylon.wasm's import graph
flat.
For the hands-on companion — "how do I add one of these?" — see Add a cap.