• sexyz: ZSINIT is never sent, and a received one is acknowledged with i

    From Rob Swindell@1:103/705 to GitLab issue in main/sbbs on Sun Aug 30 03:34:28 2026
    open https://gitlab.synchro.net/main/sbbs/-/issues/1230

    sexyz never sends a ZSINIT frame, and a received one is acknowledged with its contents thrown away. Neither the sender's escape declaration nor, more importantly, the **Attn sequence** it carries is stored or acted on.

    In `zmodem.c`, `ZSINIT` shares a `case` with `ZCOMMAND` that reads the data subpacket, ACKs it, and returns. `ZATTNLEN` is defined in `zmodem.h` and the string "attn" appears nowhere else in the engine. There is no `zmodem_send_zsinit()`.

    ## What the frame is for

    ZSINIT is how a sender declares two things before data starts: that it is escaping control characters or the 8th bit (`TESCCTL`/`TESC8`), and the **Attn sequence** — the byte string a receiver should transmit to interrupt the sender mid-stream, typically after an error, when the sender is streaming and not listening for headers.

    The escape declaration half is harmless to ignore: `TESCCTL` tells us the sender *is* escaping, and ZDLE decoding handles that without any special action. The Attn half is the real gap — a sender that expects a specific interrupt sequence will not get it from us.

    ## Severity: low, and measured rather than assumed

    This is not currently breaking anything I can find:

    - All twenty sender/receiver pairs in `docs/zmodem_comparison.md` §4.1 transfer byte-identically.
    - Error recovery is the strongest of the field, 10 of 10 in both directions at a 3e-6 bit-error rate, where the all-lrzsz pair manages 7 of 10. Interruption evidently works well enough through ZRPOS.
    - DSZ sends a ZSINIT exactly when it has something to declare — observed once, for `sz -e`. sexyz discarded the flags and the transfer still completed byte-identically (wire 10,557 bytes).

    So this is a conformance gap rather than a live defect, filed so it is not rediscovered as news, and because it is the one item from the option audit with a plausible functional consequence.

    ## Comparison

    | | Sends ZSINIT | Honours a received one |
    |---|---|---|
    | **sexyz** | **no** | **no** — ACKed, contents discarded |
    | lrzsz | yes | yes |
    | zmtx/zmrx | **`zmtx` no**; `zmrx` handles a received one | partial |
    | Forsberg DSZ | yes, when it has something to declare | — |

    `zmtx` not sending one either is worth noting: this is not a Synchronet peculiarity, and two of the four implementations get by without it.

    ## If implemented

    The useful half is storing a received Attn string and using it to interrupt a streaming sender, in place of or alongside the current ZRPOS-driven recovery. Sending our own ZSINIT is only worth doing if we ever have something to declare — we do not escape control characters unless asked, and ESC8 was removed in cf91dbd29c (judges-20-spas, 2026-08-30).

    Found during an audit of every sexyz option — command-line, `sexyz.ini`, and protocol-negotiated — prompted by #1229, where an option turned out to have been inert for 21 years. The rest of that audit came back clean: all 43 ini keys and all 16 command-line options reach live code, and every knob that should alter the negotiated ZRINIT was verified to do so on the wire.

    — *Authored by Claude (Claude Code), on behalf of @rswindell*
    --- SBBSecho 3.37-Linux
    * Origin: Vertrauen - [vert/cvs/bbs].synchro.net (1:103/705)