- Quick Connect runs the same validation as saved profiles, so an SSH
connection without a username fails up front with a clear message (F5).
- ~/.ssh/config imports fill a missing User (and jump-host user) with the
local user name, as ssh does, and skip blocks that still don't validate,
reporting how many (F6).
- An unrecognised hostKeyPolicy/lineEnding/parity/flowControl/direction in
a stored config falls back to that field's default instead of failing
the whole JSON column and silently resetting every other setting; parse
failures that remain are logged (F7).
- A JSON export without secrets now blanks login-script 'send' steps that
answer a password-like 'wait' prompt (SSH and Telnet), not just the
proxy password and inline TOTP seed (SEC-20261012-011, partial).
- Credential kinds were string literals in the session manager, profile
editor and secret store; they are now SecretKind with one ALL list, so
the profile-delete purge can't miss a kind (F19).
Moving proxyPassword out of the profile record is left open: it changes
.link/.nexus compatibility with the Go app and needs an owner decision.
An empty vault accepted any password, so a typo at the first unlock
encrypted later secrets under the wrong key. New vaults store a verifier
(a known value sealed under the key) that every unlock must open. Vaults
from Go or older builds keep today's behaviour and gain a verifier on
their next write; Go ignores the extra field (SEC-20261012-016, F8).
- IRC: channel state is kept only for rooms the client itself joined, so
JOIN/PART/KICK/NAMES for other rooms can't grow it; users per channel
are capped at 10 000 and the view keeps at most 200 conversations,
dropping the oldest idle query (SEC-20261012-014, F14).
- Recordings: the list reads the header line and the last 64 KiB for the
duration instead of whole files of up to 250 MiB, falling back to a
full scan only when the final event is longer than the window
(SEC-20261012-020, F9).
- Audit log: keeps the newest 10 000 entries (F17).
Every shared lock was std::sync::Mutex with lock().unwrap() (~150 sites),
so a panic in any session's reader or callback poisoned the lock and the
next UI frame panicked too, dropping every open session. Locks are now
parking_lot::Mutex (no poisoning; already in the dependency tree), and a
panic hook reports worker panics as an error toast instead of the session
silently stopping (F3, F4).
A password typed at a 'saved' prompt was stored before the server
accepted it, so a typo was persisted and silently retried on every
reconnect. Typed values are now held per attempt and written once the
session connects; when the server refuses a saved credential the next
attempt asks again and the new value replaces it on success. A store
failure (keychain refused, vault locked) now shows a toast instead of
being swallowed, and reconnect prompts wait 5 minutes like the first
connect (SEC-20261012-015, F1, F2, F20).
OSC 52 and VNC ServerCutText now go through one handler that honours
'allow remote clipboard' and always toasts the first line, so a silent
clipboard swap can't set up a paste into another shell (SEC-20261012-010).
Multi-line pastes into a terminal without bracketed paste ask first, with a
preview (SEC-20261012-017). The launch-time update check can be turned off
and the updater docs no longer claim it is opt-in; the unused GitHub
fallback is removed (SEC-20261012-013, F15).
Under the 'ask' policy a changed host key used to be a hard error. The
prompt now shows the stored and received fingerprints and offers Reject,
Accept Once (connect, keep the record) and Accept & Update (replace the
record; audited). Unknown keys gain an Accept Once option too. Strict and
accept-new still refuse changed keys without prompting. Known-host rows
written by the old app are confirmed removable by id (test).
The NickServ password (and any other "remember for this session" secret) was
resolved through a fresh per-connect cache, so every auto-reconnect prompted
again with a 60 s timeout and an unanswered prompt failed the reconnect.
Session-only secrets now live on the session, survive auto and explicit
reconnects, and IDENTIFY is sent on every registration as before.
- The VNC client refuses desktop sizes over 64 Mpx (ServerInit and
DesktopSize/ExtendedDesktopSize) and ZRLE rectangles over 64 MiB before
allocating, so a hostile server can't force a multi-gigabyte allocation.
- Release URLs from the update manifest and repository URLs from the GitHub
API are handed to the system opener only when they are http(s) links.
- SSH key pairs are generated with ssh-key (Ed25519/ECDSA P-256/RSA) and
encrypted in-process, so the passphrase no longer appears as an ssh-keygen
command-line argument visible in the process list.
- open_tunnel's loopback bridge checks the accepted peer is our own client
socket, so a local process racing for the ephemeral port can't be spliced
into the IRC/VNC SSH tunnel.
- run_command caps buffered output at 1 MiB; local-forward abort handles are
pruned as connections finish; SOCKS5 host/credential lengths over 255 bytes
are rejected instead of being truncated into a malformed request.
- Secrets::delete_profile removes every profile credential kind from the OS
keychain as well as the vault (previously only the vault was purged, so
keychain passwords/tokens outlived the profile).
- write_private writes to a sibling temp file and renames it into place, so a
crash mid-write cannot truncate the vault or an export, and a pre-existing
world-readable file is replaced rather than reused.
- Argon2-derived vault/export keys are Zeroizing and wiped on drop.
- Recordings (which capture typed input), JSON exports and --data-dir
directories are created owner-only.