Skip to main content
Version: 0.1.0-alpha.5 (alpha)

WIT value payloads

The generic Wasmtime backend uses application/vnd.latent.wit-values.v1+json for invocation input and output. The component's validated WIT types determine the interpretation of each value. The payload is UTF-8 JSON containing one positional array: parameters in function declaration order on input, and results in declaration order on output. A function with no parameters or results uses []. Names identifying the contract and method belong to the invocation envelope, not this array.

This value representation supports synchronous and selected canonical-async application functions exposed through component interfaces. Function kind is checked exactly and does not require an unrelated provider. It does not give clients WASI, resource handles, streams, futures, or an alternate transport. HTTP and SDK byte payloads must use the same framing when targeting the generic backend.

The inbound HTTP application profile uses an explicit body-base64: string field for its raw body. That new WIT field follows the ordinary string mapping below; it does not change the representation of list<u8> or any existing invocation contract.

Value mapping​

WIT typeJSON representation
boolBoolean.
u8, u16, u32, s8, s16, s32Integer token within the exact type's range; 1.0, 1e0, and strings are rejected.
u64, s64Canonical decimal string, such as "18446744073709551615" or "-9223372036854775808". No plus sign, leading zeroes, negative zero, fraction, exponent, or whitespace.
f32, f64String containing a finite JSON decimal, or exactly "nan", "inf", or "-inf".
charString containing exactly one Unicode scalar value.
stringUnicode string, without normalization.
list<T>Array of T; list<u8> uses integer elements, without a base64 shortcut.
tuple<...>Array of exactly the declared arity, in declaration order.
recordObject with exactly the declared field names. Output follows declaration order.
variant{"case":"name"} for a case without payload, or {"case":"name","value":...} for a case with payload.
enumExact declared name string.
flagsArray of distinct declared name strings. Input order is unrestricted; output follows declaration order.
option<T>{"none":null} or {"some":...}.
result<T,E>{"ok":...} or {"err":...}. A branch without a payload uses null.

Unknown, missing, or duplicate object keys are rejected, including duplicate keys that differ only in JSON escape spelling. Variant unit cases reject a value field, including "value":null. A result or option object contains exactly one tag. Tuple arity, field names, case names, numeric ranges, and nested shapes must match the actual component type. Resources (own and borrow), maps, futures, streams, and error contexts are rejected during preparation.

Floating-point decimal strings are parsed directly into the declared precision, so an f32 input does not first pass through f64. Finite decimal overflow is rejected; an infinity requires its explicit special token. Finite inputs follow the target precision's normal rounding, including underflow. Output uses the shortest decimal representation that round trips to that precision, preserves negative zero as "-0", and emits no redundant exponent or fraction syntax. Every NaN is written as "nan"; NaN payload bits and sign are not preserved. Finite numeric bit patterns round trip exactly. Input accepts alternate finite decimal spellings such as "1.25e+2"; output normalizes them, here to "125".

For example, a function with parameters (name: string, count: u64, enabled: option<bool>) accepts:

["example","18446744073709551615",{"some":false}]

Nested option tags preserve the distinction between no outer value and an outer value containing no inner value: [{"none":null}] and [{"some":{"none":null}}] are different values.

Results and declared errors​

Both outcomes retain the complete result array. For a function returning one result<string, variant { unavailable, rejected(string) }>, success can be:

[{"ok":"accepted"}]

An error can be:

[{"err":{"case":"rejected","value":"invalid order"}}]

Only an err branch that is the function's sole top-level result becomes an activation DeclaredError. Its code is always declared-error, its message is always component returned a declared error, its media type is the value media type above, and its payload is the entire encoded result array. Application case names remain payload data. Nested result values, including errors inside a record or list, remain ordinary returned data. If a component function has multiple results, all results remain ordinary returned data.

Bounds and allocation accounting​

ValueCodecLimits defaults are:

LimitDefault
Raw input bytes / emitted output bytes1 MiB each
JSON container depth / schema nesting depth32; configuration permits at most 64
JSON nodes16,384
Decoded UTF-8 bytes in any string or object key256 KiB
Elements or fields in any collection, including the positional array4,096
Examined type nodes and declared name slots per signature sequence4,096
UTF-8 bytes in a declared field, case, enum, or flag name256
Conservatively accounted lifted allocation per component transfer16 MiB
Conservatively accounted decoded input values16 MiB

The raw input is bounded before parsing. A lexical pass checks decoded string lengths, container depth, and nodes before recursive parsing or escaped-string scratch allocation. Nodes include containers, primitive values, and object keys. The bounded parser rejects duplicates and checks collection length before reading excess values. It constructs a bounded input JSON tree and consumes that tree while building typed Wasmtime values. The separate decoded-value allowance accounts for those retained values, including inline storage, names, strings, vectors, and boxes. Output is written directly into a capped byte buffer; serialization does not first construct an unbounded JSON result tree. Output escaping counts against the emitted byte limit.

Wasmtime's hostcall fuel bounds dynamic transfer work, but it does not directly charge every inline record field, tuple element, flag name, or variant box. Before adoption, the backend validates each parameter/result signature, including known host imports, using the same configured hostcall fuel that execution will use. The signature plan adds a conservative fixed inline allowance to the fuel allowance multiplied by the largest inline list-element amplification. Nested lists are inspected too. Arithmetic is checked, and an excessive plan fails preparation. The backend also accounts for aggregate type traversal across the component surface.

The lifted allowance applies to each transfer; the decoded input allowance is separate because input values can remain alive while a guest result or host call is lifted. Input bytes, the bounded parser tree, retained input values, lifted values, and output bytes are distinct allocations. These are conservative application-owned allocation limits, not a byte-exact process RSS claim or a replacement for guest linear-memory and execution limits. Raising one limit does not silently raise another.

Malformed input uses InvalidArgument / invalid-invocation-values; a different media type uses InvalidArgument / unsupported-invocation-media-type. Exceeded input or signature limits use ResourceExhausted / invocation-value-limit. Input failures are rejected before creating a store. Unsupported component types use IncompatibleContract / unsupported-component-value-type during preparation.

An output limit can fail after the guest has consumed resources. The backend returns a bounded guest trap with code result-limit-exceeded, preserving the measured consumption. An otherwise invalid runtime-supplied output value uses trap code invalid-component-result. Both traps have an empty backtrace and result-codec-error metadata identifying the underlying platform error code. Cleanup still completes before the backend returns its reusable-cell proof.

The codec unit fixture at crates/latent-wasmtime/src/values/types.wat exports only types. Its checked-in binary is regenerated with wasm-tools parse types.wat -o types.wasm; unit tests load it with pinned Wasmtime and do not run executable guest code.