The protocol registry¶
Machine-readable definitions of the SINTRAN XMSG wire: what each field and each bit is, how well we know it, and what the evidence is.
Why it exists¶
The same facts were being carved twice. Word 6 of the header was re-derived as a channel-and-counter model that had been superseded months earlier - while the compiler printed the obsolete warning on every build. Prose documents did not prevent it, because prose sits next to other prose and nothing says which is current.
Three things fix that, and all three are needed:
- One machine-readable statement per protocol - this folder.
- A test that fails when the code and the statement disagree -
SRC/Xmsg.Protocol.Tests/ProtocolRegistryConformanceTests.cs. - A status on every claim, with evidence - so "we think" cannot quietly become "it is".
Start here¶
PROTOCOLS.md is the generated, readable version - field tables, packet
diagrams, and every flow as a sequence diagram. It is produced from the JSON and must not be edited.
catalog.json the master list - which registries exist, and in what order to meet them
|
+-- sintran-wire.json ---> sintran-wire.md
+-- tad-wire.json ------> tad-wire.md generated by generate.py
+-- fa-qform.json ------> fa-qform.md
+-- xrout-services.json > xrout-services.md
Four things stop this drifting, and each closes a different hole:
| Check | Catches |
|---|---|
RegistryCatalogTests |
a registry nobody catalogued - so nothing checks it - or a catalog entry whose file is gone |
RegistryEnumCompletenessTests |
an enum member added in code and never written down, or a registry entry the enum no longer has, or a bitfield that stopped being [Flags] |
ProtocolRegistryConformanceTests |
a value, offset or mask that disagrees with the code, and any claim with no evidence |
GeneratedDocsAreCurrentTests |
markdown generated from an older registry - every page carries the SHA-256 of its source, so dotnet test catches it |
RegistryStructureConformanceTests |
a structure field whose C# properties do not add up to the declared width; a block that could not be generated for C; and two members that would collapse to ONE C symbol |
generate.py --check does the same job for anyone running the generator directly, but the check
does not DEPEND on it: a check somebody has to remember to run is the one that stops happening. The
hash stamp means the test suite catches a stale page without needing python at all.
The second one is the important one. The others check that what IS written down is true; only that one checks that what is TRUE has been written down. It found 74 undocumented entries the day it was added.
Generating code from these¶
generate_csharp.py produces a C# library from the registries - enums carrying the plain-English
meaning, the status and the evidence as XML doc comments, plus accessors for the header. It writes
to a temp folder, and its output compiles with documentation generation on and no warnings.
That is a test of the REGISTRY, not a replacement for the hand-written library. If the registry can reproduce code we already trust, it is rich enough for the C library that is planned and the TypeScript that is under consideration. Whatever it lacks for C# it would lack for those too.
It also refuses to emit prose that would break a doc comment - an ampersand or a stray <. The fix
is to rewrite the registry text, never to escape it: the ND manuals write AND, and a comment full
of entities is unreadable exactly where it matters.
C, as the harder test¶
generate_c.py does the same for C, and it is the better test of the registry
because C demands what C# never did: fixed-width types, explicit byte order,
header guards, one flat namespace. It emits four headers with no gaps, they
compile under gcc -std=c11 -Wall -Wextra -Werror -pedantic, and a test program
decodes a real captured D100 accept correctly - dest 102, source 100, Flags1
0x0021, checksum 0xD9F3.
It also caught a bug in the generator that C# could not: the snake-case helper
split on every capital, so the all-caps ND symbol XSLET became X_S_L_E_T. C#
never saw it because it keeps member names verbatim. A second language tests
the generator; the registry was only ever half of it.
The four protocols, and how they stack¶
| Registry | What the protocol IS |
|---|---|
sintran-wire.json |
the envelope every message rides in - the 7-word header, the bitfields, the ND link |
tad-wire.json |
terminal sessions - CONNECT-TO, the accept, keystrokes and screen output |
fa-qform.json |
remote files - open, read, write, create, list - and the QFORM encoding its bodies use |
xrout-services.json |
the name and routing service - and the seat count that decides if a service has room |
TAD (a terminal session) FA (a file transfer) chat, and anything else we build
| | |
+---------------+--------------+--------------------------+
|
XMSG messages ... addressed by MAGIC NUMBER
| ^
| | XROUT turns a NAME into one,
| | and forwards the first letter
|
SINTRAN datagram: 7-word header + body <- sintran-wire.json
|
ND link layer (Ethernet) or LAPB (HDLC)
Everything is XMSG underneath. A service is just a task holding a port; a port is addressed by a magic number; and XROUT is the phone book that turns a registered name into one. Once two parties have exchanged magic numbers they talk directly and XROUT is out of the way - it is involved in the first letter and nothing after it.
sintran-wire.json - the envelope everything rides in¶
The 7-word SINTRAN header that fronts every datagram, whatever it carries:
markers, the node pair, Flags1 (the datagram sequence for a sender/peer pair),
Flags2 (the frame class), and word 6 - a ones-complement checksum over the
other six, not the "channel and counter" an older superseded model described.
Also the frame-flag bits, the send-option bits, and the ND link frame kinds
underneath.
Read this one first. Every other registry assumes its header.
tad-wire.json - terminals¶
TAD is how a SINTRAN terminal session reaches another machine: CONNECT-TO D103
on one machine drives a letter to *TADADM on the other, which answers with an
accept and then relays keystrokes and screen output as messages. It is the
protocol our node speaks when somebody logs in to us, and the one #34's chat
room is reached through.
Holds the connect exchange field by field, the responder's sequencing rules - its own Flags1, advancing per new connect, the same number for a retransmission - and the terminal opcodes.
fa-qform.json - files¶
FA is the COSMOS file-access service: open, read, write, create, close, and walk a directory on a remote machine. It is what tasks #38, #23 and #33 run on, all three proved against D100.
Its bodies use QFORM, a tag/value encoding shared with other COSMOS services, so the tag rules are documented here too - including the two traps that cost real time: a flat walk descends into constructed values, and SINTRAN pads a body to an even length in a way that can look like the start of a field.
How to read ND's names¶
Everything in these files is named in ND's style: exactly five characters, no vowels to spare. They are not acronyms you can guess, so the registry spells out what each one DOES in plain English. The prefix tells you what kind of thing it is:
| Prefix | Kind | Example, in plain words |
|---|---|---|
XS |
a service you ask XROUT to perform | XSLET - deliver this letter to a service I only know by name |
XR |
a result XROUT gives back, good or bad | XRNSP - that service has no free slots left |
XF |
an XMSG function or option bit | XFWTF - wait until this finishes instead of returning at once |
XM |
an XMSG message type on arrival | XMROU - this arrived via XROUT, not straight from the sender |
So XSDMC and XSDSY are two names for one service - "record how to reach
another machine" - and XSGMC/XSGSY are two names for its opposite, "tell me
what you know about that machine". Nothing about the letters says so, which is
exactly why they are written out.
xrout-services.json - names, routes and seats¶
XROUT is the name and routing service. It answers "where is *FA-SERVER", it
forwards the first letter to a named port, and it keeps the free-connection
count that decides whether a service has room.
That last part is THE SEAT LAW, and it has its own section in the file because it is the rule that cost the most to learn: the seat is spent on FORWARD, so it belongs to the arrival rather than to any kind of message, and leaking one is invisible until the service quietly stops accepting anybody.
All four protocols that carry traffic here are registered. Anything new goes in the same shape rather than a new one.
The loader and the evidence rule live in SRC/Xmsg.TestSupport/ProtocolRegistry.cs,
shared by both test projects - the registry spans layers, so its checks do too:
the header and bitfields are held against Xmsg.Protocol, the TAD opcodes against
Xmsg.Node.
The shape¶
{
"structures": { "<name>": { "size_bytes": 14, "words": 7,
"fields": [ { "word": 6, "byte_offset": 12,
"name": "checksum",
"rule": "~ones_complement_sum(w0..w5)",
"status": "MEASURED",
"evidence": "carved kernel 137314; 3595/3595 frames" } ] } },
"bitfields": { "<name>": { "width_bits": 8,
"bits": [ { "bit": 2, "mask": "0x04", "name": "Letter",
"status": "UNKNOWN",
"evidence": "601-frame sweep; rule never established" } ],
"combos": [ { "name": "Setup", "value": "0x86",
"bits": ["SystemMode","Marker01","Letter"] } ] } }
}
Bitfields are why this is JSON and not prose or XML comments. A bit needs a
position, a mask, a status and evidence of its own, and some bits are shared:
XFHIP and XFRRO are the SAME BIT, disambiguated by XFROU. Prose loses that,
and losing it made a forwarded letter read as high priority.
Status values¶
| Status | Means | You may |
|---|---|---|
MEASURED |
seen on the wire or carved from the kernel | rely on it |
INFERRED |
follows from something measured | implement it, but not cite it as fact |
UNKNOWN |
we copy or mirror it | never compute or vary it |
SUPERSEDED |
believed once, now disproved | not re-derive it |
Every MEASURED, INFERRED and UNKNOWN row must carry evidence. The
conformance test fails otherwise - that rule is the only thing standing between
this file and the documents it replaced.
The rules¶
- Changing a field or bit in C#? Change the JSON in the same commit.
- Adding a constant or bitfield? It goes here, with status and evidence.
- Found something unknown? Add a row to
../WHAT-WE-DO-NOT-KNOW.mdand carry on with the task in hand. Do not chase it there and then. - Settled something? Mark it settled with the date and leave the row.