Skip to content

The component catalog

The palette is not a list somebody maintains. It is read from the images that were actually built.

Architect — what you can place, and what happens to what you build WHAT YOU CAN PLACE — read from the registry, not maintained by hand containerregistryphrame/* images capabilitymanifest(OCI label) catalogvalidate ·group by type paletteinput · processoutput carries read offer WHAT YOU BUILD — a design, compiled into the schemas the services already read canvasplace · wiretyped pins inspectorschema-drivenparameters compilergraph → configsvdi://uuid GitLab commitphrame-config configure compile apply drag a component onto the canvas orchestration — placed onto a node, deployed, running live JPEG thumbnails + stats over MQTT (~1 Hz) come back to the canvas, so a design shows the running system

A designer needs to know what components exist, what parameters each takes, and what it can be connected to. Keeping that as a list inside the designer is the obvious approach and the wrong one: it is a second description of software that already exists, and the two drift.

The failure mode is unpleasant, because it is silent. A palette listing a component the deployed image cannot provide looks exactly like a palette listing one it can — right up to the point where a design is applied and something fails to start. Equally, a module that gains a capability does not gain a palette entry until somebody notices.

So each module describes its own components, that description is attached to its container image, and Architect reads it back. The mechanism is documented under capability manifests; this page covers what Architect does with it.

The catalog service iterates the configured registry providers, lists repositories and tags, and for each reads the com.phrame.manifest label from the image config blob over the Docker Registry v2 API. No layers are pulled and no container is run, so discovering the whole platform’s capabilities costs a series of metadata requests.

Each manifest is decoded, validated against the shared schema, and its components[] folded into a matrix keyed by component type. A manifest that fails to decode or validate is skipped — the catalog degrades to offering fewer components rather than failing to load. Strictness lives in the build, where a missing manifest on a required image fails the build outright, and where somebody is watching.

The default provider is scoped to the phrame group, so any phrame/* image carrying the label is discovered with no per-module code change in Architect. Adding a component to the palette is a matter of shipping an image that declares it. Catalog results are cached in Redis, because the registry round-trip is the expensive part and the answer changes only when something is built.

From the manifest, each component carries:

FieldMeaning
typeThe component identity the compiler and the canvas both key on
labelWhat the palette shows a human
categoryinput, process or output — the palette grouping
params[]The inspector form: name, type, default, constraints
inputs[] / outputs[]The pins, and therefore what may be wired to what
plugin / note / urlFieldOptional hints for components needing them

Parameter types are flat — string, number, boolean, select — which is enough for most components and not enough for a few. Where a real capability exceeds the model (arrays of objects such as WebRTC ICE servers or an HLS ABR ladder, or per-connection placement on a compositor input) the manifest carries prose in note and the value is set in the configuration rather than offered as a generated form field.

Not every pin can be known from a manifest, because not every pin is a property of the component. Some are properties of what got connected to it:

  • a demultiplexed source whose sub-streams depend on the transport stream actually arriving,
  • an MXL source whose flows exist only once the source is attached.

Those pins are discovered at wiring time and rendered once the component has a source. The practical consequence for anyone using the canvas: if a pin you expect is missing, connect the upstream component first. The canvas is describing what it can currently know, not refusing to offer something.

This is also why the manifest’s inputs[] and outputs[] are the static pins — the ones true of the component regardless of context — rather than the complete set.

Connections are validated on type, direction and arity: an output may only reach a compatible input, and a pin that is already satisfied will not accept a second wire. The canvas resolves a dragged wire to the nearest compatible pin within a snap radius rather than demanding pixel accuracy on a small target, and pins carry a transparent hit area larger than the visible dot — wiring at low zoom was otherwise unreasonably fiddly.

Validation gates what a snapped connection is allowed to be, so a generous snap radius makes valid targets easier to hit without permitting invalid ones.