v0.21.0
What's Changed
A minor version spent on what twelve patch releases in fourteen days left
behind: waits with no deadline, a bastion setting that was stored and then
ignored at connect time, and nine dependency advisories reported for code that
was never compiled. Two of the fixes are requirement bumps a patch release would
have refused, and one is a breaking rustconn-core signature.
Added
-
CI builds and tests the macOS-specific code — a
macos-sysjob clippies and tests the fourrustconn-*-syscrates on a macOS runner. Every other job in the matrix is Linux, and until now nothing outside the maintainer's own machine had ever compiled the macOS side. That had already cost something: therustconn-pty-syscontract test proving thepre_exechook runs in the forked child — the thing that makes SSH password prompts work (#175) — had never passed there, because it acceptedENOTTYandEPERMwhile macOS answersENODEV, and it took until 0.20.11 for anyone to notice. A test guardingunsafethat no job executes is a test whose state nobody knows.Scoped to the four helper crates deliberately: they hold every
unsafeblock in the repository, and between them they need one Homebrew package. Building the GUI there would mean gtk4, libadwaita, vte3 and WebKitGTK from Homebrew — andweb-embedded, a default feature, cannot build on macOS at all, which is worth fixing before the scope grows.
Fixed
-
A Jump Host set on a group or in Preferences → Network was ignored at connect time (#301) — the three-tier resolver shipped in 0.20.9 and is correct; what was never wired is the launchers. A bastion picked from the dropdown above connection level was stored, shown in the editor as inherited, synced between machines — and then dropped at the moment it was needed. The 0.20.9 notes said inheritance "reads the same for every protocol", which was true of neither the picker for SSH nor of RDP, VNC and SPICE at all.
Seven call sites now resolve the first hop through one function: the SSH terminal, the RDP, VNC and SPICE tunnel gates, two
has_jump_hostguards that drive the sidebar status and monitoring, and the check deciding whether the bastion's own password may be prompted for. That last one matters most — it was reading the raw field, so it answered "no bastion" for an inherited one and suppressed nothing, which is how the target's password could be fed to the bastion prompt (#191).Precedence, in order: the connection's own Jump Host field wins unconditionally; otherwise
Network Mode: Directrefuses a bastion outright; otherwise the nearest group in the chain that has one; otherwise the global setting. Only the first hop resolves this way — a bastion's own bastion stays a property of that bastion, not something the target's group can redirect.What this does not fix: a text ProxyJump at group or global level still does not reach RDP, VNC or SPICE. Those protocols reach a bastion by opening an SSH tunnel, and a tunnel needs a saved connection with its own port, identity file and credentials — a
user@host:portstring has none of that. For them the bastion must be a picked connection, which now inherits. -
The Secrets page in Settings could stay empty instead of listing the password managers it found — each manager is probed by running its command-line tool, the probes run together, and the page waited for the slowest, so one tool that never answered held all of them. The three most likely to do that are the three that reach out over the network or wait on a biometric prompt:
bw status,op whoami,passbolt list. Every probe now gives up after five seconds and reports that manager as unavailable, which is already how an errored probe reads and is the honest answer when a tool will not say otherwise. -
A KeePass operation could freeze the window with no way out — all twelve
keepassxc-clicall sites waited indefinitely, in a project that already bounds a credential resolution at 30 s and every Secret Service call at 10 s. A database on a network share that had gone away, or one locked by another program, left the calling thread never coming back. Reads and probes now give up after ten seconds and writes after thirty: the consequence differs rather than the expected duration, since a kill delivered mid-write lands inside a KDBX rewrite. The timeout message names heavy key-derivation settings as a cause alongside a locked file, because that cost is paid on every invocation —keepassxc-clireopens the database each run, and a single save is four of them. -
Printing from an RDP session could stall the session itself — RustConn asks the local print system for the queue list when the connection opens and sends the document through it when the guest prints, on the connection's own thread, with no limit on any of it. Bounded at two seconds, where losing the answer costs the printer list rather than the session. One limitation is now stated rather than implied: the document is written to
lp's stdin before the wait, so a page larger than the pipe buffer blocks in that write, which no deadline on the wait can cover. -
Nine advisory warnings came from code that was never compiled — the macOS tray took
tray-iconandmudawith their default features, which pulllibappindicator,gtk0.18 and the rest of the GTK3 binding stack, unmaintained since 2024, pluslibxdo,x11and their build machinery: 39 crates. None of it was ever built — those dependencies are already Linux-gated inside the two crates, and the crates are only enabled by thetray-macosfeature that the macOS bundle alone passes, since the Linux tray isksniand pure D-Bus. Butcargo auditreadsCargo.lock, which is target-agnostic, so the advisories were reported on every platform regardless. That is also why target-gating the declarations would have fixed nothing and turning the feature off does: the feature is what pulled the optional dependency into the graph.Measured before and after on the same tree: ten warnings down to one. The nine that went are RUSTSEC-2024-0412, -0413, -0415, -0416, -0418, -0419, -0420 (GTK3 bindings), -0429 (
glibunsoundness) and -0370 (proc-macro-error). The one that remains is RUSTSEC-2023-0089,atomic-polyfill, which arrives by another route. Worth stating plainly because it was assumed otherwise while planning this release: none of the nine was ever in an allow-list..cargo/audit.tomlanddeny.tomlignore exactly one advisory between them and still do — the warnings were being counted and tolerated rather than named and accepted. -
A Flatpak build could be stopped by any one of seven download hosts having a bad day — and one was: the 0.20.11 release job failed twice with
Failed to download sources: module inetutils, becauseftp.gnu.orgwas in the middle of an outage. inetutils is the third of twelve modules, so nothing was compiled; the deb, RPM and AppImage jobs all succeeded, and the Flatpak job alone held back the GitHub release, OBS, Snap and Homebrew, which depend on it. Not one source in the three manifests usedmirror-urls, which is the flatpak-builder feature for exactly this.inetutils now leads with
ftpmirror.gnu.org— GNU's own redirector, and the primary rather than a fallback, because whileftp.gnu.orgis down a primary pointing at it costs every build the full 60-second timeout first — withftp.gnu.organdmirrors.kernel.orgbehind it.mckeeps its canonical URL and gainsftp.osuosl.org, the host actually behind the Midnight Commander FTP and HTTPS where the primary is plain HTTP. Both mirrors were verified to serve a byte-identical tarball before being trusted.slangdeliberately gets none: jedsoft.org has no alternative that could be verified, and an unverified mirror trades a download failure for a checksum failure. -
Both Flatpak
cargo-sources.jsonmanifests were three lock-file changes behind —Cargo.lockwent 733 → 695 crates across the tray feature change and the quick-xml and argon2 bumps, and neither manifest was regenerated, so both still listed 1455 entries including 15 GTK3 crates that were no longer in the graph. A sources file behind the lock makesflatpak-buildervendor crates the build then cannot find. Regenerated to 1377 entries, identical in both manifests. -
A translatable string could ship untranslated with every check reporting the catalogues complete — every i18n check read the committed template, so a string that was never extracted was invisible to all of them: the file was listed as a source, the literal was extractable, and all 17 catalogues reported 100% — complete with respect to a template that was itself incomplete, and the string rendered in English in every locale. It had happened in three releases running, eighteen strings between them, each time with a note saying the gap was real and would outlive the fix. The template is now regenerated and compared against the sources, before a release and in CI.
Changed
-
KeePassStatus::save_password_to_kdbxtakes the entry password as&SecretStringinstead of&str— a breaking change to a publicrustconn-coresignature, which is why it waited for a minor version. No caller was leaking today: all seven passed either a borrow of aZeroizingbuffer or anexpose_secret()directly. The&strsimply did not stop the next one handing it a bareString, and this is the API a GUI, a CLI and any other consumer of the crate reach for when they store a password.Two of the call sites get strictly better out of it: they are synchronous, so they now borrow the caller's secret and their intermediate plaintext copy is gone entirely. Three pass the secret into a
moveclosure on another thread and need an owned value;SecretStringisSecretBox<str>andstris notClone, so they share it through anArcrather than cloning — a clone would duplicate theBox<str>, which is the second plaintext the change is trying to remove.
Documentation
-
The VTE version ceiling in the Flatpak manifests now says why it exists, or admits that nobody wrote it down — three release notes across the 0.20 series stated that VTE "stays pinned below 0.81 by design", and not one of them, nor any comment or commit message, said what the design was. An assertion repeated until it looks settled is not a reason. What is recorded instead is the evidence:
vte4is at 0.10 with itsv0_76feature selected, so the API in use is VTE 0.76's and 0.84 still provides it; the maintainer's macOS build runs against Homebrew's 0.84.1 daily, which is the only empirical datum and points at the ceiling being unnecessary. What is not known is how a bundled 0.81+ behaves inside the GNOME 50 runtime, and whether themcSGR-mouse workaround beside it is sensitive to the version — those two are why it is still pinned rather than lifted. The steps to lift it, and the instruction to write down which of them failed if it cannot be, are in the manifest. -
adw-1-6cannot be retired yet, and the reason is now beside the feature — its comment invites "retire this once no supported target is below 1.6", which prompts the question every release, and the snap'score24platform is usually cited as the blocker, making acore26GNOME extension look like the thing to wait for. It is not: the binding constraint is Ubuntu 24.04 LTS, which ships libadwaita 1.5.0, is the baseline tier inpackaging/obs/README.md, and is supported to 2029. Checking on core26 answers nothing while 24.04 is a target.
Dependencies
-
argon2 0.5 → 0.6 — the key derivation behind the encrypted credential stores, and the question was whether existing files still open. Deferred from 0.20.11 for that reason. They do, and it is now proved rather than assumed: the derivation is byte-identical, so no migration is needed and no stored credential is affected. Nothing in RustConn's own code had to change, since the algorithm, version and cost parameters were already passed explicitly.
Proving it needed a fixture that did not exist.
derive_settings_keyhas long had a captured blob that fails to decrypt if the format moves;derive_passphrase_key, which is what opens the portable store, had none. That gap mattered at exactly this moment: the round-trip tests create a store and open it with the same build, so they pass whether or not the derivation changed, while every store already on disk would have become unopenable with nothing going red. A fixed Argon2id vector captured under 0.5.3 is now pinned and asserted to still hold, alongside a second test pinning the default cost parameters. Cheap parameters on purpose — Argon2id does not special-case cost, so a change in output shows up at 1 MiB as clearly as at 64 MiB. Bringspassword-hash0.6.1,blake20.11 andphc0.6.1; dropsrand_core0.6.4. -
quick-xml 0.41 → 0.42 — deferred from 0.20.11 because it needed a requirement widened, which a patch release would not take. The break is that decoding moved from the caller to the parser:
QNameand the three text event types now dereference tostr, andAttribute::valueis aCow<str>rather thanCow<[u8]>. EveryString::from_utf8_lossyand every.decode()on the reader path in the libvirt and RoyalTS importers was therefore redundant rather than merely renamed, so twelve call sites got shorter. Semantics were preserved deliberately at the one place they could have drifted — the text event still yields the event's own content rather than the newline-normalised form, which is what the byte-level code did. All 31 importer tests pass unchanged. -
Updated: cpufeatures 0.3.0 → 0.3.1, flate2 1.1.9 → 1.1.10 (bringing miniz_oxide 0.9.1).
cargo auditreports one allowed warning across 695 dependencies — RUSTSEC-2023-0089,atomic-polyfill, unmaintained and reached transitively;cargo deny checkis clean, andcargo machetefinds no unused dependency. Every auto-resolving CLI download endpoint answered: kubectl 1.37.0, Tailscale 1.102.3, Teleport 18.10.0, Boundary 0.21.3, Hoop.dev 1.150.2, Bitwarden CLI 2026.8.0, 1Password CLI 2.39.0, and TigerVNC — the only pinned one — is current at 1.16.2.
Installation
Flatpak (Recommended)
flatpak install flathub io.github.totoshko88.RustConnSnap
sudo snap install rustconnDebian/Ubuntu (.deb from this release)
sudo dpkg -i rustconn_0.21.0_amd64.deb
sudo apt-get install -f # Install dependencies if neededFedora (.rpm from this release)
sudo dnf install rustconn-0.21.0-1.fc44.x86_64.rpmAppImage
chmod +x RustConn-0.21.0-x86_64.AppImage
./RustConn-0.21.0-x86_64.AppImagemacOS (Homebrew)
brew tap totoshko88/rustconn
brew install rustconn
open $(brew --prefix)/opt/rustconn/RustConn.appAll dependencies (GTK4, libadwaita, VTE, Adwaita icons) are installed automatically.
Requires macOS 13 (Ventura) or later.
OBS Repositories
Packages available at: https://build.opensuse.org/package/show/home:totoshko88:rustconn/rustconn
# Debian 13 (Trixie)
echo 'deb http://download.opensuse.org/repositories/home:/totoshko88:/rustconn/Debian_13/ /' \
| sudo tee /etc/apt/sources.list.d/rustconn.list
curl -fsSL https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/Debian_13/Release.key \
| gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/rustconn.gpg > /dev/null
sudo apt update && sudo apt install rustconn
# Ubuntu 24.04 LTS (Noble)
echo 'deb http://download.opensuse.org/repositories/home:/totoshko88:/rustconn/xUbuntu_24.04/ /' \
| sudo tee /etc/apt/sources.list.d/rustconn.list
curl -fsSL https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/xUbuntu_24.04/Release.key \
| gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/rustconn.gpg > /dev/null
sudo apt update && sudo apt install rustconn
# Ubuntu 26.04 LTS (Resolute)
echo 'deb http://download.opensuse.org/repositories/home:/totoshko88:/rustconn/xUbuntu_26.04/ /' \
| sudo tee /etc/apt/sources.list.d/rustconn.list
curl -fsSL https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/xUbuntu_26.04/Release.key \
| gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/rustconn.gpg > /dev/null
sudo apt update && sudo apt install rustconn
# Fedora 44
sudo dnf config-manager addrepo --from-repofile=https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/Fedora_44/home:totoshko88:rustconn.repo
sudo dnf install rustconn
# Fedora 43
sudo dnf config-manager addrepo --from-repofile=https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/Fedora_43/home:totoshko88:rustconn.repo
sudo dnf install rustconn
# openSUSE Tumbleweed
sudo zypper ar https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/openSUSE_Tumbleweed/ rustconn
sudo zypper ref && sudo zypper in rustconn
# openSUSE Leap 16.0
sudo zypper ar https://download.opensuse.org/repositories/home:/totoshko88:/rustconn/openSUSE_Leap_16.0/ rustconn
sudo zypper ref && sudo zypper in rustconnArch Linux (AUR)
yay -S rustconnFreeBSD (Ports)
pkg install rustconnFull installation guide: https://github.com/totoshko88/RustConn/blob/main/docs/INSTALL.md