Skip to content

Releases: TheHolyOneZ/Pylon

Pylon v0.2.0

Choose a tag to compare

@TheHolyOneZ TheHolyOneZ released this 15 May 12:46

Changelog

[0.2.0] — 15.05.2026

Fixed

SSH Handshake [Session(-1)] Unable to exchange encryption keys on Windows

Root cause: libssh2-sys on Windows MSVC has two separate feature flags:

  • vendored-openssl — compiles OpenSSL from source (was already set)
  • openssl-on-win32 — tells libssh2 to use OpenSSL as its crypto backend (was missing)

Without openssl-on-win32, libssh2 always compiles with WinCNG (Windows Cryptography
Next Generation) as its backend, regardless of whether OpenSSL is present. WinCNG's
Diffie-Hellman key exchange implementation has been broken since Windows 10 v1903 (May 2019),
causing the server to close the connection mid-handshake. libssh2 surfaces this as
LIBSSH2_ERROR_SOCKET_NONE = -1 with the message "Unable to exchange encryption keys".

macOS was unaffected because non-Windows targets unconditionally use OpenSSL.

Fix (src-tauri/Cargo.toml):

ssh2 = { version = "0.9", features = ["vendored-openssl", "openssl-on-win32"] }

Adding openssl-on-win32 sets CARGO_FEATURE_OPENSSL_ON_WIN32 during the libssh2-sys build,
which switches the compile-time define from LIBSSH2_WINCNG to LIBSSH2_OPENSSL.

Additional hardening (all 4 handshake sites):

  • Removed pre-handshake TCP set_read_timeout/set_write_timeout — once libssh2 owns the
    socket via set_tcp_stream, Winsock-level timeouts conflict with libssh2's internal I/O loop.
  • Added sess.set_blocking(true) + sess.set_timeout(30_000) — proper libssh2-level
    30 s timeout that works correctly on both Windows and macOS.

Build requirement (Windows): Strawberry Perl must be in PATH for the vendored-openssl
build step that compiles OpenSSL from source. Install portable Perl to C:\strawberry-perl
and add C:\strawberry-perl\perl\bin and C:\strawberry-perl\c\bin to the system PATH.


Host Key Policy settings not respected in Vault and Quick Connect

Problems:

  1. ZVault quick-connect always showed "accept via Quick Connect first" regardless of the
    host key policy setting in Settings → Security.
  2. There was no way to accept an unknown host key from ZVault without switching to Quick Connect.
  3. The Advanced panel in Quick Connect hardcoded "Ask (known_hosts)" instead of showing
    the live policy value.

Fix:

  • Added vault_accept_host_key_and_connect(credential_id, permanent) Rust command that
    performs a fresh TCP connect + handshake + optional known_hosts write + authentication
    using vault-stored credentials (secrets never leave the backend).
  • VaultModal now reads hostKeyPolicy from the settings store on every vault connect:
    • auto-accept → silently calls vaultAcceptHostKeyAndConnect with permanent: false
    • strict → rejects immediately with an error toast
    • ask (default) → shows the same HostKeyModal used by Quick Connect, then calls
      vaultAcceptHostKeyAndConnect with the user's permanent/once choice
  • ConnectModal advanced panel now reads and displays the live policy value.

App icon not rendering in titlebar and lock screen on Windows

Problem: The custom titlebar, app lock screen, and settings panel all load /pylon-icon.png
via a root-relative URL. No public/ directory existed in the project, so Vite had no file
to serve at that path — the <img> broke in both dev and production builds.

Fix: Created public/pylon-icon.png (copied from src-tauri/icons/icon.png). Vite serves
the public/ directory verbatim at the root URL, so the existing /pylon-icon.png references
in TitleBar.tsx, AppLock.tsx, and SettingsPanel.tsx now resolve correctly in both modes.


Installer and bundle still named Pylon_0.1.0_* after version bump

Problem: src-tauri/tauri.conf.json still contained "version": "0.1.0". This is the file
the Tauri bundler reads when naming build artifacts (Pylon_0.1.0_x64-setup.exe,
pylon_0.1.0_x64_en-US.msi). Updating only package.json and Cargo.toml had no effect
on the installer names.

Fix: Bumped tauri.conf.json version to 0.2.0. Also updated the hardcoded v0.1 badge
in TitleBar.tsx to v0.2.


Rust compiler warning: unused variable mirror_perms in fs_watcher.rs

Problem: push_file_via_sftp in src-tauri/src/fs_watcher.rs:572 accepted a
mirror_perms: bool parameter that was never read (permission mirroring not yet implemented
for that path).

Fix: Renamed to _mirror_perms to explicitly mark the parameter as intentionally unused,
silencing the #[warn(unused_variables)] warning.

Pylon v0.1.0

Choose a tag to compare

@TheHolyOneZ TheHolyOneZ released this 30 Apr 10:36
8a9a76d

Pylon

Cross-platform SSH and SFTP desktop client built with Tauri v2, Rust, and React.

License: GPL-3.0 | Version: 0.1.0 (beta) | Platforms: Windows, macOS, Linux

Pylon is in beta. The vault format and configuration schema may change between pre-release versions. Back up your vault before upgrading.


Download

→ zsync.eu/pylon

Windows, macOS (Apple Silicon + Intel), and Linux — all builds available on the website. No account required. Free and open source under GPL-3.0.

macOS: Not yet notarized. Right-click the .app → Open → confirm in the security dialog on first launch.


Screenshots

Files Terminal
Files tab Terminal tab
Sync Transfers
Sync tab Transfers drawer
Settings ZVault
Settings ZVault

