Problem / Background
Running the following on macOS against an orin cluster defined in ~/.config/bssh/config.yaml:
bssh -C orin -l lablup --password -S ping
The user expected the password prompt to appear once, up-front, before execution starts. Instead, execution begins and the prompt either never appears cleanly, gets buried in the parallel progress UI, or would be issued per-node. Two underlying defects are responsible.
Defect 1 — --password is collected lazily per-connection, not up-front
src/ssh/auth.rs:412-435 (password_auth()) calls rpassword::prompt_password() inside each node's authentication task.
src/commands/ping.rs:25-64 builds the executor and immediately calls executor.execute(\"true\") — no pre-collection step.
- Because the executor fans out N parallel connection attempts, multiple tasks race for stdin, so the prompt is either missed (drowned by progress output), repeated per node, or interleaved with the progress UI rendering.
Compare with -S (sudo-password), which is done correctly: src/app/dispatcher.rs:469-474 calls get_sudo_password() before building the executor, then wraps it in Arc<SudoPassword> and injects it into every connection task. The --password path should follow the same pattern.
Defect 2 — -S is silently dropped by ping, upload, download
- The only dispatcher branches that read
cli.sudo_password are exec (src/app/dispatcher.rs:470) and the Interactive / SSH-mode interactive paths.
- The
Ping branch (src/app/dispatcher.rs:115-148) does not read cli.sudo_password at all, and ping_nodes() (src/commands/ping.rs:25-36) has no sudo password parameter. Upload (lines ~149-180) and Download (lines ~181-) likewise ignore the flag.
- This is silent failure: the user passes
-S, no error is shown, but the flag has no effect for those subcommands.
For ping specifically, sudo isn't needed (it runs true), so -S being ignored doesn't break behavior — but accepting a flag and ignoring it is misleading. For upload/download, sudo also isn't applicable to the SFTP path, but the same UX concern applies.
Proposed Solution
The user agreed: collecting once is the consistent choice ("한 번 입력하는 게 일관되겠습니다").
1. Move --password collection into the dispatcher, mirroring the -S pattern
- In
src/app/dispatcher.rs, when cli.password is true, prompt once via rpassword::prompt_password() before building the executor.
- Store as
Arc<Zeroizing<String>> (or a new Password wrapper analogous to SudoPassword in src/security/sudo.rs, using secrecy::SecretString + Zeroizing) and thread it through the executor / connection manager so every per-node auth task uses the same value instead of prompting itself.
- Remove the per-connection
rpassword::prompt_password() call in src/ssh/auth.rs:password_auth() — it should consume the pre-collected password.
- Apply to all subcommands that take
--password: exec, ping, upload, download, interactive, and SSH mode.
2. Decide -S semantics for ping/upload/download
Since sudo isn't applicable for these subcommands, either:
- (a) Print a clear warning when
-S is passed ("-S has no effect for this subcommand"), or
- (b) Reject it at CLI parse time.
Pick whichever fits the project's UX norms.
3. Optional: improve the prompt text
The prompt format in src/ssh/auth.rs:422 is \"Enter password for {username}@{host}: \". Once the prompt is hoisted to the dispatcher, change it to a single non-host-specific prompt, e.g., \"Enter SSH password (used for all hosts): \".
Acceptance Criteria
Technical Considerations
- Follow the existing
SudoPassword pattern in src/security/sudo.rs for credential handling and zeroization.
- Threading the password through the executor will likely require adding an optional
Arc<Password> to the executor's connection-task spawn API, mirroring how sudo_password is propagated today.
- Auditing where
password_auth() is invoked is required to ensure no caller relies on the embedded prompt.
- Watch for terminal-state pitfalls: prompting after the progress UI has been initialized (indicatif) can mangle terminal output; the up-front prompt must run before any progress bar is rendered.
Problem / Background
Running the following on macOS against an
orincluster defined in~/.config/bssh/config.yaml:The user expected the password prompt to appear once, up-front, before execution starts. Instead, execution begins and the prompt either never appears cleanly, gets buried in the parallel progress UI, or would be issued per-node. Two underlying defects are responsible.
Defect 1 —
--passwordis collected lazily per-connection, not up-frontsrc/ssh/auth.rs:412-435(password_auth()) callsrpassword::prompt_password()inside each node's authentication task.src/commands/ping.rs:25-64builds the executor and immediately callsexecutor.execute(\"true\")— no pre-collection step.Compare with
-S(sudo-password), which is done correctly:src/app/dispatcher.rs:469-474callsget_sudo_password()before building the executor, then wraps it inArc<SudoPassword>and injects it into every connection task. The--passwordpath should follow the same pattern.Defect 2 —
-Sis silently dropped byping,upload,downloadcli.sudo_passwordareexec(src/app/dispatcher.rs:470) and theInteractive/ SSH-mode interactive paths.Pingbranch (src/app/dispatcher.rs:115-148) does not readcli.sudo_passwordat all, andping_nodes()(src/commands/ping.rs:25-36) has no sudo password parameter.Upload(lines ~149-180) andDownload(lines ~181-) likewise ignore the flag.-S, no error is shown, but the flag has no effect for those subcommands.For
pingspecifically, sudo isn't needed (it runstrue), so-Sbeing ignored doesn't break behavior — but accepting a flag and ignoring it is misleading. Forupload/download, sudo also isn't applicable to the SFTP path, but the same UX concern applies.Proposed Solution
The user agreed: collecting once is the consistent choice ("한 번 입력하는 게 일관되겠습니다").
1. Move
--passwordcollection into the dispatcher, mirroring the-Spatternsrc/app/dispatcher.rs, whencli.passwordis true, prompt once viarpassword::prompt_password()before building the executor.Arc<Zeroizing<String>>(or a newPasswordwrapper analogous toSudoPasswordinsrc/security/sudo.rs, usingsecrecy::SecretString+Zeroizing) and thread it through the executor / connection manager so every per-node auth task uses the same value instead of prompting itself.rpassword::prompt_password()call insrc/ssh/auth.rs:password_auth()— it should consume the pre-collected password.--password: exec, ping, upload, download, interactive, and SSH mode.2. Decide
-Ssemantics forping/upload/downloadSince sudo isn't applicable for these subcommands, either:
-Sis passed ("-Shas no effect for this subcommand"), orPick whichever fits the project's UX norms.
3. Optional: improve the prompt text
The prompt format in
src/ssh/auth.rs:422is\"Enter password for {username}@{host}: \". Once the prompt is hoisted to the dispatcher, change it to a single non-host-specific prompt, e.g.,\"Enter SSH password (used for all hosts): \".Acceptance Criteria
bssh -C <cluster> --password <subcommand>prompts exactly once, before any connection attempt or progress UI output.Zeroizing/SecretString).-Seither applies to applicable subcommands or warns/errors when passed to subcommands where it has no effect.src/security/sudo.rs).Technical Considerations
SudoPasswordpattern insrc/security/sudo.rsfor credential handling and zeroization.Arc<Password>to the executor's connection-task spawn API, mirroring howsudo_passwordis propagated today.password_auth()is invoked is required to ensure no caller relies on the embedded prompt.