Learning a new XMSG sub-protocol — methodology¶
How to reverse-engineer a NEW service on top of XMSG (the file server *XM-FIDO,
app-to-app user traffic, mail, SINTRAN's own inter-node services, …) from captured
traffic, and fold it into the specs and the dissector. This is the repeatable process
distilled from the TAD terminal-protocol work — TAD is used throughout as the worked
example of what each step produces.
The core thesis: the transport is protocol-AGNOSTIC¶
Everything below the payload is already solved and is IDENTICAL for every service:
HDLC framing + FCS, the ND LAPB dialect (including the odd-length address bit), the
13-byte SINTRAN header, the datagram-sequence/Counter/channel envelope (the seed
model), the secure ACK, reachability/resync, the odd-length address rule,
and the port scheme. See XMSG-PROTOCOL.md.
So when a new sub-protocol appears, only three things are new:
- the port-0 dispatch identity — a new SERVER name (XSLET letter) or a new numbered
SERVICE (XMCSM low byte), see
XMSG-PROTOCOL.md§7 / §18.8 S10; - the XMCSM class words its data frames use (each new
Flags2 = XMCSM>>16is a new channel lane /baseLowvalue); - the trailer vocabulary — the opcode/message format inside the frames.
Everything else you already know how to decode. That is why this process is fast: you spend your effort only on those three, and you prove the rest still holds with two automated scans before touching anything.
Step 0 — capture the right traffic¶
Exercise the new service with REAL traffic between two machines and capture it on the
nd100x --hdlc TCP bridge. Save the .pcapng in E:\Dev\Ronny\X25Emulator\pcap\.
Good first targets and how to elicit them:
- File server family: the live
list-serversregistry (XMSG-PROTOCOL.md §7.1) shows a whole family of file/COSMOS servers not yet captured —*XM-FIDO(port 4),*COSPO(5),*FA-FSA(7),*XFTRA(8),*FA-SERVER(11). Do any COSMOS remote-file operation between the machines (remote file access / copy / a File-Server command); the first XSLET letter naming one of these opens that sub-protocol. Census the names (step 2) to see WHICH server the operation actually drives — a file transfer may touch several. - App-to-app XMSG: run a user program on each node that opens a port (
XFOPN) and sends/receives via the XMSG API (XFSND/XFRCV). - Capture BOTH directions and let the session run to completion (setup → work → teardown) so the full shape is present. For sequencing questions, capture with timestamps and, where relevant, several back-to-back sessions without restart.
The XMSG-OPEN-ITEMS-2026-07-06.md "combo capture" script lists concrete operator steps.
Step 1 — prove the transport still holds (two conformance scans)¶
Before decoding anything new, run the two scans that instantly confirm the new traffic obeys the known transport. If they pass, you KNOW only the application layer is new and you can trust every envelope/ACK byte.
- Envelope scan — for every Data (0x0E) frame recompute
Counter = (seed − Flags2low − Flags1) & 0xFFandChannel = 0xDE − (XMCSM>>24) − epoch, and compare to the wire bytes. Expect 0 mismatches (VERIFIED 753/753 on the current corpus). A mismatch means either a new per-link seed or — more interesting — a genuinely new envelope behaviour to chase. - ACK scan — for every subtype-0x03 frame check
trailing = (S_ack − Flags2low − ackedFlags1) & 0xFF,S_ack = seed + 0x0B,channel = 0xDE − epoch(ackedFlags1). Expect 0 mismatches (VERIFIED 904/904).
The hdlc_tcp.lua dissector performs both live (mismatches show as expert warnings), so
tshark -r cap.pcapng -Y hdlc_lapb -V and grep for warnings is the fastest scan; a
standalone Python scan over raw= hex lines is the reproducible one.
TAD example: these scans are exactly how the envelope model was proven and how the seed-per-responder hypothesis was refuted — the arithmetic held across every capture, so all TAD work could assume a correct envelope and focus purely on the payload.
Step 2 — identify the service (name census / dispatch)¶
Every request arrives at port 0 and forks on the XMCSM low byte.
- XSLET letters (low byte 0x41): scan every letter's
FF <len> 2A <name>for the SERVER name. This is an exhaustive census — extract the name from every frame carrying theFF ll 2Asignature. Compare the names to the livelist-serversregistry (name → logical port → free session slots). - Numbered services (other low bytes): e.g.
0x4B= XSGSY routing; these carry NO name — the code IS the selector.
TAD example: the census found TADADM as the only name on the wire (18/18 letters);
list-servers additionally showed *XM-FIDO at logical port 4, unaddressed in any
capture — i.e. the file server is the obvious next name to expect. Terminology
(server vs service vs function) is fixed in XMSG-PROTOCOL.md §7.
Step 3 — map the new XMCSM class words¶
List the distinct XMCSM values (and therefore Flags2 = XMCSM>>16 class words) the new
service uses. For each, record baseLow = (seed − Flags2low) & 0xFF and which channels
it rides per epoch. Each new class is a new lane the envelope already knows how to place;
you are just cataloguing which the service uses.
TAD example: four classes — 0x0400 (letters), 0x0108 (terminal data), 0x0008
(control: ESCA/0x20/DCON), 0x0006 (the 0xFD notification). A file server will
likely introduce its own class word(s) — record them the same way.
Step 4 — decode the trailer vocabulary¶
The trailer is the genuinely new part. First pass: try the TAD-style
[opcode][count][data…] chain walker (skip 0x00 bytes between messages — they are
word-align pads or the 16-bit-opcode prefix of certain opcodes). Catalog every opcode:
its direction on the wire, data length, where it appears, and any payload structure.
Then NAME the unknown opcodes by matching their byte values against the kernel symbol tables — this is the highest-leverage technique and it works because the missing kernel SOURCE left the symbol NAMES behind:
- Symbol lists:
SINTRAN\NPL-SOURCE\SYMBOLS\{K03,L07,M06}\*-SYMBOL-LIST.SYMB.TXTandSINTRAN\XMSG\XMSG-VALUES-M.SYMB— values are OCTAL; small values = opcodes/offsets, large = code/data addresses. - Look for a coherent FAMILY of related names at consecutive values (that is an enum).
- Confirm usage in the NPL sources
SINTRAN\NPL-SOURCE\NPL\*.NPL(MP-*,RP-*for the process split; message headers pack the opcode asvalue<<8).
TAD example: the 7* family named the wire opcodes — 0x0B = 7LUN (which independently
confirmed the measured LU=768+XX), 0x20 = 7ESRS escape response, 0xFD = 7POLL,
0x06/0x07 = 7CORQ/7CORS connect request/response, etc. — see TAD doc §2.1. A file
server will have its own family (look for FILE/FS/FIDO/XF*-adjacent names); the same
octal-matching method applies. The symbol-hunt agent prompt used for TAD is a reusable
template.
Step 5 — record the session shape and port lifecycle¶
Classify the exchange against the three known shapes (XMSG-PROTOCOL.md §18.8 S10):
- session-opening (accept → port-assign → session, like connect-to),
- letter-only (query + reply, no session, like list-systems),
- request/reply service (like list-route/XSGSY).
Note who allocates which port and when (§7.1): the client's own port (XFOPN on the
originating node), any well-known server wire port, and any per-session port the server
mints. Record the ACK discipline (does every data frame get its 0x03 ACK, as TAD does?)
and the teardown.
Step 6 — write it up (with discipline)¶
Document the transport-level findings as a new scenario/section in XMSG-PROTOCOL.md
(so the transport stays one source of truth) and the application layer either there or in
a companion doc (as TAD has its own TAD-Message-Formats.md). Non-negotiable discipline,
the reason these docs are trustworthy:
- Tag every claim VERIFIED (byte-for-byte in a capture, cite frame/line) / INFERRED (reasoned, state the reasoning) / ASSUMPTION / UNKNOWN. Never present a guess as a fact.
- Prefer a MEASURED scan (counts like "18/18", "753/753") over an eyeballed sample.
- When a rule can't be settled from the corpus, say so and state the exact capture or
experiment that would settle it (feed it into
XMSG-OPEN-ITEMS-*.md). - Add worked scenarios with real bytes; implementers read those first.
Step 7 — extend the dissector, then re-verify¶
Add the new opcodes / class handling / letter names / validators to
SINTRAN\Devices\HDLC\WireShark\hdlc_tcp.lua, mirroring the spec. Verify with
tshark -r <cap> -Y hdlc_lapb -V over several captures: zero Lua errors, zero NEW false
expert-warnings on known-good traffic. Install to ONE Wireshark plugin folder and reload.
Details and guardrails: the folder's README.md and the xmsg-decode skill's
"Upgrading the dissector" section.
Step 8 — (optional) build it into the C# node¶
The reference node under SINTRAN\XMSG\SRC\ is structured for this: the transport
(Xmsg.Protocol envelope/ACK) is shared, and a new service is a new handler alongside
TadTerminalResponder. Per the server-vs-service terminology, the pluggable named
services (TAD, and a coming file server) are IXmsgServer-shaped; reserve "service" for
the port-0 router dispatch.
Quick checklist¶
[ ] 0. Capture real new-service traffic (both directions, full session), save to pcap/
[ ] 1. Envelope scan + ACK scan → 0 mismatches (transport confirmed)
[ ] 2. Name census (FF ll 2A) + compare to list-servers → the new server/service
[ ] 3. Distinct XMCSM class words → baseLow lanes
[ ] 4. Trailer opcode catalog → name them via the octal symbol tables + NPL usage
[ ] 5. Session shape (session / letter-only / request-reply) + port lifecycle + ACK rule
[ ] 6. Write up in XMSG-PROTOCOL.md (+ companion), VERIFIED/INFERRED/UNKNOWN tagged
[ ] 7. Extend hdlc_tcp.lua, tshark-verify (0 errors/0 new warnings), install+reload
[ ] 8. (optional) add the IXmsgServer handler in the C# node
References¶
XMSG-PROTOCOL.md— the transport (envelope §18.5, ACK §6, ports §7.1, dispatch §7/§18.8 S10, scenarios).TAD-Message-Formats.md— the worked application example (opcode census §2.1, session §22).XMSG-OPEN-ITEMS-2026-07-06.md— the capture/experiment plan, including the*XM-FIDOopener.Devices/HDLC/WireShark/hdlc_tcp.lua+README.md— the dissector and how to run/upgrade it.xmsg-decodeskill — the condensed operational version of this method.