Part of the OpenSSH drop-in compatibility epic.
Problem
bssh decorates its output in ways that are correct for a multi-node cluster run and wrong for a single-host ssh substitution. A drop-in replacement has to be byte-transparent, because the callers are rsync -e, git, Ansible, shell pipelines and CI, none of which tolerate an extra banner or an escape sequence.
Observed on bssh 2.4.3 with stderr redirected to a pipe, so no terminal is attached:
^[[1m^[[36m>^[[39m^[[0m ^[[36mExecuting^[[39m on ^[[1m1^[[0m node:
^[[2m true^[[0m
...
^[[2m========================================^[[0m
^[[1m Summary: ^[[1m1^[[0m nodes . ^[[1m^[[31m1^[[39m^[[0m ^[[31mfailed^[[39m ^[[0m
Four distinct defects:
- A banner (
Executing on 1 node:, the echoed command) and a summary rule are emitted even for a single destination. OpenSSH emits nothing.
- ANSI escape sequences are written when the stream is not a terminal.
NO_COLOR=1 does not suppress them. Verified: identical output with and without the variable set.
-V writes the version to stdout. OpenSSH writes it to stderr and stdout stays empty, so ssh -V >/dev/null still shows the version and a caller capturing stdout gets nothing extra.
There is a fifth, smaller one: the tracing subscriber prints timestamped WARN bssh::ssh::ssh_config::parser::options: Unknown SSH config option '...' lines to stderr on every invocation when a config contains a keyword bssh does not know. OpenSSH accepts ChallengeResponseAuthentication silently as a deprecated alias, so a config that is quiet under ssh is noisy under bssh.
Scope
- In single-destination mode, suppress the banner, the per-node prefix and the summary entirely. Both stdout and stderr must carry only what the remote command and the SSH layer produce.
- Gate all styling on the destination stream being a terminal, checked per stream rather than globally.
- Honor
NO_COLOR (any non-empty value), TERM=dumb, and add an explicit --color=auto|always|never for callers that want to force either way.
- Move
-V output to stderr and match OpenSSH's single-line shape.
- Keep the deprecated-alias set OpenSSH accepts silent, and route genuine unknown-keyword warnings through the normal diagnostic path rather than the tracing subscriber, so they are not timestamped and module-tagged.
Relationship to the rest of the epic
This is what decides whether rsync -e bssh and git work at all, independent of the regress score. Several suite tests compare bytes exactly (transfer moves 10MB, stderr-data, stderr-after-eof, broken-pipe, yes-head), so they gate on it too, but the user-visible value lands before any of them pass.
Acceptance criteria
Part of #275
Part of the OpenSSH drop-in compatibility epic.
Problem
bssh decorates its output in ways that are correct for a multi-node cluster run and wrong for a single-host
sshsubstitution. A drop-in replacement has to be byte-transparent, because the callers arersync -e,git, Ansible, shell pipelines and CI, none of which tolerate an extra banner or an escape sequence.Observed on bssh 2.4.3 with stderr redirected to a pipe, so no terminal is attached:
Four distinct defects:
Executing on 1 node:, the echoed command) and a summary rule are emitted even for a single destination. OpenSSH emits nothing.NO_COLOR=1does not suppress them. Verified: identical output with and without the variable set.-Vwrites the version to stdout. OpenSSH writes it to stderr and stdout stays empty, sossh -V >/dev/nullstill shows the version and a caller capturing stdout gets nothing extra.There is a fifth, smaller one: the tracing subscriber prints timestamped
WARN bssh::ssh::ssh_config::parser::options: Unknown SSH config option '...'lines to stderr on every invocation when a config contains a keyword bssh does not know. OpenSSH acceptsChallengeResponseAuthenticationsilently as a deprecated alias, so a config that is quiet undersshis noisy underbssh.Scope
NO_COLOR(any non-empty value),TERM=dumb, and add an explicit--color=auto|always|neverfor callers that want to force either way.-Voutput to stderr and match OpenSSH's single-line shape.Relationship to the rest of the epic
This is what decides whether
rsync -e bsshandgitwork at all, independent of the regress score. Several suite tests compare bytes exactly (transfermoves 10MB,stderr-data,stderr-after-eof,broken-pipe,yes-head), so they gate on it too, but the user-visible value lands before any of them pass.Acceptance criteria
NO_COLOR,TERM=dumband--color=nevereach suppress styling;--color=alwaysforces it.bssh -Vwrites to stderr and leaves stdout empty.ChallengeResponseAuthenticationproduces no output.transfer,stderr-data,stderr-after-eofandyes-headpass in the harness.Part of #275