Content Component Contract

Content components perform finite transformations over text or bytes. The host writes input into WebAssembly memory, applies any uniforms, calls render(input_size), then reads the returned output bytes.

Use this contract for converters, validators, formatters, document renderers, generators, and pipeline stages. Use the Interactive Component Contract instead when a component must retain live state across user events and scheduled ticks.

Required Exports #

Every Content component exports:

Every pointer and capacity export is a zero-argument function returning i32.

The utf8 capacity exports declare that the corresponding bytes must be valid UTF-8. The bytes variants carry arbitrary binary data.

Host Call Flow #

For each render request using a known-valid QIP component, the host:

  1. Instantiates or reuses the component.
  2. Verifies that input_size does not exceed the input capacity.
  3. Writes the input bytes at input_ptr.
  4. Applies any requested uniforms.
  5. Calls render(input_size).
  6. Calls output_ptr and reads exactly the returned number of bytes.

If render traps, the request fails. The host must not read output_ptr; the buffer may contain stale or partial output.

A valid component guarantees that a successful render returns a byte count within its declared output capacity and that output_ptr identifies a region large enough for those bytes. Application wrappers for a component they trust may rely on those guarantees. They still check input size because the caller, not the component, chooses the input.

Known And Untrusted Components #

A known-valid component is an artifact the application deliberately trusts: for example, one built and tested with the application or obtained through a controlled artifact pipeline. Its QIP exports, memory regions, content types, and render behavior are part of that trust decision. Ordinary wrappers should use the contract directly instead of repeatedly checking whether the component honored it.

Arbitrary Wasm is different. Core WebAssembly validation proves that a module is structurally valid Wasm, not that it implements a QIP contract. A generic host accepting modules from users or third parties must establish that boundary itself. Before execution it checks the required exports and their signatures. Before copying input it checks the advertised input region. After render it checks the returned size and output region before reading component memory. It also applies the memory and execution policies described in [Hard Limits](/docs/hard-limits).

These checks belong at the point where arbitrary modules enter the application. Once an artifact has been admitted as a known-valid component, downstream wrappers can use the simpler call flow above.

Components can make the successful output-size guarantee statically certifiable with components/application/wasm/wasm-bounded-output.wasm. The checker recognizes a small compiled-Wasm proof epilogue that traps when the result exceeds the exact static output capacity. See [Bounded Output Proofs](/docs/hard-limits#bounded-output-proofs) for the accepted shape and its limits.

Repeated Renders #

Hosts may call render more than once on the same component instance. Each call is a new request using the bytes currently at input_ptr and the component's current uniform state.

Component authors should make repeated renders deliberate:

This lets browser hosts retain an instance for many requests and lets wrappers set uniforms immediately before rendering without reinstantiation.

Optional Content-Type Metadata #

A component may declare an exact input or output MIME type with:

Both exports in a pointer/size pair must be present. Omit a pair when the content type is unknown or intentionally generic.

When present, the value must be one lowercase MIME media type, such as text/markdown, text/html, or image/bmp. Do not include whitespace, media ranges, comma-separated lists, or parameters such as charset=utf-8. Hosts compare these strings exactly; they do not trim, lowercase, or remove parameters.

Content type is module metadata, not render state. Its pointer, size, and bytes must not vary with input, uniforms, previous calls, or other runtime state. Modules in the strict artifact profile make that invariant statically readable:

This lets tooling read the declared type directly from Wasm sections without allocating memory or instantiating and executing the module.

The repository includes such a reader as a QIP component. It prints the input content type, prints an empty line when the pair is omitted, and traps when any input or output content-type metadata violates the static form:

qip run -i component.wasm -- \
  components/application/wasm/wasm-read-input-content-type.wasm

Do not export text/plain merely to describe generic UTF-8. The input_utf8_cap and output_utf8_cap exports already express that constraint. Export a content type when the component requires or guarantees a specific format. Generic raw bytes normally omit it.

Pipeline Composition #

The host tracks an optional content type as bytes pass through a pipeline:

These rules let generic operations such as UTF-8 validation or byte-preserving transforms compose without erasing a more precise type, while format converters can explicitly change it.

Memory And Failure Behavior #

Future Numeric Output Shapes #

QIP previously supported output_i32_cap; it is not part of the current contract. Histograms, masks, label matrices, spectra, and similar results need more than a one-off integer-array export.

A future design should keep three concerns separate:

Until that design is specified, represent numeric collections through an explicitly documented byte format rather than relying on the removed export.