Public Access
Ask before deleting, and connect a host by double-clicking it
DELETE on a host, an SSH key, a stored password or a file on the host now puts a question where the button was, and only answering it deletes anything. It is a state rather than a dialog, which is the arrangement signing out already had and for the same reason: this is the moment that has to be able to say what is about to go before it goes. What the question says is counted rather than generic, because a confirmation that only asks whether you are sure is a click to train people out of. A key names the hosts that authenticate with it and says they will refuse to connect afterwards rather than falling back to a typed password, which is what the connect path actually does. A host discloses a terminal open on it, because deleting the host does not close the session. Every vault deletion says how far it travels and whether this machine can push the tombstone yet or is queuing it. Deleting on the host carries the strongest warning of the four on purpose: everything else here is a tombstone against a copy the server still holds, and a file on somebody's machine is bytes with nothing behind them — so that one names the full path, since a bare name identifies nothing. The armed request carries the item's entity id, so nothing that moves the selection between the question and the answer can redirect it, and answering about something that has since gone says so instead of doing nothing quietly. Disarming compares ids rather than rows, which is the subtle half: a reload replaces every row object, so the naive rule would have let the pass that runs every minute take the card away from somebody halfway through reading it. Forgetting a pinned host key is deliberately still unguarded. It costs one fingerprint check on the next connection and it is the safe direction to be wrong in — the dangerous button there is the one that adds trust, and that one is already a prompt at connect time. Discarding a stopped transfer is likewise unguarded: it removes a resumable part file and leaves the source alone. Double-clicking a host in the sidebar connects to it, wired as a gesture in the control exactly as the transfers screen opens a directory. CONNECT stays, since it is the button with the password box beside it. Ten existing delete call sites now go through arm-and-confirm helpers, and eight new flow tests cover asking first, cancelling, the counted warning, disarming on a selection change and on an editor opening, surviving a sync, and the stale-item guard. Three layout tests measure the new shapes — the sidebar card is the one card in the application a user cannot scroll — and one of them also asserts the card renders its text, because a card whose compiled bindings did not resolve would lay out perfectly as empty rows. The double-click test performs the real gesture and proves it reached the connect command through a refusal that never touches a network. dotnet build, dotnet test and dotnet format --verify-no-changes are all clean: 853 tests, including the end-to-end suite against real containers.
This commit is contained in:
@@ -132,8 +132,9 @@ dotnet run --project src/DodoSSH.Client.App
|
||||
|
||||
In the app, enter `http://localhost:5233` as the server. Your browser opens for sign-in — the realm ships
|
||||
`alice` / `alice` — then choose a vault passphrase and **write down the recovery code**, which cannot be
|
||||
skipped and cannot be recovered from the server. You can then add a host and open a shell on it. Keycloak's
|
||||
admin console is at `http://localhost:18080` (`admin` / `admin`).
|
||||
skipped and cannot be recovered from the server. You can then add a host and open a shell on it — double-click
|
||||
it in the sidebar, or select it and press **CONNECT**, which is the same command with the password box beside
|
||||
it. Keycloak's admin console is at `http://localhost:18080` (`admin` / `admin`).
|
||||
|
||||
You can also add an SSH key, which is stored in the vault like a host and synced the same way: paste the
|
||||
private key, then edit a host and pick that key from its **key** dropdown. From then on that host
|
||||
@@ -146,6 +147,14 @@ the next sync. If a server is legitimately rebuilt and offers a new key, the con
|
||||
with no way to continue from the warning — edit the host and choose **Forget host key**, which is deliberately
|
||||
somewhere you have to go on purpose.
|
||||
|
||||
**Deleting asks first, and the question is worth reading.** DELETE on a host, an SSH key or a stored password
|
||||
puts a question where the buttons were, and what it says is counted rather than generic: how many hosts
|
||||
authenticate with the key about to go — they refuse to connect afterwards rather than falling back to a typed
|
||||
password — whether a terminal is open on the host about to go, and whether this machine can push the deletion
|
||||
yet or is queuing it. There is no undo, which is the other thing it says. Withdrawing host key trust is the
|
||||
deliberate exception: it costs one fingerprint check on the next connection, and the dangerous button there is
|
||||
the one that *adds* trust.
|
||||
|
||||
**Signing in once is enough.** The refresh token is kept in the local cache, sealed under the vault's own
|
||||
key, so a later launch resumes the session itself and no browser opens — and because it is sealed under that
|
||||
key, resuming can only happen *after* the vault is unlocked. A machine that unlocks with no network keeps
|
||||
@@ -186,6 +195,9 @@ beside its destination and is renamed into place at the end, so an interrupted t
|
||||
mistaken for a finished one — which matters most for what people actually use this for, which is copying a
|
||||
build artefact onto a server and then running it. A destination that already exists is refused outright
|
||||
rather than overwritten; the remote pane has **DELETE** and **MKDIR** so that refusal is not a dead end.
|
||||
DELETE asks first and names the full path, and it carries the strongest warning in the application on
|
||||
purpose: everything else DodoSSH deletes is a tombstone against a copy the server still holds, and a file on
|
||||
somebody's host is bytes with nothing behind them.
|
||||
**RESUME** on a stopped transfer carries on from what the part file already holds.
|
||||
|
||||
Resume works within a run of the application and not across a restart, and that limit is deliberate: nothing
|
||||
|
||||
Reference in New Issue
Block a user