Skip to content

v0.21.0

Choose a tag to compare

@github-actions github-actions released this 28 Aug 13:03
· 11 commits to main since this release

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-sys job clippies and tests the four rustconn-*-sys crates 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: the rustconn-pty-sys contract test proving the pre_exec hook runs in the forked child — the thing that makes SSH password prompts work (#175) — had never passed there, because it accepted ENOTTY and EPERM while macOS answers ENODEV, and it took until 0.20.11 for anyone to notice. A test guarding unsafe that no job executes is a test whose state nobody knows.

    Scoped to the four helper crates deliberately: they hold every unsafe block in the repository, and between them they need one Homebrew package. Building the GUI there would mean gtk4, libadwaita, vte3 and WebKitGTK from Homebrew — and web-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_host guards 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: Direct refuses 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:port string 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-cli call 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-cli reopens 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-icon and muda with their default features, which pull libappindicator, gtk 0.18 and the rest of the GTK3 binding stack, unmaintained since 2024, plus libxdo, x11 and 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 the tray-macos feature that the macOS bundle alone passes, since the Linux tray is ksni and pure D-Bus. But cargo audit reads Cargo.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 (glib unsoundness) 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.toml and deny.toml ignore 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, because ftp.gnu.org was 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 used mirror-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 while ftp.gnu.org is down a primary pointing at it costs every build the full 60-second timeout first — with ftp.gnu.org and mirrors.kernel.org behind it. mc keeps its canonical URL and gains ftp.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. slang deliberately 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.json manifests were three lock-file changes behindCargo.lock went 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 makes flatpak-builder vendor 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_kdbx takes the entry password as &SecretString instead of &str — a breaking change to a public rustconn-core signature, which is why it waited for a minor version. No caller was leaking today: all seven passed either a borrow of a Zeroizing buffer or an expose_secret() directly. The &str simply did not stop the next one handing it a bare String, 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 move closure on another thread and need an owned value; SecretString is SecretBox<str> and str is not Clone, so they share it through an Arc rather than cloning — a clone would duplicate the Box<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: vte4 is at 0.10 with its v0_76 feature 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 the mc SGR-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-6 cannot 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's core24 platform is usually cited as the blocker, making a core26 GNOME 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 in packaging/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_key has 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. Brings password-hash 0.6.1, blake2 0.11 and phc 0.6.1; drops rand_core 0.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: QName and the three text event types now dereference to str, and Attribute::value is a Cow<str> rather than Cow<[u8]>. Every String::from_utf8_lossy and 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 audit reports one allowed warning across 695 dependencies — RUSTSEC-2023-0089, atomic-polyfill, unmaintained and reached transitively; cargo deny check is clean, and cargo machete finds 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.RustConn

Snap

sudo snap install rustconn

Debian/Ubuntu (.deb from this release)

sudo dpkg -i rustconn_0.21.0_amd64.deb
sudo apt-get install -f  # Install dependencies if needed

Fedora (.rpm from this release)

sudo dnf install rustconn-0.21.0-1.fc44.x86_64.rpm

AppImage

chmod +x RustConn-0.21.0-x86_64.AppImage
./RustConn-0.21.0-x86_64.AppImage

macOS (Homebrew)

brew tap totoshko88/rustconn
brew install rustconn
open $(brew --prefix)/opt/rustconn/RustConn.app

All 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 rustconn

Arch Linux (AUR)

yay -S rustconn

FreeBSD (Ports)

pkg install rustconn

Full installation guide: https://github.com/totoshko88/RustConn/blob/main/docs/INSTALL.md