From 988248fa57afa15747b6ced0bd81cf1396090c12 Mon Sep 17 00:00:00 2001 From: Jaap-Jan de Wit | DodoTech Date: Wed, 14 Oct 2026 17:02:26 +0200 Subject: [PATCH] Quick connect: type user@host into Ctrl+K to open a terminal on a machine without saving it The desktop had no way to reach a machine that was not in the keychain; the phone has had a manual box on its terminal screen all along. The palette's query now reads as an address when it has an @ in it: a "not saved" row offers to dial it, Enter asks for the password in place, and a second Enter connects. Escape on the password step goes back to the address. It goes through the phone's own path, now split out as VaultViewModel.ConnectToEndpointAsync, so the tab, the host-key question and the connection-log entry are the same on both heads and nothing is written to the keychain. Password only, as on the phone: a key is the reason to save the host. The typed row takes the highlight unless a saved host dials exactly that address, so typing a new address cannot land on a saved machine whose address merely starts the same way. Covered by eleven new QuickConnectTests; manual check 7.10a covers the real dial. --- docs/design-import-gaps.md | 2 +- docs/manual-checks.md | 16 + src/DodoSSH.Client.App/App.axaml | 13 + .../Views/QuickConnect.axaml | 239 +++++++++----- .../Views/QuickConnect.axaml.cs | 76 ++++- .../ViewModels/MainWindowViewModel.cs | 292 +++++++++++++++++- .../ViewModels/VaultViewModel.cs | 44 ++- .../QuickConnectTests.cs | 257 +++++++++++++++ 8 files changed, 840 insertions(+), 99 deletions(-) diff --git a/docs/design-import-gaps.md b/docs/design-import-gaps.md index 03f34a8..b873f14 100644 --- a/docs/design-import-gaps.md +++ b/docs/design-import-gaps.md @@ -510,7 +510,7 @@ field cannot be removed and stays as a permanently refused member; `SyncEndpoint | Org chip `dodotech / platform` | contracts + server | An *organisation* above teams, which does not exist — `VaultSummary.TeamId` is no longer always null since M3, but a team is not an org and there is exactly one tenant per deployment. | The vault's own name, and the account this machine is enrolled as. Team names are on the VAULTS screen, where they are about something. | | `SYNCED` dot, always green | client-app | Nothing — the design's claim is simply unconditional. | Green **only** when a connection is held, the last sync pass actually reached the server, and the outbox is empty; otherwise `UNREACHABLE`, the count of changes still waiting, or `OFFLINE`. Holding an `IVaultServer` proves a sign-in once succeeded and nothing more, so a laptop whose lid has been shut all afternoon still has one — reachability comes from the outcome of the last pass. A permanently green light is the same as no light. | | `VAULT SYNCED 11:02` | client-session | `StoredSyncState.LastPulledAt`/`LastPushedAt` are persisted, but `VaultSession` exposes the store as `internal`. A property away. | Omitted. The one honest sync fact — the outbox depth — is in the titlebar and the status bar. | -| `⌘K` command palette running commands | client-domain | A snippet or saved-command item type (`SyncEntityType.Snippet = 8` is reserved). | Ctrl+K opens a real host search that connects on Enter. The box says "search hosts", not "search hosts · run command". | +| `⌘K` command palette running commands | client-domain | A snippet or saved-command item type (`SyncEntityType.Snippet = 8` is reserved). | Ctrl+K opens a real host search that connects on Enter, and reads a typed `user@host[:port]` as a machine to connect to with a password, without saving it. The box says "search hosts, or type user@host", not "search hosts · run command". | | Status bar `· via bastion-eu` | client-ssh | Jump-host execution. See below. | Omitted. | | Status bar port forwards | client-ssh | — | **Shipped** on the session status bar as a count of listening forwards; the ports themselves are in the session sidebar. | | Status bar `sftp · 2 transfers` | client-app | Nothing now — file transfer is built. What is missing is the count reaching the status bar, which is a screen away from where the queue lives. | Omitted from the status bar. The queue itself is on the FILES screen, with a row per transfer. | diff --git a/docs/manual-checks.md b/docs/manual-checks.md index d49fc09..1cb9058 100644 --- a/docs/manual-checks.md +++ b/docs/manual-checks.md @@ -1388,6 +1388,22 @@ Ctrl+K, then click a result with the mouse rather than pressing Enter. **Pass:** the palette closes and the connection starts. +### 7.10a Connecting to a machine that is not saved + +Ctrl+K, type `user@host` for a reachable machine that is **not** on HOSTS, press Enter, type its password and +press Enter again. Do it once against a machine whose host key this vault has never seen. + +**Pass:** the palette asks for the password in place, with the caret already in the box. On the second Enter +it closes, a tab named `user@host` opens, and the fingerprint card asks about the key; approving it connects. +Afterwards HOSTS has nothing new on it, and the connection log has an entry for `user@host:22`. Escape from +the password box goes back to the address with what was typed still in it. + +**Failure means:** a new row on HOSTS is the palette saving what it promised not to. A tab that fails with +"authentication failed" for a password known to be right is worth comparing against the phone's CONNECT TO A +MACHINE box, which takes the same path — `VaultViewModel.ConnectToEndpointAsync` — so a difference between +the two is in how the palette hands the password over, not in the connection. The headless half of this is +`QuickConnectTests`; what it cannot do is dial. + ### 7.11 A host or a bucket made now can be picked on FILES now With the files screen's picker open on an empty or short list, go to HOSTS and add a host — or to KEYS and diff --git a/src/DodoSSH.Client.App/App.axaml b/src/DodoSSH.Client.App/App.axaml index 8caa75a..45f86fe 100644 --- a/src/DodoSSH.Client.App/App.axaml +++ b/src/DodoSSH.Client.App/App.axaml @@ -1342,6 +1342,19 @@ + + + +