• SyncTERM Enhancements - WebSocket Support

    From Tracker1@VERT to Deuce on Mon Nov 9 12:31:45 2026
    PROPOSAL: Transport/protocol split, URL terminal options, and WebSocket support
    for SyncTERM =========================================================================

    MOTIVATION
    ----------

    BBS endpoints can be reachable over WebSockets: such as systems
    behind HTTPS reverse proxies, and cloud hosting that has no raw TCP
    ingress. Today those are only reachable from browser clients. I'd like
    SyncTERM to connect to them directly, with every existing network
    protocol supported.

    Getting there cleanly takes several steps, each a separate merge request
    that's useful on its own.


    PHASE 1: SEPARATE TRANSPORT FROM PROTOCOL IN THE UI ---------------------------------------------------

    Directory entries would choose a transport first, then a protocol where
    the transport has one:

    TCP Socket ........ Telnet, RLogin, SSH, Raw, MBBS GHost
    TCP + TLS ......... Telnet, RLogin, Raw
    Modem / Serial / 3-wire / Shell .... no protocol; their existing settings rows

    TelnetS becomes Telnet over the TCP + TLS transport. RLogin and Raw over
    TLS are new combinations that come from the same TLS layer. SSH isn't
    offered over TLS, since it already encrypts.

    Edit menu behavior:

    - New entries default to TCP + Telnet, with the cursor on Address, so
    creating an entry works the same as today.

    - Choosing a transport that has no protocols hides the Protocol row
    and shows that transport's settings.

    - The existing port update on protocol change stays as it is. Telnet
    over TLS keeps TelnetS's current default port; RLogin and Raw over
    TLS have no standard port, so the entered port is kept.

    Storage and compatibility:

    - Existing entries are written exactly as they are now. A Transport
    value is derived from the existing connection type when loading:
    Modem, Serial, 3-wire, and Shell map to their own transports,
    TelnetS maps to TCP + TLS with Telnet, and every other network type
    maps to TCP.

    - TLS + Telnet is written back as ConnectionType=TelnetS, so older
    builds still read the file. Only the new combinations (RLogin and
    Raw over TLS) need a Transport key.

    - The TLS setup moves into one shared transport layer, reused later by
    wss:// in Phase 3. The rest of the connection code keeps its current
    handling.

    Emulation, screen mode, RIP, and font stay exactly as they are; they
    don't depend on transport.


    PHASE 2: URL PROCESSING - TLS SCHEMES AND TERMINAL OPTIONS ----------------------------------------------------------

    TLS schemes, with the inner protocol first like git+ssh:

    telnet+tls:// rlogin+tls:// raw+tls://

    - telnets:// stays as a permanent alias for telnet+tls://, so
    existing links keep working.

    - User and password in the URL are handled the same way as the
    existing telnet:// and rlogin:// URIs.

    Terminal options in the fragment:

    URIs on the command line, and links launched from a browser, can carry
    terminal options in the fragment:

    telnet://bbs.example.com#emulation=petscii40
    rlogin+tls://user:pass@bbs.example.com#rip

    - Keys match the directory-entry fields for emulation, screen mode,
    RIP, and font; values are short lowercase names (ansi, rip,
    petscii40, petscii80, atascii, ...).

    - Encoded like a query string: percent-decoded, and a key with no
    value (#rip) means =1. Unknown keys are ignored.

    - Options that extend ANSI, such as RIP, apply only when the
    emulation is ANSI, whether set explicitly or left at the default.
    With any other emulation they're ignored, the same as an unknown
    key, so a link never fails to connect because of a bad combination.

    - A URL is handled the same way existing URIs are: it isn't matched
    against directory entries, and its options apply only to that
    session.

    The fragment is used rather than the query string because the query
    belongs to the server, which matters for WebSocket URLs in Phase 3.


    PHASE 3: WEBSOCKET AS A TRANSPORT
    ---------------------------------

    Adds WebSocket to the transport list:

    WebSocket ......... Telnet, RLogin, SSH, Raw

    The URL scheme (ws:// or wss://) decides whether TLS is used, so there's
    one WebSocket transport rather than two.

    Edit menu behavior:

    - Selecting WebSocket hides Address and Port and shows a URL field
    (ws:// or wss://, host, port, and path), prefilled from any address
    and port already entered. Switching back restores them.

    - Changing the protocol on a WebSocket entry never rewrites the URL.

    Storage:

    WebSocket entries add Transport=WebSocket and WebSocketURL=...
    All other entries are unchanged.

    Implementation:

    - The WS layer does the HTTP upgrade and framing (using Phase 1's
    shared TLS layer for wss), then hands the existing Telnet, RLogin,
    SSH, or Raw code a local socket pair. Those modules don't change.
    Cryptlib can take an already-connected socket, so SSH over WS works
    the same way.

    - Windows needs a loopback socketpair() emulation if xpdev doesn't
    already have one.

    - Outgoing frames are binary. Incoming text frames are accepted and
    treated as raw bytes, never decoded as UTF-8.

    - Sec-WebSocket-Protocol identifies the wire protocol only (telnet,
    rlogin, ssh, raw), never the emulation.

    - The URL path and query are sent to the server unchanged, since one
    server can expose several endpoints on a single port.

    URL schemes:

    telnet+ws:// telnet+wss://
    rlogin+ws:// rlogin+wss://
    ssh+ws:// ssh+wss://
    raw+ws:// raw+wss://

    - These work on the command line and can be registered as OS URL
    handlers (Windows registry, macOS Info.plist, Linux
    x-scheme-handler), so a browser link can launch SyncTERM. The
    Phase 2 TLS schemes can be registered the same way.

    - A bare ws:// or wss:// URL defaults to Raw, since nothing in the URL
    says which protocol runs inside it.

    - Phase 2's fragment options work the same way here:

    rlogin+wss://user:pass@bbs.example.com:11235/rlogin
    ?token=abc#emulation=petscii40

    (one URL, wrapped here for width)


    PHASE 4: MIGRATE STORED ENTRIES TO SEPARATE TRANSPORT AND PROTOCOL ------------------------------------------------------------------

    Phases 1-3 keep the legacy ConnectionType values on disk for
    compatibility, so the directory file still mixes transport and protocol.
    Phase 4 makes the stored format match the model:

    - Every entry is written with explicit Transport and Protocol keys
    (Protocol omitted for Modem, Serial, 3-wire, and Shell).

    - Loading still accepts the legacy ConnectionType values
    indefinitely, so old files, hand-edited files, and files shared
    between machines keep working.

    - Before the first save in the new format, the existing directory
    file is backed up (e.g. syncterm.lst.bak), so moving back to an
    older build is a file copy, not a rebuild by hand.

    - For a transition period, ConnectionType is still written alongside
    the new keys for every combination an older build understands, so
    older builds can keep reading migrated files. Dropping it later is a
    one-line change, done once older builds are no longer a concern.

    - The load and save code then works only in transport and protocol
    terms, so the legacy mapping lives in one place: the loader.


    Feedback welcome, especially on the INI keys and the scheme order,
    before I start on Phase 1.


    --
    Michael J. Ryan
    http://tracker1.dev/

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net