Features

File Management

  • Dual-pane browser: local filesystem on the left, remote SFTP on the right, resizable
  • Multi-select with Shift+Click (range), Ctrl+Click (individual), Ctrl+A (all)
  • Drag-and-drop upload and download between panes
  • Context menu with full file operations
  • Sortable columns (name, size, modified, permissions)
  • Editable path bar (Ctrl+L), click-to-navigate breadcrumb, bookmarks
  • File permission display (octal and rwx notation)
  • Regex and fuzzy filter per pane
  • Remote chmod editor

Transfer Engine

  • Resume support: interrupted transfers restart from the last verified byte
  • Atomic writes: files staged as .pylon_tmp_* and renamed on completion
  • SHA-256 checksumming per transfer
  • Real-time bytes/sec and ETA in the transfer drawer (Ctrl+T)
  • Pause, resume, and retry controls per transfer
  • Concurrent transfers (1–16, configurable)
  • Optional per-transfer bandwidth limit (kbps)
  • Recursive directory upload/download
  • Overwrite policy: ask / overwrite all / skip all

SSH Terminal

  • xterm.js with full VT100/ANSI, 256-color support
  • Multiple terminal tabs per session, with tab rename
  • Navigating the remote pane sends a cd command to the active terminal
  • Command snippets toolbar (save frequently used commands as quick-launch buttons)
  • Clickable URLs via xterm-addon-web-links
  • Shell selection (bash, zsh, sh, fish) per connection

ZVault — Credential Manager

  • AES-256-GCM encryption for all stored credentials
  • Argon2id key derivation from master passphrase (m=64 MB, t=3, p=4)
  • Unique random salt and nonce per vault; no pre-computation attacks apply
  • Auto-lock after configurable idle timeout
  • Decrypted credentials kept in memory only; never written to disk in plaintext
  • SSH key path storage alongside passwords
  • Full CRUD UI for managing stored profiles

Sync Watchers

  • Watch a local directory and sync changes to a remote path over SFTP
  • Powered by the notify crate (inotify / kqueue / ReadDirectoryChangesW)
  • Only changed files are uploaded; unchanged files are skipped
  • 100 ms debounce window to coalesce rapid saves
  • Delete propagation: remote files are deleted when local files are removed
  • Optional timestamp preservation (mtime mirrored to remote)
  • Optional permission mirroring (chmod applied to remote)
  • .syncignore file support (gitignore syntax); .gitignore is also respected
  • Keep-on-disconnect: watchers can be paused rather than stopped when a session ends
  • Configurable overwrite rules: always overwrite / new files only / update existing / confirm on overwrite / confirm all

Metadata Stripping

Strip privacy-sensitive metadata in-flight on upload, or on-demand via context menu.

Format Stripped
JPEG / PNG / WebP / GIF EXIF data (GPS, camera, serial, timestamps, thumbnails)
PDF Author, creator, producer, keywords, XMP metadata

Enable globally in Settings → Security, or trigger per-file from the context menu.

Security

  • SSH host key verification against ~/.ssh/known_hosts on every connection
  • Changed host keys show a blocking warning dialog (configurable policy: ask / auto-accept / strict)
  • SSH keepalive prevents silent session drops
  • No telemetry; no outbound connections beyond what you explicitly initiate
  • Tauri CSP configured in src-tauri/capabilities/default.json

Other

  • Command palette (Ctrl+K)
  • Activity log
  • Keyboard shortcuts for all major actions
  • Bookmarks for local and remote paths

Building

Prerequisites

Tool Required version
Rust stable ≥ 1.77
Node.js ≥ 20 LTS
pnpm ≥ 9

Linux — install WebKit2GTK and related system libraries:

# Debian / Ubuntu
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf

# Fedora / RHEL
sudo dnf install webkit2gtk4.1-devel libappindicator-gtk3-devel librsvg2-devel

macOS — Xcode Command Line Tools:

xcode-select --install

Dev

pnpm install
pnpm tauri dev

Frontend only (no Rust compilation, faster iteration):

pnpm dev

Verbose Rust logging:

RUST_LOG=debug pnpm tauri dev

Release build

pnpm install
pnpm tauri build

Artifacts are placed in src-tauri/target/release/bundle/.


Keyboard Shortcuts

Global

Shortcut Action
Ctrl+K Command palette
Ctrl+N New connection
Ctrl+T Toggle transfer drawer
Ctrl+D Disconnect active session

File browser

Shortcut Action
F5 Refresh pane
Ctrl+A Select all
Ctrl+L Edit path bar
Enter Open folder / download file
Backspace Navigate up

Terminal

Shortcut Action
Ctrl+Shift+T New terminal tab
Ctrl+Shift+C Copy selection
Ctrl+Shift+V Paste

Tech Stack

Frontend: React 18, TypeScript 5, Vite 5, Tailwind CSS 3, Zustand, Framer Motion, xterm.js 5, Lucide React, react-resizable-panels

Backend (Rust): tauri 2, ssh2, tokio, aes-gcm, argon2, notify, image, kamadak-exif, lopdf, sha2, ignore, rayon, flume, serde, anyhow, tracing


Vault file location

Platform Path
Linux ~/.config/pylon/vault-default.json
macOS ~/Library/Application Support/pylon/vault-default.json
Windows %APPDATA%\pylon\vault-default.json

If you forget your master password there is no recovery mechanism. The vault cannot be decrypted without the correct passphrase.


macOS note

Pylon is not yet notarized by Apple. On first launch, right-click the .app and choose Open, then confirm in the security dialog.


License

GNU General Public License v3.0. See LICENSE.