Keep the password that just worked, so the host stops asking for it

The vault has held credentials and host bindings since they landed, and the
only route from a typed password into one ran through two screens: add a
password under Keychain, open the host, bind it. The password box's own tooltip
instructed people to do exactly that by hand — which means typing the secret a
second time, into a screen that has no idea which host it is for, while the
screen that does have the password is the one being left behind. A tick beside
the box does it in one step.

What it produces is an ordinary CredentialSecret, named after the host, bound
through CredentialId with AsksForPassword cleared beside it. That is the whole
reason nothing else here had to change: it syncs, it merges, it appears in the
keychain, it can be renamed and deleted, and it can be bound to the other
nineteen machines that share the account. A password field on HostSecret would
have been a payload schema bump, a fourth place a secret lives, and a copy per
host to rotate and forget.

It waits for a handshake the remote accepted, and that is not caution for its
own sake. Binding on the keystroke would store whatever was in the box —
including the typo about to be refused — and the host would then stop asking,
leaving a machine nobody can connect to until they work out that the keychain
is where the wrong password now lives. For the same reason the password is read
from the credential as dialled rather than from the box, which stays typeable
throughout a handshake.

Off by default, because the typed box exists precisely for the passwords that
should not be in a synchronised vault — a one-off on a machine somebody will
never open again. The credential is written into the host's own vault rather
than the active one: in the personal vault, bound to a team's host, it would be
a binding every other member can see and none of them can resolve.

One thing is given up knowingly. The confirmation naming where the password
went is replaced a moment later by the auto-sync's own count, exactly as
SaveHostAsync's "Saved 'x'" is; this follows that rather than becoming the one
write that suppresses a sync line. The feedback that lasts is the row answering
"credential" and the box disappearing — which is also the only way to store a
password on the phone at all, since that head lists credentials but has never
had an editor to create one in.
This commit is contained in:
2026-08-03 14:40:25 +02:00
parent 416f233657
commit b86ff6158b
4 changed files with 278 additions and 7 deletions
@@ -418,6 +418,10 @@
<!--
Shown only for a host that actually asks for one. A password box beside a key-authenticated host
is an invitation to type a secret nothing will use.
The tick below it is the phone's whole answer to storing one, and on this head it is the only one:
the keychain lists credentials here but has no editor to create one in, so before this a password
typed on a phone could only ever be typed again. The host editor's picker could then bind it.
-->
<TextBox Classes="field secret" IsVisible="{Binding SelectedHostAsksForAPassword}"
Text="{Binding ConnectPassword}" PlaceholderText="password">
@@ -426,6 +430,12 @@
</TextBox.KeyBindings>
</TextBox>
<CheckBox IsChecked="{Binding RemembersConnectPassword}" MinHeight="44"
IsVisible="{Binding SelectedHostAsksForAPassword}">
<TextBlock Classes="mono" FontSize="11.5" TextWrapping="Wrap"
Text="Remember this password for this host" />
</CheckBox>
<TextBlock Classes="detail" TextWrapping="Wrap" IsVisible="{Binding !SelectedHostAsksForAPassword}"
Text="{Binding SelectedHostAuthenticationNote}" />