v2.3.0
New Features
- Server-side SSH compression is now configurable via
server.compression(YAML),BSSH_COMPRESSION(env), andServerConfigBuilder::compression, replacing the hard-disabled behavior from v2.2.x. The default stays off; enabling it logs a warning about the russh delayed-zlib desync (#220).
Improvements
- Roughly double single-connection SFTP write throughput on
bssh-server.build_russh_confignow advertises a 65535-byte maximum packet size and an 8 MiB window (both configurable viaserver.maximum_packet_size/server.window_sizeandBSSH_MAX_PACKET_SIZE/BSSH_WINDOW_SIZE), the server SFTP loop pipelines writes with sequential coalescing, and sequential handlers elide the per-chunk seek. Local loopback 1 GiB benchmark: upload 71 to 437 MiB/s (6.2x), download 73 to 282 MiB/s (3.9x) (#187, #224). - Set
TCP_NODELAYon accepted server sockets, removing a ~37 ms delayed-ACK stall per strictly sequential SFTP round trip (#227). - Wire the ssh_config
Compression yes|nodirective into the russh client; it was parsed but silently ignored before.Compression yesadvertises eagerzlib, unset/noadvertises onlynone, andzlib@openssh.comis never advertised client-side (#219).
Bug Fixes
- Fix an SFTP session deadlock under paramiko's unbounded READ prefetch that froze every
bssh-serverdownload to a paramiko client at its initial 2 MiB channel window. The SFTP intake queue is now byte-bounded (max_buffered_request_bytes, default 8 MiB) instead of count-bounded, so the reader never stops draining the channel. Client-initiated disconnects are no longer logged at ERROR (#227). - Make
sftp.root/scp.rootchroot usable by re-anchoring client paths under the root;cd,get,open, andstatpreviously failed with "path outside root" and only bare/and readdir worked. SCP is unified with thesftp.rootbehavior (#214). - Confine absolute SFTP symlink targets to the chroot so a chrooted client can no longer create a link that resolves to the host filesystem (#214).
- Advertise only
noneSSH compression by default so clients that negotiatezlib@openssh.com(Cyberduck,sftp -C) no longer complete the handshake and then drop mid-session withSshEncoding: length invalid(#215).
CI/CD Improvements
- Add a CI MSRV job that reads
rust-versionfromCargo.tomland runscargo check --workspace --lockedon that toolchain, so the declared MSRV can no longer silently drift from what actually builds.
Technical Details
- Server SFTP write path: the strict read-process-write-flush loop is restructured into a reader task with a byte-bounded intake queue plus an in-order processor. Responses flush once per burst instead of once per request, and consecutive sequential
SSH_FXP_WRITErequests to the same handle are coalesced (max_write_coalesce_len, default 256 KiB) while every merged request id still receives its own status reply. - Tuned
[profile.release](lto = "fat",codegen-units = 1) with documentedRUSTFLAGS="-C target-cpu=..."guidance for self-builds. - New
tools/bench/harness:bench.sh(bssh-server vs OpenSSH SFTP throughput, with optional before/after and single-core pinning),profile.sh(perf plus inferno flamegraph of the upload path), andinterop/(sshj and paramiko round-trip checks). Loopback-only and byte-for-byte verified (#228). - Unify the auxiliary binaries on the "Broadcast SSH" name in
--help/version output, and add an Apache-2.0NOTICEfile (#210).
Dependencies
- Drop the vendored
bssh-russhfork and build against crates.iorussh0.62.1 now that both fork patches are upstream (PTYHandle::datafix in russh 0.62.0, SHA-1 MAC exclusion in 0.60.2).ssh-keyis bumped to=0.7.0-rc.11to match russh 0.62.x, andchannel_open_sessionis adapted to the new 0.62 handler signature (#212).
Breaking Changes
- The workspace minimum supported Rust version is raised from 1.88 to 1.93. The old 1.88 floor was already inaccurate (
rustyline18 needsFile::lock, stabilized in Rust 1.89); 1.93 aligns with the Launchpad PPA and Ubuntu 26.04 toolchains. Building from source now requires rustc 1.93 or newer.
Known Issues
- Server-side SSH compression stays off by default because russh's delayed-zlib (
zlib@openssh.com) transport desyncs a few packets after compression activates post-auth (reproduced on russh 0.61.1 and 0.62.1). Enablingserver.compressionadvertiseszlib/zlib@openssh.comand logs a warning; the underlying desync is left to be fixed upstream. - Native Windows client execution is not supported. Linux and macOS are fully supported; WSL2 is the recommended path on Windows (see #213).
What's Changed
- build: standardize Rust toolchain on 1.93 (PPA build, MSRV, CI verification) by @inureyes in #209
- docs: add Apache-2.0 NOTICE and align tool naming to Broadcast SSH by @inureyes in #210
- chore(deps): drop bssh-russh fork, use upstream russh 0.62.1 by @Yaminyam in #212
- fix(sftp): resolve chroot client absolute paths relative to the root (fixes #214) by @Yaminyam in #218
- fix(server): offer only
noneSSH compression to avoid zlib@openssh.com desync (fixes #215) by @Yaminyam in #217 - docs: state native Windows client support is not supported by @inureyes in #221
- feat(server): make SSH compression configurable instead of hard-disabled by @inureyes in #222
- fix: wire ssh_config Compression directive into the russh client by @inureyes in #223
- perf(sftp): raise channel sizing, pipeline server writes, tune release build by @inureyes in #224
- feat(tools): add SFTP benchmark, flamegraph, and interop harness by @inureyes in #228
- fix(sftp): fix paramiko prefetch deadlock and enable TCP_NODELAY by @inureyes in #229
Full Changelog: v2.2.3...v2.3.0