Furth FortuneEngineering reference
TECHNICAL DOCUMENTATION

2027 · MerTek technology

On this page

Core system / System overview

Rune

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 rolling upgrade across 140 parts-service nodes

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.

  1. 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)
    }
  2. 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"
    }
  3. 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
    }
  4. 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
    }
  5. 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
    }
What happened

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

A checked message exchange

The receiver checks schema and type identity before it reads the payload.

  1. Typed record AssetStatus v4
  2. Rune encode schema ID + fields
  3. Transport compact bytes
  4. Receiver accept v4 or refuse by name

Inside the system

How the parts work together

Follow the data, decisions, and access boundaries. Each diagram explains a concept used by applications.

Map 01

Logical and encoded shapes

Applications use typed values; transport uses a compact checked form.

  1. Logical record named fields + types
  2. Schema stable shape
  3. Encode compact bytes
  4. Decode same logical record
Map 02

One spelling per type

Retired or unknown spellings fail instead of becoming aliases.

  1. Type name declared spelling
  2. Registry lookup one authority
  3. Known encode with identity
  4. Unknown named refusal
Map 03

Encoded sections

Header, schema and payload have separate jobs. A reference is a stored id, never a memory address.

  1. Header format + version
  2. Schema names + complete types
  3. Payload typed bytes
  4. References store ids or graph positions
Map 04

Rolling-fleet compatibility

A schema fingerprint is checked before an older node reads a new message.

  1. Sender schema fingerprint A
  2. Message fingerprint + bytes
  3. Receiver schema supported set
  4. Accept or refuse before field access
Map 05

Graph snapshot

Stable keys replace memory pointers inside a declared snapshot scope.

  1. Object graph linked values
  2. Scope included objects
  3. Stable keys links encoded
  4. Restore relationships rebuilt

Key parts

What the system does

These are the main boundaries, inputs, outputs, and failure rules. The examples show a specific use of each part.

One spelling per value

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.

For example

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.

Schema identity travels with the bytes

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.

For example

A receiver compares an incoming fingerprint with the shapes it accepts before it reads one value.

Stable keys instead of pointers

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.

For example

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

Rune in code

Each example is short and complete enough to read on its own. Hover a dotted word for its definition.

Trident

Field order never changes the bytes

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
}
Trident

Name exactly what differs

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
}
Trident

A change applies only to its own base

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)
}
Trident

A work package as one graph value

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)
}
C

Read the framing before the body

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

Example uses

  • Messages between nodes during a rolling upgrade
  • Records that must hash the same everywhere
  • Saved work packages and other linked snapshots

Review questions

What the team must decide

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

Contact us
FURTH FORTUNE / ENGINEERING REFERENCEBack to top ↑