Compose and composectl
composectl
(sister repo at ~/git/webassembly-component-orchestration/) is a
component-orchestration substrate. Pylon consumes it as a library,
embedded in packaging/launcher-rs/pylon-launch.
Composectl gives us:
- Content-addressed blob store at
.compose/blobs/. Every component.wasmis stored by SHA-256 digest. The plan JSON refers to digests, not paths. - Trust store. Digests must be trusted before instantiation.
pylon-launchtrusts each digest embedded in the plan via composectl's Rust API (workaround while the standalonecomposectl trust addCLI is not yet exposed). - Plan JSON loader. A single JSON document names the root, the set of components, the compose-time bindings between them, and the runtime dynlink capabilities the host grants.
Plan JSON shape
The full reference is Plan JSON structure.
A representative slice from plans/python-pylon-dynlink.json:
{
"version": "1",
"root": "python",
"linkage": "runtime",
"components": [
{ "id": "aead", "digest": [112, 58, 92, ...] },
{ "id": "arrow-core", "digest": [181, 17, 115, ...] }
],
"bindings": [
{
"consumer_id": "python",
"import_name": "arrow:core/array@0.1.0",
"provider_id": "arrow-core",
"export_name": "arrow:core/array@0.1.0"
}
],
"policy": {
"determinism": "relaxed",
"capabilities": [
{ "name": "dynlink:resolve", "level": "required" },
{ "name": "dynlink:invoke", "level": "required" }
]
}
}
Load-bearing fields:
root— which component to invokerun.run()on. Alwayspython.linkage: "runtime"— composectl resolvesbindingsat instantiation time (as opposed to"static", which requires pre-composed artifacts).components[].digest— 32-byte SHA-256 of the blob in.compose/blobs/. Pylon-launch trusts each digest before handing it to composectl.bindings— every non-wasi import declared bypython.wasmneeds one entry naming which component satisfies it.wac-graphmatches on(consumer_id, import_name).policy.capabilities— grants for runtime-resolved features.dynlink:resolve/dynlink:invokegate the runtime-dynlink path.
The plan is auto-generated by
scripts/build-pylon-dynlink-plan.py,
which walks every non-wasi import declared by python.wasm, locates
the corresponding cap component .wasm on disk (env-var-overridable
paths), stages each into .compose/blobs/, and writes the JSON.