Service Wire Schema Dialect¶
Channel·Transport topic table of contents · Spec table of contents · Previous: 06. Service Wire Protocol
What this chapter answers — how to read
framework/runtime/protocol/service-wire-v1.schema.json: what each top-level key declares, how the tentypes[].kindvalues lay out bytes, and when and how encoders and decoders evaluate keywords such as$ref,$bound,when, andconstraints.Contract ownership — the schema owns the values; this chapter owns how to read them. This chapter lists no command table, field list, numeric bound, or enum value — open the schema for those. Conversely, a new top-level key, a new
kind, or a new keyword in the schema is a dialect change and updates this chapter in the same change. A key or keyword that exists in the schema but not in this chapter is a defect of this chapter, not of the schema.Related contracts — 06. Service Wire Protocol (frame layout, command list, decode bounds, verification requirements) · Message model §5 (typed payload JSON) · tool usage in
framework/runtime/protocol/README.ko.md
| Section | Covers |
|---|---|
| 1. Dialect and Consumers | dialect identity, the three consumers of the schema, the status of sentence-like string values |
| 2. Top-Level Keys | the 28 keys — two dialect identifiers and four groups (protocol constants, layout declarations, semantic declarations, profile declarations) |
| 3. Rules Common to Every Layout | byte order, integer encodings, $ref·$bound, size bounds and allocation order, the kind of failure |
4. The Ten types[].kind Values |
declared keys, byte layout, and decode failure conditions per kind |
| 5. Field Keywords | evaluating constant·minimum·maximum·when·otherwise·required·constraints |
6. commands[] and flags[] |
command entry keys, flag declarations, payload policy, why direction is not in the schema |
| 7. Durable Formats and the Logical Stream | header·checksum·fixture declarations of durableFormats[] and relocationLogicalStreamFormat |
| 8. Semantic Declarations | what semanticContexts, semanticConstraints, and relocationStateMachine pin down |
| 9. Profiles and Owning Clauses | which spec clause each of the 15 profiles pins in machine-readable form |
| 10. Verification Requirements | what confirms a dialect change |
1. Dialect and Consumers¶
schemaDialect is zlink-service-wire-schema-v1 and schemaVersion is 1. These two values
identify the reading rules described in this chapter. When a reading rule changes, the dialect value
changes; when only values change (a new command, an adjusted bound, an added field), the dialect
stays the same.
Three consumers read the schema.
| Consumer | What it reads | Where |
|---|---|---|
| Validator | The whole schema. Checks reference integrity, length capacity, closed unions, TLV order, durable checksums, and literal equality of semantic declarations | validate-service-wire-schema.mjs |
| Generator | The whole schema the validator accepted. Lowering turns this chapter's reading rules into a language-neutral operation IR once; each language renderer emits only that operation's syntax. Constant tables, codecs and the fixture index are produced from one manifest, with drift checks | service-wire-lowering.mjs, render-service-wire-*.mjs, generate-service-wire-{assets,fixtures,codecs}.mjs, service-wire-output-manifest.mjs |
| Runtime codec | The layouts in types, commands, durableFormats, and relocationLogicalStreamFormat. Generated or handwritten, it produces and reads bytes by the rules in this chapter |
each language runtime |
Only a schema that the validator accepts is the reference. Codecs and fixtures built from a rejected schema are not the contract.
Sentence-like string values. Profiles and semantic declarations contain string values that join
a sentence with hyphens, such as
source-memory-payload-is-the-only-handoff-source-for-work-and-timers-accepted-before-capture.
These are not free prose. They are literals that the validator compares byte for byte against
its expected values, and the rule a literal stands for is stated in prose by the owning clause in
§9. Changing such a value therefore changes the owning clause, and
changing the owning clause changes the value — a change on one side alone is rejected by the
validator or in review.
2. Top-Level Keys¶
Apart from the two identifiers schemaDialect and schemaVersion (§1), the 26 keys play four roles.
Protocol constants — values shared by every record.
| Key | Declares |
|---|---|
protocol |
name, magic (byte array), wireMajor, byteOrder, headPrefixBytes, requiredCapability. The head prefix in 06 §2 uses these values as they are |
bounds |
a list of named positive integer limits, { "name", "value" }. Layouts point at them with $bound (§3) |
flags |
the bit declarations of the head prefix flags byte (§6) |
reservedCommandRanges |
closed ranges { "first", "last" } of command IDs that are never assigned |
Layout declarations — the arrangement of bytes.
| Key | Declares |
|---|---|
types |
the list of named layouts. Every layout has name and kind, and the kind determines the remaining keys (§4) |
commands |
the list of command entries. body is the layout of the bytes after the head prefix (§6) |
durableFormats |
header·body·checksum declarations of the four envelopes that persist in a store (§7) |
relocationLogicalStreamFormat |
the declaration of the relocation logical stream that flows directly from source to target (§7) |
Semantic declarations — rules outside the bytes, pinned so that a machine can read them.
| Key | Declares |
|---|---|
semanticContexts |
name, type, and origin of discriminator values that come from outside the layout (§8) |
semanticConstraints |
a list of literal tuples for lifecycle·fence·ordering rules, one per kind (§8) |
relocationStateMachine |
commit order, transitions, and per-command rules of relocation phases (§8) |
Profile declarations — machine-readable pins of rules that other spec clauses own in prose. There are 15, and the table in §9 connects each to its owning clause.
3. Rules Common to Every Layout¶
Byte order. The only value protocol.byteOrder and every durableFormats[].byteOrder accept
is big-endian. There is no place to declare another byte order. The byte-order rule of the wire
itself is owned by 06 §2.
Integer encodings. The encoding of an integer kind is one of five — u8, u16, u32,
u64, i64 — and all are fixed width. This dialect has no variable-length integers. Lengths,
counts, discriminators, and field IDs all point with $ref at one of these five; a durable checksum
states the same width directly as encoding: "u32-big-endian" (§7.1).
$ref. Names a types[].name. A reference to an undeclared name makes the validator reject
the schema. The bytes at a $ref are exactly the named layout — no header or length prefix is
added (when a length is needed, the referenced layout declares it through its own kind).
$bound. Names a bounds[].name. It appears in place of a number in maximumBytes,
maximumItems, maximumEncodedBytes, and maximum. Every declared bound must be referenced at
least once; the validator rejects an unreferenced bound. This keeps one limit from being written as
two different numbers in two places.
Size bounds. maximumEncodedBytes is the encoded bound of the whole layout, and the validator
computes that the sum of the component bounds per kind does not exceed it. runtimeMaximumBytes and
runtimeMaximumEncodedBytes declare per-topology negotiated bounds ($negotiatedBound); a
negotiated value never exceeds absoluteMaximum (a $bound). The order in which bounds are checked
and how an excess is rejected are owned by 06 §2.
The kind of failure. Every condition this chapter calls a "failure" is a protocol error under 06 §2. An encoder does not produce bytes for input that breaks the same declaration.
Encoder and decoder read the same declaration. No keyword exists "for the encoder only" or "for the decoder only". The encoder produces bytes as declared, and the decoder rejects bytes that differ from the declaration. This chapter states the two sides separately per keyword not because the rules differ but because the place where a violation surfaces differs.
4. The Ten types[].kind Values¶
Declared keys and byte layout per kind. "Failure" is the protocol error of §3.
4.1 integer¶
| Key | Meaning |
|---|---|
encoding |
one of u8·u16·u32·u64·i64 |
minimum, maximum |
the value range, as a number or $bound. Written only when narrower than the encoding's own range |
The layout is one big-endian integer of the encoding's width. Failure: too few bytes, a value outside the range.
4.2 enum¶
| Key | Meaning |
|---|---|
encoding |
an integer encoding |
values |
a list of { "name", "value" }. Neither names nor values repeat |
The layout is one integer of the encoding's width, and only the values in values are valid.
Failure: too few bytes, an undeclared value. Names never go on the wire — the generated constant
tables and fixtures use them.
4.3 length-prefixed-bytes¶
| Key | Meaning |
|---|---|
lengthType |
the integer layout of the length prefix ($ref) |
minimumBytes, maximumBytes |
the length range. maximumBytes is usually a $bound |
runtimeMaximumBytes |
a per-topology negotiated bound (§3) |
zeroLengthMeaning |
absent. Written only on optional layouts where length 0 means "no value" |
The layout is the length prefix followed by exactly that many raw bytes. Failure: a short prefix, a
length outside the range, too few bytes. Without zeroLengthMeaning, length 0 is an empty value,
not an absent one.
4.4 length-prefixed-text¶
Adds the following to the keys of length-prefixed-bytes.
| Key | Meaning |
|---|---|
encoding |
utf-8 |
nul |
forbidden. The text carries no NUL byte |
The layout is the same. Failure adds an invalid UTF-8 sequence and a NUL byte. The length counts bytes, not code points.
4.5 struct¶
| Key | Meaning |
|---|---|
fields |
the field list. Declaration order is byte order (§5) |
constraints |
constraints across fields: not-both-zero (fields), field-less-than-or-equal (two fields of the same struct) |
maximumEncodedBytes |
the encoded bound |
trailingBytes |
forbidden. Rejects leftover bytes in a struct whose length is fixed from outside |
presence, scope, storage, metadataMeaning, queueMeaning |
literals stating where this layout appears and what it means. No effect on byte layout; referenced by the rules in §8 and §9 |
The layout is the fields concatenated in declaration order. There are no separators, padding, or
alignment. A field whose when is false does not exist in the bytes (§5).
Failure: any field's failure, a constraint violation, leftover bytes under trailingBytes: forbidden.
4.6 vector¶
| Key | Meaning |
|---|---|
countType |
the integer layout of the count prefix |
maximumItems |
the count bound, as a number or $bound |
item |
the item layout ($ref) |
constraints |
sorted, unique. sorted always carries a comparison — canonical-authority-key-bytes, utf-8-bytes, wire-value, wire-value-then-utf-8-bytes, unsigned-wire-value. unique names what is compared with field (one) or fields (several), and its comparison is optional |
participantIdDerivation |
a literal stating how an item's participant identifier is obtained. No effect on layout |
The layout is the count prefix followed by the item repeated count times. sorted requires
ascending order under the declared comparison; unique requires that no two items compare equal.
Both exist for canonical encoding, so a decoder rejects an unsorted or duplicated vector rather
than repairing it while reading. Failure: a short count, a count over the bound, an item failure, an
order or duplicate violation.
4.7 versioned-vector¶
| Key | Meaning |
|---|---|
layout |
exactly three entries — a version field with constant, a count field whose counts names the repeated field, and a repeated field with kind: "repeat", countFrom, and item |
constraints, maximumEncodedBytes, trailingBytes |
as for vector and struct |
The layout is version, count, then the repeated items. Failure: a version mismatch, a vector
failure, leftover bytes. Layouts that occupy a whole frame of their own, such as metadata-frame,
use this kind.
4.8 versioned-length-delimited¶
| Key | Meaning |
|---|---|
version |
an integer field with constant |
length |
an integer layout with covers: "body". The length covers the whole body |
body |
a field list, as struct.fields |
maximumEncodedBytes, runtimeMaximumEncodedBytes, trailingBytes |
as above |
constraints |
the terminal envelope shape constraints — terminal-success-shape, terminal-failure-shape, existing-has-no-application-payload |
correlationFields, description |
a literal naming the correlation identifier fields, and a description. No effect on layout |
The layout is version, length, body. The decoder reads the length, checks the bound, then reads exactly that many body bytes. A body shorter or longer than the length is a failure. Because the length field fixes this kind's boundary against the bytes that follow, layouts carried in another frame — such as the typed payload envelope — use it.
4.9 conditional-union¶
| Key | Meaning |
|---|---|
discriminators |
the list of values that select a case. Each entry's source gives the origin — the string "wire" (a value read immediately before this layout, whose layout is the entry's $ref), the object { "enclosingField": "<field>" } (a field already read from the struct containing this union), the object { "context": "<name>" } (a name in semanticContexts) |
bodyLengthType, bodyLengthCovers |
a length prefix before the selected case and its coverage (selected-case). A union without them places the case directly, with no length prefix |
cases |
a list of { "when": { discriminator: value, … }, "fields": [...], "constraints": [...] }. Every discriminator is assigned exactly once. constraints is optional and applies the same kinds as a struct's constraints (§4.5) to that case's fields |
otherwise |
when no case matches — protocol-error (reject) or an explicit field list { "fields": [...] } (the current schema uses only the empty list) |
maximumEncodedBytes, trailingBytes |
as for struct (§4.5) |
presence, release |
literals stating when the union appears and when it is released. No effect on layout |
The layout is the source: wire discriminators, the body length (if declared), then the fields of
the selected case. enclosingField and context discriminators occupy no bytes. Failure: a
discriminator failure, no matching case under otherwise: protocol-error, a length inconsistent
with the case, leftover bytes.
4.10 tlv32¶
| Key | Meaning |
|---|---|
totalLengthType |
the integer layout of the total length prefix |
fieldIdType, fieldLengthType |
the integer layouts of each field's ID and length prefix |
fields |
a list of { "id", "name", "$ref", "required", … } |
encodingOrder |
ascending-field-id. Fields appear only in ascending ID order |
duplicateField |
protocol-error. Two fields with the same ID are rejected |
unknownField |
skip-after-length-validation. An unknown ID is skipped after its length is checked |
maximumEncodedBytes, trailingBytes |
as above |
The layout is the total length followed by repeated (field ID, field length, field value). This is
the only kind that can omit or add fields, so extensible descriptors use it. Failure: a field
extending past the total length, an order violation, a duplicate ID, a missing required: true
field, a value failure of a known field. An unknown field is not a failure — the decoder skips its
length.
5. Field Keywords¶
The keywords used by field entries in struct.fields, versioned-length-delimited.body,
conditional-union.cases[].fields, and commands[].body.
| Keyword | Meaning | Encoder | Decoder |
|---|---|---|---|
name |
the field name, unique within its field list | — | — |
$ref |
the layout (§3) | writes with that layout | reads with that layout |
constant |
the value this field must carry (a version, a magic, …) | writes that value | any other value is a failure |
minimum, maximum |
a narrower range that applies to this field alone when $ref is an integer |
rejects a value outside the range | a value outside the range is a failure |
when |
the condition under which this field exists: fieldPresent: "<earlier field>", fieldEquals: { "name": "<earlier field>", "value": … }, or allFlagsSet: [...]·anyFlagsSet: [...] over the head prefix flags. It references only earlier fields and flags |
writes nothing when the condition is false | reads nothing when the condition is false. The condition is decided from values already read |
otherwise |
when when is false — forbidden |
rejects a supplied value when the condition is false | (no bytes are consumed when the condition is false, so a violation surfaces as a failure of the following field) |
required |
tlv32 only. true means the field must be present |
never emits without a required field | absence is a failure |
id |
tlv32 only. The field ID |
— | — |
constraints |
a constraint on one field. Currently contains-protocol-required-capability (a text vector must contain protocol.requiredCapability) |
rejects violating input | a violation is a failure |
description |
a descriptive string stating what the value means. No effect on layout or validation | — | — |
That when references only earlier fields and flags is a property of the dialect — one forward
decode pass decides every condition, and no second pass or backtracking is needed.
6. commands[] and flags[]¶
6.1 Command Entries¶
| Key | Meaning |
|---|---|
id |
a non-zero u8 value. Unique and outside reservedCommandRanges |
name |
a unique name, used by the generated constant tables |
domain |
application or infrastructure. The dispatch difference between the two is owned by 06 §3 |
allowedFlags, requiredFlags |
the flags this command may set and must set. requiredFlags ⊆ allowedFlags |
flagConstraints |
relations between flags — all-or-none (the listed flags are all set or all clear), implies |
body |
the field list of the bytes right after the head prefix (§5) |
payload |
forbidden·optional·required. Whether a typed payload envelope frame follows |
payloadType |
the layout ($ref) of that frame when payload is not forbidden |
semanticConstraints |
the names of semantic rules attached to this command (§8) |
Direction is not in the schema. Which side may send which command, and on which topology it is
allowed, is a runtime rule owned by 06 §3,
§4, and
relocationStateMachine.commandRules. Reading the bytes of a command and deciding whether this
connection may accept the command it read are different layers.
Reply relations are not in the entry either. Which reply answers which request is declared by
the correlation fields of the body (correlationFields in
§4.8) and by relocationStateMachine. There is no generic key
such as replyTo.
6.2 Flag Declarations¶
| Key | Meaning |
|---|---|
name |
the flag name |
bit |
the bit within the flags byte. A power of two, unique |
frame |
the layout ($ref) of the frame that follows when this flag is set |
whenSet, whenClear |
frame-required·frame-forbidden. The frame must be present when set and absent when clear |
controls |
a literal stating that the flag switches part of the command body (a tail, an extension) rather than a frame. Body fields make it their when condition |
An undeclared bit, a bit outside allowedFlags, a missing requiredFlags bit, a
flagConstraints violation, and a frame count that differs from whenSet·whenClear are all
failures under §3. How flags determine the frame count of a record
is owned by 06 §2.
7. Durable Formats and the Logical Stream¶
7.1 durableFormats[]¶
Envelopes that persist in a store beyond a process lifetime. Each entry declares the following.
| Key | Meaning |
|---|---|
name |
the format name |
magic, formatVersion |
the magic bytes and version at the front of the envelope |
flags, flagsType |
the flags value and its integer layout |
byteOrder |
big-endian |
bodyLengthType, body, maximumEncodedBytes |
the body length prefix, the body layout ($ref), and the bound |
checksum |
algorithm (crc32c-castagnoli), encoding, coverage (for example magic-through-body), position (trailing) |
goldenFixture |
the path of the fixture that pins this format's accepted and rejected bytes |
providerInterpretation |
opaque-bytes. The store provider does not interpret these bytes |
The layout is magic, formatVersion, flags, body length, body, checksum, and the checksum covers the
range given by coverage. The decoder verifies magic, version, length, and checksum — all of
them — before reading the body. Any single mismatch is a failure, and there is no partial
recovery. What each format means inside the store (when it is written and when it is deleted) is
owned by 06 §7 and
§9.
7.2 relocationLogicalStreamFormat¶
The declaration of the relocation payload that travels directly from source memory to the target.
body ($ref) is the layout of the whole logical stream, and generatedObjectTree points with
$ref at the sub-trees the runtime handles separately (application state, saved work, timers).
maximumBytes is the logical bound and goldenFixture pins the bytes. chunkSplit states that a
chunk may end at any byte boundary, including inside a frozen record. replay is the generated
decoder's incremental contract, and the five fields of the replay object define these values: the
decoder is fed the chunks in order and decodes without allocating an input buffer for the complete
encoded stream (wholeStreamInputAllocation), returns success only on the final chunk
(completion), and ends with the failure kind named by incompleteFinalChunk when the root is
unfinished at the final chunk or by bytesAfterRoot when bytes remain after the root. The chunk
commands, the checksum convention, and the procedure by which the target feeds chunks are owned by
06 §9.
8. Semantic Declarations¶
8.1 semanticContexts¶
Declarations of values that come from outside the layout. Each entry has name, the value's type
(valueType with $ref, or valueKind: boolean), and source (a literal stating where the value
is obtained). A conditional-union discriminator with source: context references this name. It is
the mechanism for selecting a case by a value that is not in the bytes but that the decoder already
knows (for example, the kind of the original operation). The decoder must hold the context value
before reading the bytes; without it, the union cannot be read.
8.2 semanticConstraints¶
A list of objects, one per kind. implies is a generic relation with contextEquals under
if/then; every other kind pins one bundle of rules (authority state combinations, ownership of
saved work, terminal ownership of a relay, admission fences, …) as tuples of field names and
literal values.
The validator compares each kind's object literally against the expected value in its own code.
The list is therefore not documentation but build-time checked data — when a rule changes, the
validator's expectation and the schema change together, and a schema changed on one side alone does
not pass. By contrast, only one tuple, terminal-failure-integrity (the allowed combinations of
terminal result, failure code, and payload), is lowered into a runtime decoder check. Its fields is
the only list of the wire owners that receive the check: every owner that declares both
terminalResult and failureCode (a type's fields or body, a command's body, or one named
conditional-union case; a layout and an otherwise fallback may not carry the pair) — the
validator checks that the two sets are equal, and the lowering attaches the check only to the owners
in this list. For the other
kinds, the owning clause in §9 states the rule, the runtime
implements it from that clause, and the schema preserves it in a form a machine can compare.
8.3 relocationStateMachine¶
Declares phaseType (the $ref of the phase enum), authorityCommitOrder (per phase, what must
have finished before it and whether a relocation record exists), transitions (allowed transitions),
and commandRules (per command, the phases and roles that may send it). The meaning of the
transitions is owned by 06 §10
and Location runtime; this declaration pins
those rules so the validator can check them against the command declarations.
9. Profiles and Owning Clauses¶
Profile keys pin, as machine-readable literals, rules that other clauses own in prose. To learn what a value means, read the owning clause; when the owning clause changes, the profile changes with it (§1).
| Profile key | Owning clause |
|---|---|
frameworkJsonV1Profile |
Message model §5 |
frameworkMultipartV1Profile |
06 §2 "Framework multipart application profile" |
authorityKeyFormat |
06 §1 "Location Store Authority Key Format" |
authorityStoreAccessProfile |
Location runtime §6.1 |
descriptorEnumerationProfile |
Location runtime §4 · §6.2 |
authorityStoreGenerationProfile |
Location runtime §3.1–§3.3 |
authorityStoreDurabilityProfile |
Location runtime §3.3 · §11 |
ownerLeaseAuthorityProfile |
Location runtime §4.1 · §5 |
relocationRetentionPolicy |
Redis Relocation Store §4 · §5 · §6 |
relocationStorageProfile |
Redis Relocation Store §2–§6 · 06 §9 |
relocationTransferChecksumProfile |
06 §9 "CRC-32C convention and capability" |
maintenanceAdmissionProfile |
Host relocation flow §4 · §5 |
terminationResultProfile |
06 §11 |
livenessProfile |
06 §5 · Transport liveness §3 |
fanoutLivenessProfile |
06 §5 · Transport liveness §4 |
durableFormats, relocationLogicalStreamFormat, and relocationStateMachine are layout and
semantic declarations rather than profiles; their owning clauses are given in
§7 and §8.
10. Verification Requirements¶
A dialect change (a new top-level key, a new kind, a new keyword, or a change to a keyword's
evaluation rule) provides all of the following.
- The validator interprets the key, kind, or keyword, and
--self-testactually rejects a mutation that breaks the rule (06 §1 "Validator"). - The corresponding section of this chapter is updated in the same change. A key, kind, or keyword present in the schema but absent from this chapter is a defect of this chapter.
- A new layout kind pins both accepted and rejected bytes in a golden fixture (06 §12).
A value change (a new command, field, bound, or enum) is not a dialect change and follows only the requirements of 06 §12.
Channel·Transport topic table of contents · Spec table of contents · Previous: 06. Service Wire Protocol