fix(desktop): explain why SSH connection reuse silently stops persisting - #84913
Open
pierrenode wants to merge 1 commit into
Open
fix(desktop): explain why SSH connection reuse silently stops persisting#84913pierrenode wants to merge 1 commit into
pierrenode wants to merge 1 commit into
Conversation
On keyring-less Linux (Hyprland/Sway, no GNOME Keyring or KWallet), persistSshConnectionToken() calls encryptDesktopSecret(token) with no allowPlainText option — it throws, the catch logs only to the internal sshRememberLog buffer, and every future launch re-spawns the remote connection instead of reusing the one from last time, with no indication in the UI of why. The sibling "remote" mode connection path already reports this exact signal (secureTokenStorage, computed from safeStorage.isEncryptionAvailable() in sanitizeDesktopConnectionConfig — mode-agnostic, not remote-specific) and surfaces it via a persistent banner in Settings -> Gateway. SSH mode never read it: the notice was gated to remote/token auth only. This does not change persistence behavior — the SSH-issued dashboard token still refuses to persist without a keyring, matching the pre-existing security posture (unlike remote mode's user-typed credential, the SSH token isn't collected through a discrete "Save" action a confirm dialog could gate, so this stays out of scope for now; see PR body). It surfaces the *reason* connection reuse silently degrades, reusing the exact secureTokenStorage signal already computed and already sent to the renderer for every connection mode. Adds an informational notice under the SSH connection fields, gated on the existing secureTokenStorage flag, localized in en/ja/zh/zh-hant (matching the sibling banner's locale coverage; ar.ts has no strings for this settings section and relies on the existing locale->en fallback).
Contributor
fix(desktop): explain why SSH connection reuse silently stops persisting No blocking issues found. A few minor observations:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On keyring-less Linux (Hyprland/Sway, no GNOME Keyring or KWallet — the same environment #62319/salvaged-in-this-window fixed for the "remote" connection mode),
persistSshConnectionToken()inapps/desktop/electron/main.tscallsencryptDesktopSecret(token)with noallowPlainTextoption:encryptDesktopSecretthrows whensafeStorage.isEncryptionAvailable()is false andallowPlainTextisn't set. The catch only writes to the internalsshRememberLogdiagnostic buffer — nothing surfaces to the UI. Every subsequent app launch re-spawns the remote connection instead of reusing the last one, with zero indication of why.Why this stayed out of #62319's scope
The "remote" mode fix added
allowPlainTextas an explicit, per-action opt-in gated behind a destructive confirm dialog in Settings → Gateway, triggered from the discrete "Save"/"Apply" button click where the user just typed a token. SSH mode has no equivalent moment:buildSshBlock()never accepts a user-typed token at all — the only place an SSH token is ever set ispersistSshConnectionToken(), called automatically after a successful SSH bootstrap. There's no "Save" click to gate a confirm dialog on, and prompting a blocking dialog from an automatic background reconnect would be poor UX (and might not even have a foreground window in every trigger path).Given that, I kept this fix conservative: it does not change what gets persisted (SSH tokens still correctly refuse to persist without a keyring, same security posture as before). It fixes specifically the silent part of the bug — the reason a working feature quietly stops working with no UI trace.
Fix
sanitizeDesktopConnectionConfig()already computessecureTokenStorage(fromsafeStorage.isEncryptionAvailable()) mode-agnostically and sends it to the renderer for every connection mode — the "remote" mode banner already consumes it, gated tomode === 'remote' && authMode === 'token'. SSH mode never read this existing signal.Adds an informational (non-destructive-styled, distinct from the existing plain-text-storage warning) notice in the SSH connection fields section of Settings → Gateway, shown when
secureTokenStorage === false, explaining that connection reuse is unavailable on this machine and reuse can be restored by enabling GNOME Keyring or KWallet. Nomain.ts/hardening.tschanges — purely renderer + i18n.Localized in en/ja/zh/zh-hant, matching the locale coverage of the sibling remote-mode banner from #62319.
ar.tshas no strings at all for this settings section already (falls back to the existing locale→en runtime fallback), so it's left untouched for consistency with that precedent.Testing
gateway-settings.test.tsx: the notice renders whenmode: 'ssh', secureTokenStorage: false, and does not render whensecureTokenStorage: true.git stash— the new "warns" test fails against the pre-fix code, passes after.tsc --noEmit -p tsconfig.json: clean.vitest runongateway-settings.test.tsx+i18n/languages.test.ts+i18n/runtime.test.ts+boot-failure-overlay.test.tsx+boot-failure-reauth.test.ts(the two files most likely to reuse<GatewaySettings>in embedded mode): all pass (41 tests).eslintcould not run in this environment (rooteslint.config.mjsneeds aglobalspeer dependency not present in this workspace-scoped install) — relied ontsc --noEmitfor type-safety instead.Checklist
remote-lifecycle.ts(remote-host-side process/token-path handling), zero file overlap with this change. fix(desktop): create connection.json owner-only #77622 touchesmain.ts/hardening.ts(file-permission hardening forconnection.json) — also zero file overlap, since this fix never touches those files.