• src/ssh/design-channel-io-api.md

    From Deucе@VERT to Git commit to main/sbbs/master on Sat Mar 28 16:46:56 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/d5a9dd33ac68c0f3fd88906d
    Modified Files:
    src/ssh/design-channel-io-api.md
    Log Message:
    Revise channel I/O design: two models, deferred window, linear accumulation

    Replace unified read/write proposal with two distinct I/O models:
    - Stream API (dssh_channel_read/write) for session channels (shell/exec)
    - Zero-copy callback for subsystem channels (sftp, etc.)

    Key design decisions:
    - Max packet size per-session (set once, used in all channel opens)
    - Max window size per-channel (deferred to channel request/accept)
    - Channel accept as finalization point (server callback, client function)
    - Linear accumulation buffer for subsystem callbacks (no ring, no wrap)
    - Flow control via natural window exhaustion during accumulation

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Sun Mar 29 10:21:44 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/4150f7036a5e39b78802cb29
    Modified Files:
    src/ssh/design-channel-io-api.md
    Log Message:
    Channel I/O design: ZC/stream split, locking, events, params builder

    Squash of design iteration commits into one coherent update.

    - dssh_chan_ prefix; zero-copy API uses dssh_chan_zc_
    - Stream API (open/read/write/poll) built on ZC internals
    - ZC API (zc_open/zc_getbuf/zc_send/zc_cancel): zero-copy TX
    (app writes directly into tx_packet) and RX (callback gets
    pointer into rx_packet). Zero mallocs, zero copies both directions.
    - All functions except open take dssh_channel only (channel carries
    session). No mismatched sess/ch pairs.
    - Stream parameter (0=stdout, 1=stderr) replaces _ext variants
    - Channel type as enum in params struct, not separate open functions
    - Params builder: init/set_*/free, all strings copied in. Type,
    max_window, pty (orthogonal to type), modes, env all in struct.
    Zero terminal modes by default (library can't know terminal state).
    Consumed at open time, library keeps no references.
    - Events separate from data (signalfd model). poll(DSSH_POLL_EVENT)
    + read_event(), or event callback with full event struct.
    Poll freezes positions; one event per cycle; uncollected discarded.
    - dssh_chan_close(ch, int64_t exit_code): negative = no exit-status.
    Preserves full uint32 wire range.
    - dssh_chan_shutwr(ch): half-close (EOF), shutdown(SHUT_WR) semantics
    - TX locking: zc_getbuf acquires tx_mtx, zc_send releases. App must
    not block between them. tx_mac_scratch eliminated via 4-byte seq
    prefix in tx_packet.
    - RX locking: ZC callback runs with no library mutex. remote_window
    and state flags are atomic.
    - RX callback cannot TX (deadlock: rekey needs demux thread).
    Enforced via _Thread_local bool in_zc_rx.
    - Callback protection: cb_mtx per channel. Session-level defaults
    copied to channel at creation time (no open-time race).
    - Stream built on ZC: internal zc_cb copies to ring buffer; public
    ZC functions validate, internal versions skip checks.
    - No void returns for fallible functions; infallible ops (free) void.

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

    ---
    ■ Synchronet ■ Vertrauen ■ Home of Synchronet ■ [vert/cvs/bbs].synchro.net
  • From Deucе@VERT to Git commit to main/sbbs/master on Sun Mar 29 10:21:44 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/73b7f48b88ccdbc7afa7fa4d
    Modified Files:
    src/ssh/design-channel-io-api.md
    Log Message:
    Server-side accept API: callback-driven setup, shared params struct

    Complete server-side channel API design:

    - dssh_chan_accept() blocks until incoming channel setup completes.
    Library drives the setup state machine; app provides per-request
    callbacks (pty_req, env, shell, exec, subsystem).
    - Library populates dssh_chan_params internally during accept using
    the same builder functions the client uses. Every callback receives
    the accumulated params struct showing all setup state so far.
    Channel owns the params after accept; getters read from it.
    - Same struct round-trips through the wire — in selftests, client's
    builder-constructed params and server's wire-populated params can
    be compared directly for round-trip verification.
    - NULL callback semantics: only pty_req auto-accepts (benign).
    Everything else auto-rejects: env (RFC s6.4 security hazard —
    uncontrolled env vars like LD_PRELOAD), shell, exec, subsystem.
    App must explicitly declare what it accepts. No accidental
    open-everything servers, no unfiltered env vars.
    - Terminal request callbacks receive pre-filled dssh_chan_accept_result
    for I/O model selection (stream vs ZC, max_window). ZC requires
    the callback (only way to provide zc_cb).
    - pty_req/env reject = non-fatal (CHANNEL_FAILURE, continue setup).
    Terminal request reject = close channel, keep waiting.
    - Lifecycle enforcement: env before terminal request (RFC s6.4),
    one terminal request per channel (RFC s6.5), second pty-req =
    disconnect (OpenSSH convention), post-setup only window-change/
    break/signal.
    - Getters work for both client-opened and server-accepted channels.
    Returned pointers valid for channel lifetime.
    - Accept copies session-level event callback default; post-setup
    events arrive through normal event queue/callback.
    - Server initiating channels uses dssh_chan_open (side-neutral API).
    - Parse helpers eliminated — library populates params internally.
    - All open items resolved (client and server).

    Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

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