You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make bssh a true drop-in replacement for OpenSSH ssh(1) in single-destination mode, with bssh's parallel, cluster, and pdsh features layered strictly on top as additive extensions. OpenSSH semantics become the substrate; nothing bssh adds may take a name or a behavior that OpenSSH already defines.
Conformance is measured against the OpenSSH project's own regression suite (regress/ in openssh-portable), not against our own reading of the manual. The suite is the instrument, and its score is the acceptance gate.
Measured baseline (bssh 2.4.3)
Measurement performed on 2026-08-26 against a self-built openssh-portableV_10_3_P1 on macOS arm64, using 89 client-relevant tests selected from the suite's 116 shell tests. Every bssh failure was re-run with the stock OpenSSH client to separate genuine incompatibilities from environment limitations.
Run
Result
bssh, unmodified
0 / 89
bssh, behind a shim that absorbs -E
23 PASS / 10 SKIP / 56 FAIL
OpenSSH baseline re-run of those 56 failures
48 baseline-PASS, 8 baseline-FAIL
Score on environment-valid, non-skipped tests: 23 / 71 (32%).
The unmodified run scored zero because test-exec.sh wrapped every client invocation as exec ${SSH} -E${logfile} "$@", and bssh rejected -E. One missing flag made the entire suite unrunnable.
Tests that passed in that baseline: addrmatch, agent-subprocess, agent-timeout, broken-pipe, cfgparse, dhgex, exit-status-signal, keygen-knownhosts, keyscan, krl, proto-mismatch, proto-version, scp, scp-uri, servcfginclude, sftp, sftp-badcmds, sftp-batch, sftp-cmds, sftp-glob, sftp-perm, sftp-resume, sshsig.
Root causes behind the 48 genuine failures
The ssh_config layer parses far more than it uses. Of 81 fields on the resolved host config, only about 10 are read anywhere outside src/ssh/ssh_config/: hostname, user, port, identity files, StrictHostKeyChecking, ProxyJump, Compression, AddressFamily, ServerAliveInterval and ServerAliveCountMax. The rest are parsed, validated, merged by the resolver, and then never consulted.
Host key file selection is the largest single cluster.src/ssh/known_hosts.rs:20 always resolves to ~/.ssh/known_hosts; user_known_hosts_file and global_known_hosts_file are assigned in resolver.rs:120-124 and never read. bssh also validates those paths strictly enough to reject GlobalKnownHostsFile /dev/null, a standard OpenSSH idiom, as a "security violation" that aborts config parsing outright.
Missing flags, ranked by how often the suite uses them. Across 1074 traced client invocations: -E 1071, -G 104, -c 84, -m 42, -s 8, -n 8, -W 4, -S 3, -O 2, -M 1.
Short flags that collide with OpenSSH.-N, -f, -C, -A, -S, -k and -b all mean something different in bssh than in ssh(1). dynamic-forward.sh uses four of them in a single invocation.
Output is not byte-transparent. bssh writes a decorative banner and a summary rule to stderr even for a single host, emits ANSI escapes when stderr is not a terminal, ignores NO_COLOR, and routes -V to stdout where OpenSSH uses stderr.
Diagnostics are opaque. 20 of the 48 failures surface only as Error: I/O error, which covers connection refusal, authentication failure, and channel errors alike.
Governing rule for the superset
In a single-destination invocation, OpenSSH semantics win unconditionally. bssh extensions are reachable only through long flags. The pdsh single-letter meanings survive only under --pdsh-compat or when invoked as pdsh via argv[0]. This is the only rule under which "fully OpenSSH compatible, plus more" is self-consistent, and it makes the flag reassignment in the last sub-issue a breaking change scheduled for 3.0.
Explicitly out of scope
These are recorded as permanent skips rather than pending work, and the harness must report them as such: GSSAPI authentication, PKCS#11 and FIDO/security-key middleware, X11 forwarding, tun device forwarding (-w), KRL generation, certificate authority key signing, sshd-side configuration tests, and interoperability with PuTTY, Dropbear, Twisted conch and ssh.com.
Sub-issues
Ordered by leverage measured against the suite, not by size.
PR #313 completed the final integration. The pinned OpenSSH V_10_3_P1 harness reports:
macOS: 60 passed out of 66 eligible tests, 6 failed, 7 environmental, 6 skipped
Ubuntu: 61 passed out of 69 eligible tests, 8 failed, 4 environmental, 6 skipped
Both platforms meet the 60-pass gate. The remaining candidate-only failures are enumerated with platform evidence in #314. The broader parsed-keyword tracker #281 remains open for residual compatibility work; neither issue blocks completion of this score-gated epic.
The breaking short-flag migration cannot retroactively add a warning cycle to an already published 2.x release. The merged implementation emits bounded interactive migration warnings, documents the long-option replacements, and keeps redirected protocol streams and -E logs byte-transparent.
Acceptance criteria for the epic
The regress harness runs in CI and reports a pass/skip/fail score against a committed baseline.
bssh scores at least 60 of the 71 environment-valid, non-skipped tests, with every remaining failure either listed in the out-of-scope set above or tracked by an open issue.
README.md no longer claims unqualified drop-in replacement status; it links to the measured score and the documented skip list.
Every keyword bssh's ssh_config parser accepts is either consumed at runtime or rejected with a warning that says it is unimplemented. No keyword is silently parsed and discarded.
Goal
Make bssh a true drop-in replacement for OpenSSH
ssh(1)in single-destination mode, with bssh's parallel, cluster, and pdsh features layered strictly on top as additive extensions. OpenSSH semantics become the substrate; nothing bssh adds may take a name or a behavior that OpenSSH already defines.Conformance is measured against the OpenSSH project's own regression suite (
regress/in openssh-portable), not against our own reading of the manual. The suite is the instrument, and its score is the acceptance gate.Measured baseline (bssh 2.4.3)
Measurement performed on 2026-08-26 against a self-built
openssh-portableV_10_3_P1on macOS arm64, using 89 client-relevant tests selected from the suite's 116 shell tests. Every bssh failure was re-run with the stock OpenSSH client to separate genuine incompatibilities from environment limitations.-EScore on environment-valid, non-skipped tests: 23 / 71 (32%).
The unmodified run scored zero because
test-exec.shwrapped every client invocation asexec ${SSH} -E${logfile} "$@", and bssh rejected-E. One missing flag made the entire suite unrunnable.Tests that passed in that baseline:
addrmatch,agent-subprocess,agent-timeout,broken-pipe,cfgparse,dhgex,exit-status-signal,keygen-knownhosts,keyscan,krl,proto-mismatch,proto-version,scp,scp-uri,servcfginclude,sftp,sftp-badcmds,sftp-batch,sftp-cmds,sftp-glob,sftp-perm,sftp-resume,sshsig.Root causes behind the 48 genuine failures
src/ssh/ssh_config/: hostname, user, port, identity files, StrictHostKeyChecking, ProxyJump, Compression, AddressFamily, ServerAliveInterval and ServerAliveCountMax. The rest are parsed, validated, merged by the resolver, and then never consulted.src/ssh/known_hosts.rs:20always resolves to~/.ssh/known_hosts;user_known_hosts_fileandglobal_known_hosts_fileare assigned inresolver.rs:120-124and never read. bssh also validates those paths strictly enough to rejectGlobalKnownHostsFile /dev/null, a standard OpenSSH idiom, as a "security violation" that aborts config parsing outright.-E1071,-G104,-c84,-m42,-s8,-n8,-W4,-S3,-O2,-M1.-N,-f,-C,-A,-S,-kand-ball mean something different in bssh than inssh(1).dynamic-forward.shuses four of them in a single invocation.NO_COLOR, and routes-Vto stdout where OpenSSH uses stderr.Error: I/O error, which covers connection refusal, authentication failure, and channel errors alike.Governing rule for the superset
In a single-destination invocation, OpenSSH semantics win unconditionally. bssh extensions are reachable only through long flags. The pdsh single-letter meanings survive only under
--pdsh-compator when invoked aspdshvia argv[0]. This is the only rule under which "fully OpenSSH compatible, plus more" is self-consistent, and it makes the flag reassignment in the last sub-issue a breaking change scheduled for 3.0.Explicitly out of scope
These are recorded as permanent skips rather than pending work, and the harness must report them as such: GSSAPI authentication, PKCS#11 and FIDO/security-key middleware, X11 forwarding, tun device forwarding (
-w), KRL generation, certificate authority key signing, sshd-side configuration tests, and interoperability with PuTTY, Dropbear, Twisted conch and ssh.com.Sub-issues
Ordered by leverage measured against the suite, not by size.
Phase 1
Phase 2
-E log_filePhase 3
UserKnownHostsFileandGlobalKnownHostsFileProxyCommandinstead of parsing and discarding itNO_COLORPhase 4
HostKeyAliaswhen looking up host keys (depends on fix(ssh_config): honor UserKnownHostsFile and GlobalKnownHostsFile #278)Phase 5
I/O errorwith actionable diagnostics (depends on fix(ssh_config): honor HostKeyAlias when looking up host keys #279, fix(ssh_config): execute ProxyCommand instead of parsing and discarding it #280, fix(ui): make single-host output byte-transparent and honor NO_COLOR #283)Phase 6
Phase 7
Phase 8
Phase 9
-Gto dump the resolved configuration (depends on feat(ssh_config): wire the parsed-but-unused ssh_config keywords #281)Phase 10
-c,-m,-s,-nand-W(depends on feat(cli): implement -G to dump the resolved configuration #282)Phase 11
-M,-O,ControlPath) (depends on feat(cli): implement -c, -m, -s, -n and -W #285)Phase 12
Final result (2026-09-01)
PR #313 completed the final integration. The pinned OpenSSH
V_10_3_P1harness reports:Both platforms meet the 60-pass gate. The remaining candidate-only failures are enumerated with platform evidence in #314. The broader parsed-keyword tracker #281 remains open for residual compatibility work; neither issue blocks completion of this score-gated epic.
The breaking short-flag migration cannot retroactively add a warning cycle to an already published 2.x release. The merged implementation emits bounded interactive migration warnings, documents the long-option replacements, and keeps redirected protocol streams and
-Elogs byte-transparent.Acceptance criteria for the epic
README.mdno longer claims unqualified drop-in replacement status; it links to the measured score and the documented skip list.