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)