Clavyn v0.1.2-alpha.6
Pre-release
Pre-release
What's Changed
This alpha is a performance and hardening release. Terminals now render on the GPU, the app boots in 3 IPC round-trips instead of 12, terminal output travels as coalesced binary batches instead of one JSON event per packet, and the vault file authenticates its own header. It also closes a remote process-crash vector in the SSH stack and a webview route to downgrade-installed updates.
Highlights
- GPU-accelerated terminal rendering. Panes render through the xterm WebGL addon — a render-bound stream measured 5.9x faster per frame (4.5 ms → 0.73 ms at 120x40), up to 10.7 MiB/s. A least-recently-used budget of eight panes keeps the app under the browser's 16-context WebGL cap; evicted panes and lost contexts fall back to the DOM renderer with the transcript intact. The addon loads on demand in its own chunk. (#63)
- Startup boots in 3 IPC round-trips instead of 12. The independent boot queries run in parallel and the update check no longer blocks first paint — it fires in the background and still opens the modal when a release is found. Host/group loads are de-duplicated and versioned, so an in-flight read can no longer overwrite a newer change. (#55)
- Terminal output is coalesced off the JSON event path. Output batches at 64 KiB or a 6 ms window and travels as binary over a per-session channel. Bulk output drops from 128 to 16 IPC messages per MiB; a byte-at-a-time writer drops from 19,519 messages to 30; a 32 KiB chunk no longer becomes ~114 KiB of evaluated JavaScript. Keystroke echo still goes out immediately. (#49)
- Changed host keys are reported with both fingerprints instead of a bare "Unknown server key". The presented key is held under Known Hosts pending fingerprint confirmation; removing a host tombstones its entry so it cannot silently fall back to trust-on-first-use; "Forget permanently" and key replacement confirm through a native OS dialog showing the real stored fingerprint. (#38)
- The vault file authenticates its own header. Key metadata, salt, version and epoch are bound to the ciphertext as AES-GCM associated data, so a tampered
vault.json(e.g. a swapped-in public key under your label) fails the tag check. Decrypted payload memory is zeroized, and legacy vault migration rebuilds metadata from the private keys rather than trusting the old header. (#49) - russh is built without zlib, closing an unbounded decompression loop a hostile server could drive into a panic that took the whole process down. Clavyn never negotiates compression, so nothing is lost. (#29)
- SSH connects no longer freeze the vault. Connects held the vault mutex across the network exchange — one silent host blocked unlock, reset and every key operation until restart. The lock is now held only for key auth, connects are time-bounded with keepalives, and the Argon2id KDF runs on a blocking thread with the master key cached per session. (#52)
Fixed
- A live session id can no longer be hijacked by a second terminal; concurrent local terminals are capped and the slot is reserved before the blocking PTY spawn. (#31, #49)
- A session's final output is never lost to the close notification —
cat bigfile; exitpreviously dropped the last batch. A detached PTY reader can no longer emit stale output into a reused session id. (#49) - A second instance can no longer clobber shared app data (it focuses the running window), and workspace layouts are validated on write so a corrupt split ratio can't brick startup. (#40)
- Key import no longer leaves private key material in the dialog after Cancel, a failed import, or a vault auto-lock; a failed import keeps your label and Import tab. (#58)
- Out-of-range terminal dimensions are rejected instead of wrapping to a zero-column PTY or going out on the wire over SSH. (#49, #61)
- The frontend entry chunk is 25% smaller — navigable views and the search addon load on first render,
tailwind-mergeis dropped, and the build targets es2022 with Vue production flags. (#37) - The update check runs once per launch instead of twice. (#66)
Security hardening
- Vault reset now tears down live SSH sessions, SFTP channels and local PTYs under the same destructive boundary it describes. (#58)
- Disabling biometric unlock now requires an unlocked vault; the webview's unused updater/process capability — which permitted downgrades and an unconditional app-kill — is removed. (#33)
- The webview CSP now declares every directive that does not inherit from
default-src, drops the unused GitHub connect-src origins, and scopes shell open to the two URLs the UI uses. (#47) - Windows state files get an explicit owner-only DACL instead of inheriting the profile's, and the temp file is
CREATE_NEWso a planted file can't be written through and renamed into place. (#46) - The release pipeline is hardened: CI actions pinned to commit SHAs,
npm ciwithout install scripts, per-job release permissions, fork PRs kept off self-hosted runners, and semver-validated dispatch inputs. (#42) - Two unregistered SFTP commands — an unbounded remote read and an unconstrained remote write — are deleted. (#61)
Dependencies
- Rust: russh-sftp 3.0, russh-keys 0.49.2, russh-cryptovec 0.62, argon2 0.6, rand 0.10, base64 0.23, uuid. (#80, #82, #83, #84, #85, #88, #89)
- Frontend: Vite 8, TypeScript 7, @xterm/addon-fit 0.11, @xterm/addon-webgl 0.19; Playwright 1.63 for e2e. (#75, #78, #79, #90)
- CI: actions/checkout 7, actions/setup-node 7, actions/upload-artifact 7, tauri-action 1.0. (#72, #73, #74, #76)
See CHANGELOG.md for the full history.
Full Changelog: v0.1.2-alpha.5...v0.1.2-alpha.6