Messages
Messages are the primary data type in Vexil -- ordered, typed fields with explicit ordinals.
message SensorReading {
channel @0 : u4
kind @1 : SensorKind
value @2 : u16
sequence @3 : u32 @varint
}
Fields are encoded in ordinal order. Each field has a name, an ordinal (@N), and a type.
Field ordinals
Ordinals determine wire order. They must be unique within a message but do not need to be sequential. Gaps can reserve positions and make a schema easier to read, but filling a gap or appending a field still changes the message contract.
message Config {
name @0 : string
# @1 was removed
timeout @2 : u32
retries @3 : u8
}
Field annotations
Fields can carry encoding annotations:
message Packet {
sequence @0 : u32 @varint # LEB128 variable-length encoding
delta @1 : i32 @zigzag # ZigZag encoding for signed values
payload @2 : bytes
}
Wire encoding
Fields are packed in ordinal order with LSB-first bit packing. Sub-byte fields (like u4) pack tightly -- two u4 fields occupy a single byte. After all fields, the encoder flushes to a byte boundary.
Unknown fields
Generated message types carry target-specific storage for unknown bytes, but decoders do not populate it. The storage is empty after every decode. Vexil does not currently provide lossless unknown-field round-tripping.
Message values are not internally length-delimited. A decoder therefore cannot identify unknown trailing fields safely when the message is nested inside a parent or inline aggregate. Adding a field is classified as breaking. See schema evolution for the boundary and migration guidance.
See the language specification for the full normative reference.