mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 00:02:23 +00:00
`try_keyboard_interactive` answered a password-shaped round from the keychain, marked the stored password spent whether or not it had been used, and returned on the first `Failure` — so the `MAX_ROUNDS` loop never got a second pass with the stored password withheld. The same dead secret went out on every reconnect and the user was never once asked to type a different one; `ki_submit` always emitted `KeychainWrite::None`, so nothing could clear it either. `collect_ki_answers` now reports where its answers came from, and only a round that actually sent the stored password spends it — which also fixes an OTP-then-password flow that was refusing the stored password for no reason, its first round having burned the allowance on a code. On a rejection whose last round came from the keychain, and where the server still offers the method, the request is started over with the stored password withheld, so the next round reaches the prompt. That retry is bounded twice over: the restart spends the stored password, so no second restart can qualify, and the round counter it shares with the info-request loop caps the method either way. The failure text now says which of the two was turned down. Scope, honestly: the only live scenario is auth mode Auto against a server offering keyboard-interactive but not password, with a stored password for that endpoint — a profile pinned to KeyboardInteractive gets `password: None` and always prompts, and Password never tries KI. Whether the symptom shows also depends on the server: OpenSSH ends a rejected kbdint request with USERAUTH_FAILURE (symptom holds), while a device that re-issues an InfoRequest in the same request already reached the prompt. `AuthPromptKind::KeyboardInteractive` grows a `#[serde(default)]` `stored_rejected`, same both-directions compatibility as `KeyPassphrase`'s `rejected` and the same reason `PROTOCOL_VERSION` stays put. The sheet shows the warning line and, on submit, forgets the rejected password. That needed an endpoint the KI prompt does not carry, which also fixed a bug next door: `raise_routed_auth` called `from_prompt(.., None, false)`, so every routed password write was keyed to port 22 regardless of the real port and the rejected self-heal could never fire there. `PendingAuth` now carries the endpoint and the auto-supplied flag, read straight off the route's `NativeSshSpec`.