Plan: Server-to-Server Protocol Document (docs/s2s.md)#
Context#
This repository is a strangler-fig port of IRCnet ircd 2.11.3 (the ircu/IRCnet
lineage) from C to Rust. There is already a docs/design.md describing the internal
architecture of the C side, but nothing documents the wire protocol two servers speak to
each other. Someone implementing a pseudo-server, a services package, or the Rust port's
link layer currently has to reverse-engineer it from ircd/s_serv.c, ircd/s_user.c, and
ircd/channel.c.
The user wants a detailed, accurate S2S protocol reference written to docs/s2s.md, plus
runnable Python demonstrating how to implement a peer "from scratch". Per the clarifying
answers: cover the full S2S lifecycle, use per-section Python snippets in the prose,
and ship one runnable minimal pseudo-server at docs/pseudoserver/main.py.
The protocol facts below were verified directly against the C source (file:line cited), so the doc reflects what this server actually emits — not generic RFC 1459 / TS6 lore.
Verified protocol facts (the doc's source of truth)#
- Lineage / version: IRCnet
ircd2.11.3.PATCHLEVEL="0211030000",IRC_VERSION="2.11.3"(common/patchlevel.h:20-22). This is not ircd-hybrid/ratbox TS6 — noSVINFO, no:UIDcolon prefixes in the TS6 sense. - Framing:
[:prefix] COMMAND [params...] [:trailing],BUFSIZE= 512 incl. CRLF (common/struct_def.h:57), max 15 paramsMPAR(common/parse.c:32). - Identifiers:
SIDLEN= 4 chars, but a SID string is 3 chars (e.g.1AB);UIDLEN= 9 = SID(3) + 6 digits (e.g.1AB000001) (common/struct_def.h:50,64).SV_UID/SV_2_11=0x0001is the capability bit gating the modern (UID) protocol. - Message table (
common/parse.c:37-121): per-command handler array indexed by[server, client, oper, service, unregistered]. Server-relevant commands includeSERVER, SMASK, UNICK, NJOIN, EOB, EOBACK, SQUIT, KILL, SAVE, MODE, NICK, QUIT, KICK, PART, ENCAP, SERVICE. - Handshake (outbound),
ircd/s_serv.c:1044-1062:PASS <password> <pass_version> IRC|<serveropts> <P?><j?><Z?>SERVER <myname> 1 <my_sid> :<my_info>
- PASS handler
m_passcarries version inparv[2], options inparv[3](ircd/s_user.c:3034-3079); version is parsed bycheck_versioninto theSV_UIDbit. - SERVER handler
m_server(ircd/s_serv.c:557-564):parv[1]=name,parv[2]=hop,parv[3]=SID (2.11),parv[4]=version,parv[5]=info. Establishment inm_server_estab; SID validated viasid_valid/find_sid. - Burst order (
ircd/s_serv.c:1246-1377), emitted to a freshly linked peer:send_server_burst→ for each known server::<uplink_sid> SERVER <name> <hop+1> <sid> <verstr> :<info>, or:<uplink_sid> SMASK <sid> <verstr>when masked (s_serv.c:441-449).- For each user:
:<sid> UNICK <nick> <uid> <user> <host> <ip> <umodes> :<realname>(s_serv.c:1261-1268;+sent when umodes empty). Services via:<sid> SERVICE .... - For each channel with members:
:<sid> NJOIN <chan> :[@@|@|+]<uid-or-nick>,...(channel.c:979-1009, prefixes@@=uniqop,@=op,+=voice) then:<sid> MODE <chan> <modes> <params>+ ban/except/invite/reop lists viasend_channel_modes(channel.c:935-972). Empty channels::<sid> NJOIN <chan> .(lock marker,s_serv.c:1308). - End of burst:
:<sid> EOBalone, or:<sid> EOB :<sid1>,<sid2>,...listing already-finished servers (s_serv.c:1315-1377). Peer replies:<sid> EOBACK.
- Ongoing propagation uses source-prefixed relays (
:<uid|sid> COMMAND ...) throughsendto_serv_butone/sendto_match_servs/sendto_serv_v(..., SV_UID, ...):NICK(rename),MODE,TOPIC,KICK,PART,QUIT,KILL(UID target),SQUIT <target_sid> :<reason>,SAVE(nick-collision TS resolution),ENCAP.
Deliverables#
1. docs/s2s.md — the protocol reference#
Match docs/design.md house style: subsystem-organized prose, path:symbol /
path:line references back into the C so readers can verify, fenced wire examples, and
focused Python snippets inline. Proposed sections:
- Overview & lineage — what protocol this is (IRCnet 2.11.3), the
SV_UIDcapability era, where it sits vs. RFC 1459 and TS6, and the one-paragraph lifecycle map (connect → PASS/SERVER → burst → EOB/EOBACK → steady-state propagation → SQUIT). - Message framing — the
[:prefix] CMD params :trailinggrammar, 512-byte/15-param limits, CRLF, prefix forms (SID for servers, UID for users, bare name on legacy links). Snippet: aparse_line()/format_line()pair. - Identifiers (SID / UID) — formats, allocation, why UIDs survive nick collisions,
SV_UIDnegotiation. Snippet: UID allocator. - The handshake — exact
PASS+SERVERexchange, both directions,serveroptscapability flag string, password verification, version →SV_UIDparsing, error/ERRORresponses. Snippet: build and send the handshake; parse the peer's. - The netburst — ordered walkthrough:
SERVER/SMASK,UNICK,SERVICE,NJOIN- channel
MODE/lists, empty-channel lock,EOB/EOBACK. Document member-prefix encoding and the EOB server-list batching. Snippets: emitUNICK; emit/parseNJOIN.
- channel
- Steady-state propagation — prefix-relay model and the routing helpers; per-command
subsections with exact syntax for
NICK(rename),MODE,TOPIC,KICK,PART,QUIT,KILL,SAVE,ENCAP,PRIVMSG/NOTICE,PING/PONG. - Network splits —
SQUITsyntax/semantics, what state to tear down, ghost/SAVEtimestamp resolution at a high level. - Full command reference table — command → syntax → direction → handler
file:line, built fromcommon/parse.c:37-121. - Pointer to
docs/pseudoserver/main.py— how to run it against thisircd.
2. docs/pseudoserver/main.py — runnable minimal pseudo-server#
A single dependency-free (stdlib socket/selectors) Python 3 program, heavily commented,
that:
- Connects (or listens) and performs the
PASS/SERVERhandshake withSV_UIDadvertised. - Parses the incoming burst (
SERVER/SMASK/UNICK/NJOIN/MODE) into in-memory network state (servers, users, channels). - Sends its own minimal burst: itself (already in
SERVER), one pseudo-user viaUNICK, thenEOB; repliesEOBACK. - Runs a read loop dispatching prefixed commands (
PING→PONG,QUIT,NICK,MODE,PRIVMSG,SQUIT), logging each to stdout. - A
__main__with argparse:--connect host:port,--name,--sid,--password.
Mirrors the same line codec and message shapes used in the doc snippets so the two stay consistent.
Critical files#
- Create:
docs/s2s.md,docs/pseudoserver/main.py. - Reference (read-only, for accuracy):
common/patchlevel.h,common/parse.c,common/struct_def.h,ircd/s_serv.c,ircd/s_user.c,ircd/channel.c, anddocs/design.md(style).
Verification#
python3 docs/pseudoserver/main.py --helpruns and shows usage (no import/syntax errors).python3 -c "import ast; ast.parse(open('docs/pseudoserver/main.py').read())"parses.- Extract each ```python block from
docs/s2s.mdandpython3 -c "compile(...)"it to confirm every snippet is syntactically valid. - Manually re-check each documented wire format /
file:linecitation against the C source listed above (the facts section is the checklist). - Optional end-to-end: boot the C
ircdwith a matching N/C-line config and pointmain.py --connectat it; confirm the handshake completes and a burst is received. (Will flag if the local build/config isn't readily available rather than claim success.)