Skip to content

fix(ssh): exit closes the tab instead of reconnecting (#538) - #566

Merged
kipavy merged 1 commit into
devfrom
fix/exit-no-reconnect-538
Oct 7, 2026
Merged

kipavy merged 1 commit into
devfrom
fix/exit-no-reconnect-538

Conversation

@kipavy

@kipavy kipavy commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #538.

Causes

  1. Close decided on EOF. channel_io emitted ssh-closed on the server's EOF, before the exit-status that marks a deliberate exit. Traced on a real RouterOS 7.23.7 (CHR): /quit sends EOF → exit-status → CLOSE, so it read as a dropped link and reconnected.
  2. Persistence silently downgraded. The backend drops persist on Windows hosts (and when the wrapper exceeds dropbear's limit), but the frontend still treated the tab as persistent, skipped the clean-exit path and reconnected to a plain shell — with no attach probe to stop it.

Fix

  • Only CLOSE (or the end of the stream) settles the channel; an exit-status/exit-signal anywhere before it counts (CloseWatch).
  • A wrapped persistent session's clean exit probes the multiplexer on the still-open connection: gone → ended, present (detach) → reconnect as before. The attach-time probe now shares persistent_session_present.
  • Frontend: remoteExit now means "ended" and closes the tab regardless of persist; persistent tabs are also tombstoned. Linux persistent exit no longer shows the 2–3 s reconnect checklist before closing.

Verification

Live, private headless container with this build:

Case Result
RouterOS /quit, persist off tab gone in 0.5 s
same, pre-fix binary (control) connecting → reconnected at 3 s (bug reproduced)
Linux exit, persist off gone in 0.5 s
Linux exit, persist on gone in 0.5 s, no reconnect
Linux tmux detach, persist on connecting → reconnected in 1.7 s (unchanged)

Unit tests for both message orders and a drop; reconnectBackoff tests; tsc; cargo fmt --check; cargo clippy --all-targets -D warnings.

Not run on a Windows host: relies on Windows OpenSSH sending exit-status before CLOSE (shared OpenSSH session code). Cause 2 is fixed regardless.

The close event went out on the server's EOF, before the exit-status
that marks a deliberate exit. RouterOS sends EOF, exit-status, CLOSE in
that order, so `/quit` read as a dropped link and the tab reconnected.
Only CLOSE (or the end of the stream) settles the channel now; an
exit-status anywhere before it counts.

Persistent sessions on hosts without a multiplexer wrapper (Windows,
dropbear) run a plain shell, but the frontend still treated them as
persistent and reconnected on every exit. The backend now decides: a
wrapped session's clean exit probes the multiplexer on the live
connection, so a detach still reattaches while an `exit` ends the tab
at once, without the reconnect-then-SESSION_ENDED detour. The attach
probe shares the same helper.
@kipavy
kipavy merged commit f26a6a2 into dev Oct 7, 2026
4 checks passed
@kipavy
kipavy deleted the fix/exit-no-reconnect-538 branch October 7, 2026 11:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant