Skip to content

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:

  1. One machine-readable statement per protocol - this folder.
  2. A test that fails when the code and the statement disagree - SRC/Xmsg.Protocol.Tests/ProtocolRegistryConformanceTests.cs.
  3. 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.md and 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.