Guest→Host Syscall Payload Contract v1#
Scope#
This document specifies the project-owned binary wire format used for MuduDB guest→host syscalls.
The wire format keeps the 16-byte header introduced in Phase 1 and encodes the
message body with MessagePack. The MessagePack layout is project-controlled:
it is derived from the canonical WIT definitions in
mudu_binding/wit/ and generated by mgen into
custom serde implementations for Rust and custom MessagePack formatters for C#.
The implementation uses rmp_serde (or an equivalent MessagePack library on the
C#/AssemblyScript side) only as the low-level encoder/decoder; it does not
use the default rmp_serde derive behavior, which would serialize Rust enum
variants as maps or strings and would not be stable across guest languages.
The canonical syscall boundary is the WIT function interface declared in
uni-syscall.wit. The header and
MessagePack body described here are the runtime’s internal serialization of
those WIT function calls; guest code should call the WIT functions directly and
let the generated bindings handle the wire format.
The logical data model is the existing mudu_binding::universal type family;
this contract defines the wire encoding of those types.
The obsolete UniDatTypeId / UniMuTypeFamily enumerations are not part of
this contract and have been removed from the binding layer. When a type-family
value needs to cross the boundary, it is carried inside UniDataType /
UniScalar instead.
Registry#
Property |
Value |
|---|---|
Format family |
|
Magic |
|
Current version |
|
Supported range |
|
Registry |
|
The runtime decodes only the current version. An unsupported version fails fast
with ErrorCode::UnsupportedFormatVersion. When the format version changes, MPK
packages are rebuilt for the matching runtime; the runtime does not retain
legacy decoders.
Version history#
Version |
Date |
Summary |
|---|---|---|
1 |
2026-07-01 |
16-byte header (magic, version, flags, message kind) plus a MessagePack body. The MessagePack layout is generated from WIT by |
Payload layout#
Every syscall request and every syscall response is encoded as a single self-describing message:
+--------------------------------+
| Header (16 bytes) |
+--------------------------------+
| Body (variable, MessagePack) |
+--------------------------------+
There is no separate length prefix outside the header; the WIT transport delivers the exact byte range.
Header#
All header fields are big-endian.
Offset |
Size |
Field |
Description |
|---|---|---|---|
0 |
4 |
|
Magic |
4 |
4 |
|
Payload format version. Current value: |
8 |
4 |
|
Reserved. Must be |
12 |
4 |
|
Message-kind discriminant. Still present in the wire header for routing, but the set of valid kinds is defined by |
version is the payload format version, not the WIT interface version.
The WIT function interface in uni-syscall.wit is the authoritative definition
of which syscalls exist and what their request/response types are. The runtime
maps each WIT function to the same internal header + MessagePack path; the
message_kind discriminant is therefore an implementation detail retained for
runtime routing.
Body encoding#
The body is a single MessagePack value. The exact byte sequence is controlled by
the mgen templates and must not be inferred from generic rmp_serde defaults.
Source of truth#
The canonical schema is the set of WIT files under
mudu_binding/wit/:
WIT file |
Rust generated type |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
syscall function interface |
mgen reads these WIT files and emits language-specific source files. For Rust
it uses the templates in
mudu_gen/templates/rust/; for C# it uses
mudu_gen/templates/csharp/.
MessagePack encoding rules#
The generated code obeys the following rules, which override any default
rmp_serde behavior:
WIT construct |
MessagePack encoding |
|---|---|
|
Fixed-length MessagePack array, one element per field, in declaration order. |
|
Two-element MessagePack array |
|
Bare |
|
MessagePack array of encoded |
|
MessagePack nil for |
|
MessagePack array |
|
MessagePack str. |
|
MessagePack bin. |
Primitive scalars ( |
Standard MessagePack integer/float/bool/str representations. |
|
Same encoding as |
Because the layout is generated from WIT, the same rules apply to the C# and
AssemblyScript guests. The Rust side uses rmp_serde with custom
Serialize/Deserialize implementations; the C# side uses custom
IMessagePackFormatter<T> implementations generated from the same WIT.
OID / UniOid#
OID is the logical 128-bit object identifier (mudu::common::id::OID). On
the wire it is encoded as a record with two u64 fields, h and l, which
yields a two-element MessagePack array [h, l]. Decoders reconstruct the logical
OID as ((h as u128) << 64) | (l as u128).
Variant tag tables#
The tags below are derived from the WIT declarations in
mudu_binding/wit/. They are listed here for
convenience, but the WIT files remain the canonical source.
UniScalarValue — variant tags:
Tag |
Variant |
Payload |
|---|---|---|
0 |
|
|
1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
|
10 |
|
|
11 |
|
|
12 |
|
|
13 |
|
|
14 |
|
|
15 |
|
|
16 |
|
|
17 |
|
|
18 |
|
|
19 |
|
|
20 |
|
|
Note: the WIT uni-scalar-value defines blob for binary scalar payloads;
UniScalarValue::Blob carries a Vec<u8>. U128 / I128 use 16-byte
big-endian byte arrays to stay portable across guest languages that lack a
native 128-bit integer MessagePack type.
UniDataValue — variant tags:
Tag |
Variant |
Payload |
|---|---|---|
0 |
|
|
1 |
|
|
2 |
|
|
3 |
|
|
A UniDataValueField is a record of [field_name: string, field_value: UniDataValue].
UniDataType — variant tags:
Tag |
Variant |
Payload |
|---|---|---|
0 |
|
|
1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
Note: box<T> is not a standalone variant. It appears only as a WIT
syntax sugar for recursive types (array, option) and is transparent on the
wire.
UniScalar — WIT enum, bare u32 discriminant:
Value |
Name |
Value |
Name |
|
|---|---|---|---|---|
0 |
|
11 |
|
|
1 |
|
12 |
|
|
2 |
|
13 |
|
|
3 |
|
14 |
|
|
4 |
|
15 |
|
|
5 |
|
16 |
|
|
6 |
|
17 |
|
|
7 |
|
18 |
|
|
8 |
|
19 |
|
|
9 |
|
20 |
|
|
10 |
|
UniScalar and UniScalarValue now share the same tag numbering.
Composite type layouts#
The MessagePack encoding of each record/variant follows the generic rules above. For reference, the field order and payload shapes are:
UniOid—[h: u64, l: u64](record).UniSqlStmt—[sql_string: string](record).UniSqlParam—[params: list<UniDataValue>](record).UniTupleRow—[fields: list<UniDataValue>](record).UniFieldAttr—[attr_name: string, attr_value: string](record).UniRecordField—[field_name: string, field_type: UniDataType, field_attrs: list<UniFieldAttr>](record).UniRecordType—[record_name: string, record_fields: list<UniRecordField>](record).UniResultType—[ok: option<UniDataType>, err: option<UniDataType>](record).UniResultSet—[eof: bool, row_set: list<UniTupleRow>, cursor: list<u8>](record).UniError—[err_code: u32, err_msg: string, err_src: string, err_loc: string, err_details: list<u8>](record).UniQueryArgv—[oid: UniOid, query: UniSqlStmt, param_list: UniSqlParam](record).UniCommandArgv—[oid: UniOid, command: UniSqlStmt, param_list: UniSqlParam](record).UniQueryResult—[tuple_desc: UniRecordType, result_set: UniResultSet](record).UniCommandResult—[affected_rows: u64](record).UniCommandReturn— variant:ok(UniCommandResult)orerr(UniError).UniQueryReturn— variant:ok(UniQueryResult)orerr(UniError).UniSessionOpenArgv— currently a hand-written Rust record; its wire shape is[worker_id: UniOid]. It does not yet have a dedicated WIT file; if it is moved into WIT in the future it will follow the same encoding rules as above.
Relationship to the current session codec#
The KV syscalls (Open…Range) are currently hand-written binary in
handle_sys_session.rs.
Their existing conventions match this contract’s primitives. v1 wraps those
payloads in the 16-byte header and uses the project-controlled MessagePack
Result<T, E> encoding for errors, instead of the legacy MERR magic escape.
Unifying the session codec onto the same mgen-generated MessagePack path is
Phase 2 work.
Integrity mechanisms#
Magic check: decoders reject messages whose magic is not
0x4D53_5350.Version check: decoders reject any version outside
[1, 1], mapping toErrorCode::UnsupportedFormatVersion.Flags check: decoders reject any non-zero
flagsvalue.Message-kind validation: decoders reject unknown/
0message_kindvalues.Length checks: decoders require at least 16 bytes for the header.
MessagePack structural validation: decoders reject malformed MessagePack payloads, unknown variant tags, and record arrays of unexpected length.
UTF-8 validation:
stringfields must be valid UTF-8.
Structural body decode failures map to ErrorCode::Decode. Magic mismatches map
to ErrorCode::CorruptedData via the compatibility registry.
Compatibility matrix#
Reader \ Writer |
v1 |
|---|---|
v1 |
Compatible |
Only version 1 is supported.
Upgrade and rollback rules#
Upgrade: A future v2 changes the header
versionand/or the body layout. When v2 is introduced, MPK packages are rebuilt for the matching runtime and offline migration handlers convert stored fixtures/test data.No legacy decoders: the runtime decodes only the current version. A message with an unsupported version fails fast with
ErrorCode::UnsupportedFormatVersion.No legacy MPK support: packages are rebuilt per ABI version bump; versioning exists to make future upgrades explicit and controllable, not to preserve cross-version compatibility.
Deprecation policy#
Version 1 may be deprecated only after:
All guest bindings (Rust, C#, AssemblyScript) emit and accept the new version.
The new version has been the default for at least one release cycle.
MPK packages have been rebuilt against the new runtime.
References#
Registry:
mudu/src/compat/mod.rsWIT schema source:
mudu_binding/wit/Syscall function interface:
mudu_binding/wit/uni-syscall.witmgenRust templates:mudu_gen/templates/rust/mgenC# templates:mudu_gen/templates/csharp/universaltype family (Rust):mudu_binding/src/universal/Existing hand-written KV codec:
mudu_binding/src/codec/handle_sys_session.rsSQL request/response codec:
mudu_binding/src/codec/handle_sys_incoming.rs,handle_sys_outcoming.rsHost syscall entry points:
mudu_runtime/src/interface/kernel.rsDesign plan:
doc/cn/todo/project-controlled-guest-host-abi.md