Logical and encoded shapes
Applications use typed values; transport uses a compact checked form.
- Logical record named fields + types
- Schema stable shape
- Encode compact bytes
- Decode same logical record
2027 · MerTek technology
Core system / System overview
Typed bytes that name their own shape
Rune is the binary format every MTK value takes in memory, in MerDB and on the wire. Each document carries its field names and complete types beside its values, in one canonical byte spelling. A receiver can check exactly what it holds before it reads a field, and a Trident program holds a Rune document as a chest value.
Worked example
A fleet logistics team runs its parts service on 140 nodes and upgrades them in waves of 20 over two days. Release 4.2 adds a torqueNm field to the part-status message. For those two days 4.1 and 4.2 nodes exchange messages, and a 4.1 node must never act on a 4.2 message it cannot read in full.
Release 4.1 declares the message as an ingot, an exact record. Encoding writes three parts. A 94-byte sealed header holds the lengths, the schema version and two SHA-256 digests. A schema section lists each field’s name and type, and a value section holds the values. This message comes to 182 bytes.
ingot partStatus {
text serial = ""
text status = "ready"
i32 bin = 0
}
buffer send(text serial, text status, i32 bin) {
partStatus update = { serial = serial, status = status, bin = bin }
return t.chest.encode(update)
}Every message carries a schema fingerprint, the SHA-256 of its schema section and schema version. Two part-status messages share one fingerprint whatever their values. The extra field in 4.2 gives a new one.
sha256? shapeOf(chest doc) {
chest facts = t.chest.digests(t.chest.encode(doc))
return facts.schemaDigest
}
text main(text[] argv) {
chest monday = { text serial = "P-204", text status = "ready", i32 bin = 12 }
chest tuesday = { text serial = "P-911", text status = "installed", i32 bin = 3 }
chest newer = { text serial = "P-204", text status = "ready", i32 bin = 12, f64 torqueNm = 41.5 }
assert (shapeOf(monday) == shapeOf(tuesday), "values never move the fingerprint")
assert (shapeOf(monday) != shapeOf(newer), "a new field does")
return "ok"
}A 4.1 node receives a 4.2 message. expect compares the complete field set with the ingot before it binds a single name, so the else arm runs. The node logs what differs, added torqueNm, and acts on nothing.
text receive(buffer bytes) {
chest doc = t.chest.decode(bytes)
expect partStatus update = doc else {
partStatus known = { serial = "", status = "ready", bin = 0 }
chest change = t.chest.diff(known, doc)
chest[] rows = change.props
foreach (row in rows) {
text kind = row.kind
text name = row.name
if (kind != "value") {
t.log.warn("refused part status: " + kind + " " + name)
}
}
return "refused"
}
return update.serial + " is " + update.status
}A message damaged in transit stops sooner. A sealed document’s digests are recomputed before any field is read, so one changed byte makes the whole document invalid.
text main(text[] argv) {
chest update = { text serial = "P-204", text status = "installed", i32 bin = 12 }
u8[] raw = t.buffer.toBytes(t.chest.encode(update))
raw[raw.count() - 1] = raw[raw.count() - 1] + 1
chest verdict = t.chest.check(t.buffer.fromBytes(raw))
bool ok = verdict.ok
text status = verdict.status
return text(ok) + " " + status
}The 4.2 reporting service needs only serial and status. It declares them as an outline, an open record, so it reads 4.1 and 4.2 messages alike and leaves any extra fields in place.
outline partSeen {
text serial = ""
text status = ""
}
text label(chest doc) {
expect partSeen seen = doc else { return "unreadable" }
return seen.serial + " " + seen.status
}For two days each 4.1 node refused every 4.2 message with one log line naming torqueNm, while the reporting service read both versions. No node read a torque value where it expected a bin number, and the last wave finished with no conversion code anywhere in the fleet.
System diagram
The receiver checks schema and type identity before it reads the payload.
Inside the system
Follow the data, decisions, and access boundaries. Each diagram explains a concept used by applications.
Applications use typed values; transport uses a compact checked form.
Retired or unknown spellings fail instead of becoming aliases.
Header, schema and payload have separate jobs. A reference is a stored id, never a memory address.
A schema fingerprint is checked before an older node reads a new message.
Stable keys replace memory pointers inside a declared snapshot scope.
Key parts
These are the main boundaries, inputs, outputs, and failure rules. The examples show a specific use of each part.
Fields are stored in name order, each type has one canonical spelling, and each value has one byte encoding. Two programs that build the same record in different orders produce identical bytes.
A depot node and a cloud service hash the same part-status record and get the same SHA-256, with no normalizing step between them.
The schema section lists every field’s name and complete type, including units, nullability and array shape. Its digest, the schema fingerprint, changes when a field or type changes and stays fixed when only values change.
A receiver compares an incoming fingerprint with the shapes it accepts before it reads one value.
A field that refers to another record holds the id the store issued for it, and a graph value stores each arc as a vertex position. A saved snapshot carries no memory addresses, so it restores the same relationships on any machine.
A pump-swap work package with four steps and their order saves on a tablet and reopens on a server with every dependency intact.
Code
Each example is short and complete enough to read on its own. Hover a dotted word for its definition.
Both records encode to identical bytes. t.chest.fields lists the stored order, which is name order: bin, serial, status.
text main(text[] argv) {
chest first = { text status = "installed", text serial = "P-204", i32 bin = 12 }
chest second = { i32 bin = 12, text serial = "P-204", text status = "installed" }
assert (t.chest.encode(first) == t.chest.encode(second), "one value, one spelling")
text order = ""
foreach (column in t.chest.fields(first)) {
text name = column.name
text spelling = column.typeSpelling
order = order + name + ":" + spelling + " "
}
return order
}
A comparison reports each differing field by name and kind. Here bin changed type from i32 to i64 and torqueNm was added. It names what changed and never carries the values themselves.
text main(text[] argv) {
chest older = { text serial = "P-204", text status = "installed", i32 bin = 12 }
chest newer = { text serial = "P-204", text status = "installed", i64 bin = 12, f64 torqueNm = 41.5 }
chest change = t.chest.diff(older, newer)
chest[] rows = change.props
text report = ""
foreach (row in rows) {
text kind = row.kind
text name = row.name
report = report + kind + " " + name + "; "
}
return report
}
The change document names the record it came from by that record’s schema and payload digests, and sets one field. Applying it to any other record faults digest_mismatch.
text main(text[] argv) {
chest before = { text serial = "P-204", text status = "ready", i32 bin = 12 }
chest after = { text serial = "P-204", text status = "installed", i32 bin = 12 }
chest delta = t.chest.change(before, after)
chest rebuilt = t.chest.apply(before, delta)
assert (t.chest.encode(rebuilt) == t.chest.encode(after), "the change rebuilds the record")
return t.draft.format(delta)
}
Each arc is a vertex position, such as { 1, 2 }, never a pointer. The acyclic rule is checked whenever the value is built or opened, so a restored package cannot contain a loop.
outline jobStep {
text name = ""
f64 hours = 0.0
}
buffer savePumpSwap() {
graph<jobStep,acyclic> order = [
{ name = "Drain hydraulics", hours = 1.5 } { 1, 2 },
{ name = "Remove pump", hours = 2.0 } { 3 },
{ name = "Order seals", hours = 0.5 } { 3 },
{ name = "Fit new pump", hours = 3.0 }
]
chest workPack = {}
workPack.order = order
return t.chest.encode(workPack)
}
A receiver reads the header alone to learn a document’s exact length, waits for that many bytes, then opens the whole document with the strict reader. The header says how long it is; only the full open says whether it is valid.
/* bytes holds at least one complete header. Sets *needed and returns
CHEST_STATUS_LIMIT while the rest of the document is still arriving. */
chest_status next_document(const uint8_t *bytes, size_t available,
const chest_options *options, chest **document, size_t *needed)
{
uint32_t fields = 0, schema_version = 0;
size_t schema_size = 0, payload_size = 0, header_size = 0, total = 0;
int sealed = 0;
*needed = 0;
if (!chest_header_decode(bytes, available, &fields, &schema_version,
&schema_size, &payload_size, &header_size, &total, &sealed))
return CHEST_STATUS_FORMAT;
if (available < total) {
*needed = total;
return CHEST_STATUS_LIMIT;
}
return chest_open_with_options(bytes, total, options, document);
}
Uses
Review questions
Which receivers need an exact record and which can read an open one
Which fields a newer release adds, and how older nodes should report a refusal
Where documents cross a boundary and need a sealed image