Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bayesian SSH

Bayesian SSH Banner

An ultra-fast and intelligent SSH session manager with Bayesian-ranked search, fuzzy matching, Kerberos support, bastion hosts, and advanced history management.

What is Bayesian SSH?

Bayesian SSH transforms your SSH experience with intelligent automation:

  • Bayesian-ranked search - connections ranked by frequency, recency, and match quality
  • Intelligent fuzzy search across all commands - find connections by partial names, tags, or patterns
  • One-click connections to your servers
  • Automatic Kerberos ticket management
  • Smart bastion host routing — including interactive bastions (e.g. OVH “The Bastion”)
  • Native russh transport with SFTP support — exec, upload, download without shelling out
  • Local port forwarding (forward -L) and SOCKS5 dynamic proxy (proxy -D) tunnels
  • Multi-environment profiles (env, --env) — isolated databases per client/context
  • Tag-based organization with grouping and multi-select
  • Complete connection history with statistics
  • SQLite database for persistence
  • Desktop GUI with connections, history, SFTP browser, and tunnels

Documentation Overview

This documentation is organized into the following sections:

  • Getting Started - Installation, quick start, and initial configuration
  • User Guide - Day-to-day usage and features
  • Advanced Usage - Enterprise environments, cloud infrastructure, and complex scenarios
  • Reference - Technical architecture, troubleshooting, and changelog

Installation

# CLI (bayesian-ssh + bssh) and the desktop app, with its menu entry
curl -fsSL https://raw.githubusercontent.com/abdoufermat5/bayesian-ssh/main/install.sh | bash

# CLI only (servers, headless machines)
curl -fsSL https://raw.githubusercontent.com/abdoufermat5/bayesian-ssh/main/install.sh | bash -s -- --no-gui

# Choose interactively (pre-built or source build, with or without the desktop app)
curl -fsSL https://raw.githubusercontent.com/abdoufermat5/bayesian-ssh/main/install.sh | bash -s -- --interactive

Binaries go to /usr/local/bin and every download is checked against the release’s SHA256SUMS. The desktop app needs WebKitGTK 4.1 (libwebkit2gtk-4.1-0 on Debian/Ubuntu, webkit2gtk4.1 on Fedora); the installer warns when it is missing.

Option 2: Manual Build

Prerequisites

rustup install stable

Build and Install

# Clone and build
git clone https://github.com/abdoufermat5/bayesian-ssh.git
cd bayesian-ssh

# Build and install using Makefile
make release
make install

# Or build manually
cargo build --release
sudo cp target/release/bayesian-ssh /usr/local/bin/

Option 3: Snap Store

sudo snap install bayesian-ssh
sudo snap connect bayesian-ssh:ssh-keys   # read-only ~/.ssh and /etc/ssh
sudo snap alias bayesian-ssh bssh         # optional short alias

The snap ships both the CLI (bayesian-ssh, or bssh after the alias) and the desktop app (bayesian-ssh.gui). Strict confinement limits it — ~/.ssh is read-only (known_hosts is not updated, key generate cannot write there), its config/database live under ~/snap/bayesian-ssh/current/.config/bayesian-ssh, it uses its own ssh/kinit/klist, and Kerberos (optional) needs sudo snap connect bayesian-ssh:kerberos-tickets to reuse host tickets. See Distribution for the full list and how to publish.

Verify Installation

bayesian-ssh --version

Verify a release

Release assets are signed. With cosign and the gh CLI installed, check a downloaded release like this:

cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity-regexp '^https://github\.com/abdoufermat5/bayesian-ssh/\.github/workflows/release\.yml@refs/(tags/v.+|heads/main)$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS

sha256sum --ignore-missing -c SHA256SUMS

gh attestation verify bayesian-ssh-linux-x86_64.tar.gz --repo abdoufermat5/bayesian-ssh

install.sh performs the cosign check automatically when cosign is on PATH, and always verifies SHA256SUMS with sha256sum -c. See Distribution for details.

In-app updates

The desktop .AppImage and the bayesian-ssh-desktop .deb/.rpm from the GitHub release can update themselves from Settings → Updates; installing closes open sessions and restarts the app. The snap, the install.sh binaries, the unified bayesian-ssh packages and source builds are updated the way they were installed.

Enable Tab Completion

Generate and source a completion script for your shell:

# Bash
bayesian-ssh completions bash > bayesian-ssh-completion.bash
source bayesian-ssh-completion.bash

# Zsh
bayesian-ssh completions zsh > _bayesian-ssh
# Move to your zsh completions directory

# Fish
bayesian-ssh completions fish > bayesian-ssh.fish
# Move to your fish completions directory

To make completions permanent, add the source line to your shell’s rc file (e.g. ~/.bashrc).

Quick Start

Add Your First Server

bayesian-ssh add "My Server" server.company.com

Connect to It

bayesian-ssh connect "My Server"

Browse All Connections

# List view
bayesian-ssh list

# Desktop GUI
bayesian-ssh desktop

Core Commands at a Glance

CommandDescription
bayesian-ssh addAdd a new connection
bayesian-ssh connectConnect to a server (fuzzy search)
bayesian-ssh listList all connections
bayesian-ssh showShow connection details
bayesian-ssh editEdit a connection
bayesian-ssh removeRemove a connection
bayesian-ssh importImport from SSH config
bayesian-ssh desktopLaunch the desktop GUI
bayesian-ssh historyView session history
bayesian-ssh aliasManage connection aliases
bayesian-ssh configView/update configuration
bayesian-ssh statsView statistics
bayesian-ssh closeManage active sessions
bayesian-ssh backupBackup database
bayesian-ssh restoreRestore from backup
bayesian-ssh pingCheck server latency

All commands support intelligent fuzzy search:

bayesian-ssh connect "webprod"     # Finds "web-prod-server"
bayesian-ssh connect "prod"        # Shows all production servers
bayesian-ssh show "dbprod"         # Show connection details
bayesian-ssh edit "apigateway"     # Edit connection settings

Search is Bayesian-ranked by default, combining:

  • Usage frequency (with Laplace smoothing)
  • Match quality (exact, prefix, word-boundary, contains)
  • Recency (exponential decay based on last use)
  • Success rate (connections that work get boosted)

Configuration

Configuration File Location

Bayesian SSH automatically creates its configuration directory at:

~/.config/bayesian-ssh/
├── config.json          # Application configuration
└── history.db           # SQLite database

Viewing and Updating Configuration

# View current configuration
bayesian-ssh config

# Update configuration
bayesian-ssh config --use-kerberos --default-user customuser

# Set default bastion
bayesian-ssh config --default-bastion bastion.company.com

# Clear default bastion
bayesian-ssh config --clear-bastion

# Set search mode
bayesian-ssh config --search-mode bayesian   # Smart ranking (default)
bayesian-ssh config --search-mode fuzzy      # Simple pattern matching

Configuration Options

{
  "default_user": "current-system-user",
  "default_bastion": "bastion.company.com",
  "default_bastion_user": "current-system-user",
  "use_kerberos_by_default": false,
  "log_level": "info",
  "auto_save_history": true,
  "max_history_size": 1000,
  "search_mode": "bayesian"
}
OptionDefaultDescription
default_userSystem userDefault SSH user for new connections
default_bastionNoneDefault bastion host for all connections
default_bastion_userSystem userDefault user for bastion connections
use_kerberos_by_defaultfalseEnable Kerberos authentication by default
log_level"info"Log verbosity: trace, debug, info, warn, error, off
auto_save_historytrueAutomatically save session history
max_history_size1000Maximum number of history entries
search_mode"bayesian"Search mode: bayesian or fuzzy

Multi-Environment Configuration

Manage separate configs per environment:

# Use a specific environment
bayesian-ssh --env production connect "Server"
bayesian-ssh --env staging list

Connection Management

Adding Connections

# Basic connection
bayesian-ssh add "Server Name" hostname.com

# With specific bastion
bayesian-ssh add "Server Name" hostname.com --bastion bastion.company.com

# Force direct connection (no bastion)
bayesian-ssh add "Server Name" hostname.com --no-bastion

# With tags for organization
bayesian-ssh add "Web Prod" web-prod.company.com --tags production,web

# With custom user and key
bayesian-ssh add "EC2 Web" ec2-web.company.com \
  --user ubuntu \
  --kerberos false \
  --key ~/.ssh/ec2-key.pem \
  --tags ec2,production

Connecting to Servers

# Exact match
bayesian-ssh connect "Server Name"

# Fuzzy search
bayesian-ssh connect "webprod"            # Finds "web-prod-server"
bayesian-ssh connect "prod"               # Shows all production servers

# With overrides
bayesian-ssh connect "Server Name" --no-bastion --user customuser

Listing Connections

# List all connections
bayesian-ssh list

# Filter by tag
bayesian-ssh list --tag production
bayesian-ssh list --tag development

Viewing Connection Details

bayesian-ssh show "Server Name"

# Fuzzy search works here too
bayesian-ssh show "dbprod"

Editing Connections

bayesian-ssh edit "Server Name"

# Fuzzy search
bayesian-ssh edit "webprod"

Removing Connections

# With confirmation prompt
bayesian-ssh remove "Server Name"

# Skip confirmation
bayesian-ssh remove "Server Name" --force

Duplicating Connections

Clone an existing connection with a new name:

bayesian-ssh duplicate "Source Server" "New Server"

Grouping Connections

Organize connections into groups:

bayesian-ssh groups

Ping / Latency Check

Test connectivity to a server:

bayesian-ssh ping "Server Name"

Session Management

View Session History

# Recent sessions with stats
bayesian-ssh history

# Filter by connection
bayesian-ssh history --connection prod

# Last 7 days, failures only
bayesian-ssh history --days 7 --failed

# Limit results
bayesian-ssh history --limit 50

Manage Active Sessions

# List active sessions (shows PIDs and stale detection)
bayesian-ssh close

# Close specific session
bayesian-ssh close "Prod Server"

# Clean up stale sessions (PIDs no longer running)
bayesian-ssh close --cleanup

# Force close all
bayesian-ssh close --all --force

View Statistics

bayesian-ssh stats

Statistics include success/failure rates, average session duration, and usage frequency across all connections.

Backup and Restore

Backup

bayesian-ssh backup

Restore

bayesian-ssh restore

This backs up and restores the SQLite database containing all connections, sessions, and aliases.

Connection Aliases

Create shortcuts for frequently used connections.

Adding Aliases

bayesian-ssh alias add p1 Portail01
bayesian-ssh alias add db prod-database
bayesian-ssh alias add staging Portail-staging

Using Aliases

Aliases work transparently with the connect command:

bayesian-ssh connect p1        # Connects to Portail01
bayesian-ssh connect db        # Connects to prod-database

Listing Aliases

# List all aliases
bayesian-ssh alias list

# List aliases for a specific connection
bayesian-ssh alias list Portail01

Removing Aliases

bayesian-ssh alias remove p1

Bastion Hosts

Bayesian SSH provides flexible bastion (jump host) management for enterprise and cloud environments.

Default Bastion

Set a default bastion that all connections will use automatically:

bayesian-ssh config --default-bastion bastion.company.com

Connections added after this will route through the default bastion unless overridden.

Direct Connections (Bypassing Bastion)

Force a direct connection, bypassing the default bastion:

# At connection creation
bayesian-ssh add "Cloud Server" cloud.company.com --no-bastion

# At connection time
bayesian-ssh connect "Cloud Server" --no-bastion

Custom Bastion per Connection

Override the default bastion with a specific one:

bayesian-ssh add "DMZ Server" dmz.company.com \
  --bastion dmz-bastion.company.com

Mixed Environment Example

# Internal servers (use default bastion automatically)
bayesian-ssh add "App Server" app.company.com --tags internal,production

# Cloud servers (direct connection)
bayesian-ssh add "Cloud App" cloud.company.com --no-bastion --tags cloud,production

# Special network (custom bastion)
bayesian-ssh add "Special Server" special.company.com \
  --bastion special-bastion.company.com \
  --tags special,production

Bastion Troubleshooting

Test Bastion Connectivity

ssh -t -A -K user@bastion.company.com

Override Bastion User

bayesian-ssh connect "Target Server" --bastion-user customuser

Check Connection Configuration

bayesian-ssh show "Server Name"

Remote Execution & File Transfer

Bayesian SSH ships with native commands for running remote commands and moving files without dropping into an interactive shell. They work with direct connections, ProxyJump (-J) bastions, and interactive bastions (such as OVH “The Bastion”) that don’t accept inline remote commands.

exec — Run a Command Remotely

bayesian-ssh exec <host> -- <command...>

The -- separator is required so that flags belonging to the remote command are not interpreted by bayesian-ssh.

# Simple command
bayesian-ssh exec web-prod -- "uptime"

# With pipes / redirection (the whole quoted string runs in the remote shell)
bayesian-ssh exec db-prod -- "psql -c 'SELECT count(*) FROM users'"

# Through a Kerberos + interactive bastion (handled automatically)
bayesian-ssh exec cs-qauth -- "ls -l /home"

The remote process’s stdout and stderr are streamed back, and bayesian-ssh exits with the remote command’s exit code so it can be chained with shell logic.

Interactive-Bastion Behaviour

When a connection has both Kerberos and an interactive bastion configured, exec can’t pass the command as SSH arguments (the bastion would treat them as a target name). Instead, it opens a PTY shell, drains the bastion banner/MOTD, brackets the command with unique BSSH_<id>_START / BSSH_<id>_END markers, and extracts the clean output between them. The PTY is widened to 200 columns so column-aware tools (ls -l, ps, etc.) don’t wrap or pad to 80 columns.

upload — Send Files to a Remote Host

bayesian-ssh upload <host> <local-path> <remote-path> [--recursive]
# Single file
bayesian-ssh upload web-prod ./config.yml /etc/myapp/config.yml

# Whole directory
bayesian-ssh upload web-prod ./dist /var/www/app --recursive

Uses native SFTP via russh-sftp when available and falls back to scp for transports that don’t support it.

download — Fetch Files from a Remote Host

bayesian-ssh download <host> <remote-path> <local-path> [--recursive]
# Single file
bayesian-ssh download db-prod /var/log/postgres.log ./postgres.log

# Whole directory
bayesian-ssh download db-prod /var/backups ./backups --recursive

forward — Local Port Forwarding

Tunnel a local port to a remote address through the SSH connection.

bayesian-ssh forward <host> -L <local-port>:<remote-host>:<remote-port>
# Expose remote PostgreSQL on localhost:5432
bayesian-ssh forward db-prod -L 5432:localhost:5432

# Reach an internal-only HTTP service via a bastion-routed host
bayesian-ssh forward jump-host -L 8080:internal-api.local:80

The tunnel runs in the foreground; Ctrl+C tears it down.

proxy — SOCKS5 Dynamic Proxy

Start a SOCKS5 proxy that tunnels traffic through the SSH connection — useful for reaching multiple internal hosts without setting up one tunnel per port.

bayesian-ssh proxy <host> -D <local-port>
# Browse the internal network through host bastion-01
bayesian-ssh proxy bastion-01 -D 1080

# Then point a browser / curl at the SOCKS5 proxy:
curl --socks5 localhost:1080 http://internal.local

Choosing the Right Tool

Use caseCommand
Run a one-off command and capture outputexec
Copy one or more filesupload / download
Reach a single remote port from your laptopforward
Reach many internal hosts/ports through one SSH sessionproxy
Interactive shell sessionconnect

Environments

Environments are isolated profiles, each with its own connection database. They let you keep personal, work, client-A, and client-B connections completely separate without juggling config files.

Listing Environments

bayesian-ssh env list

The currently active environment is highlighted.

Creating an Environment

bayesian-ssh env create work
bayesian-ssh env create client-acme

Each environment gets its own SQLite database under the application data directory.

Switching Environments

Switch the active environment (persisted across invocations):

bayesian-ssh env use work

Override the environment for a single command without changing the active one:

bayesian-ssh --env client-acme list
bayesian-ssh --env client-acme connect web-prod

The active environment name is shown in the tracing logs.

Removing an Environment

bayesian-ssh env remove client-acme

⚠️ This deletes the environment’s database and all connections it contains.

Typical Workflows

Per-client isolation

bayesian-ssh env create acme
bayesian-ssh --env acme import ~/.ssh/acme_config
bayesian-ssh --env acme list

Quick context switch

bayesian-ssh env use work    # default environment for the session
bayesian-ssh connect db-prod # uses 'work' environment
bayesian-ssh --env personal connect home-nas  # one-off in 'personal'

Desktop GUI Mode

Bayesian SSH ships a standalone desktop app: a SvelteKit/TypeScript front end on a Rust/Tauri backend. It manages the same hosts, profiles and session history as the CLI, and adds terminals, a file browser, tunnel rules and a security audit.

Launching

bssh desktop
bayesian-ssh desktop

desktop (alias gui) starts the GUI detached in the background and returns the prompt immediately. The launcher looks for the bayesian-ssh-gui binary next to the CLI executable, then in PATH.

Packaged installs start the same bayesian-ssh-gui binary:

  • the .deb, .rpm and AppImage bundles are built under the bayesian-ssh-desktop product name; the .deb and .rpm add an entry to the application menu;
  • in the snap, the bayesian-ssh.gui app runs the desktop binary and is also available from the app menu.

The first launch runs onboarding: profile name and connection defaults, an optional openSSH import, the theme, and an optional restore from a backup file.

Window layout

  • Title bar — app name, a command search button (Search hosts and commands, Ctrl/Cmd + K), a keyboard-shortcuts button, an About button and the window controls. The bar is draggable.
  • Sidebar — a profile switcher at the top, grouped navigation, status entries at the bottom, and a collapse toggle.
  • Main area — the active view, each with a single header holding its title and one primary action.

Sidebar navigation:

  • Hosts, Terminals, Files, Tunnels (Files and Tunnels appear only when they are enabled under Settings → Features)
  • Security: Keys, Audit
  • Activity: History, Snippets
  • At the bottom: Background (only while sessions run outside the tab bar), the SSH agent entry (state plus loaded-key count; click to start the agent or open its manager), the Kerberos entry, and Settings. The Kerberos entry stays hidden until a connection actually uses Kerberos.

The sidebar collapses automatically when the window is narrower than 860 px.

Views

Hosts

The host list is the main view. It has a filter box, tag chips, a sort selector and a list/grid toggle.

  • Sort — Smart rank (frequency and recency), Last used, Name or Address.
  • List view — columns Name, Address, Tags and Last used. Address is shown as user@host, with :port when it is not 22. Hosts with Kerberos or a jump host carry a badge.
  • Grid view — the same information as cards, mounted progressively while you scroll. The list is windowed, so large host lists stay responsive.
  • Ping — checks reachability for every host, then reports n/m up and shows a latency next to each reachable host.
  • Batch run — opens the batch-execution modal (see below).
  • New host — opens the host form.

Row actions (also on cards): Connect, Copy SSH command, Edit, Duplicate and Delete. Duplicate creates <name> (Copy), flashes the new row and opens it for editing. Delete asks for confirmation and shows user@host:port.

Keyboard: ↑/↓ or j/k move the selection, Enter connects, Ctrl/Cmd + E edits the selected host. With no hosts yet, the empty state offers Import ~/.ssh/config and New host.

The host form (New host / Edit host) collects Name, Host, Port, User and an identity file, plus a Kerberos switch, comma-separated tags and an Advanced section for a jump host and jump user. Changes apply to new sessions; saved hosts are written to the active profile.

Terminals

Terminals open as tabs over xterm.js sessions backed by Rust PTY processes. The empty state lists your recent hosts to connect with, and the + button opens a host launcher with a Connect to host… filter.

Each tab shows a status dot (Connected, Connecting, Failed to connect, Disconnected) and a close button; middle-clicking a tab closes it. With an active session:

  • Find (Ctrl/Cmd + F) opens a per-tab search bar over the scrollback (Enter next match, Shift + Enter previous).
  • Font size buttons adjust the terminal text; Ctrl/Cmd + mouse wheel zooms, and Ctrl/Cmd + +/-/0 adjust or reset it when the terminal is not focused. The value is remembered in settings.
  • More offers Open in new window, Run in background, Export scrollback (to a .txt file), Clear screen, Manage sessions…, and Close all sessions.

Sessions can live in three places: a tab, a pop-out window, or the background. Dragging a tab away from the tab strip opens it in its own window; dragging a pop-out or background session back onto the tab bar docks or reattaches it with its buffered output. The Running sessions manager lists everything outside the tab bar and can focus, dock, reattach or terminate each session, or terminate all of them.

Closing the window while sessions are open shows a confirmation with Cancel, Minimize to tray (keeps the sessions running) and Disconnect and quit. With no open sessions the window simply hides to the tray.

Terminal copy/paste uses the usual key combinations (Ctrl/Cmd + C for a selection, Ctrl/Shift + C, Ctrl/Cmd + V, Ctrl/Shift + V or Shift + Insert). OSC 52 clipboard writes from remote hosts are honoured; reads are refused.

Files

The Files view browses a remote directory over SFTP. Pick a host and press Connect; the header then shows user@host and the button becomes Disconnect.

  • Breadcrumbs navigate the tree; a go-to button turns the path into an editable field (Enter loads, Esc cancels).
  • Parent directory and Refresh buttons sit in the toolbar, next to quick bookmarks for /, ~, /var/www, /etc, /var/log and /tmp.
  • The filter matches a file name or its permissions, and a Hidden files toggle shows or hides dotfiles (with a count of what is hidden).
  • List and grid layouts are available; each entry shows its type icon, size, permissions and modification time, and its remote path can be copied.

The browser is read-only: it lists directories and reports errors, but it does not upload or download files.

Tunnels

The Tunnels view manages port-forwarding rules: local (-L), remote (-R) and SOCKS5 dynamic (-D). You can start from a preset or add a rule with a host, type, local port, remote host and remote port; the form validates the values and previews the matching ssh command. Each rule can be enabled or disabled, edited, deleted, or have its local address copied.

Rules are configuration held by the view: no tunnel process is started, and rules are not persisted across restarts. The section is hidden when Tunnels is turned off under Settings → Features.

Keys

The Keys view lists the identity files in ~/.ssh, paired from each *.pub file:

  • columns Key (name, comment and shortened path), Type, Fingerprint (copyable) and Permissions;
  • a green permissions badge means the private key is only readable by you; a red badge means group/other bits are set, and an alert at the top gives a copyable chmod 600 command for every affected key;
  • Generate key creates a new pair in ~/.ssh (Ed25519, or RSA for older servers) with an empty passphrase;
  • Deploy copies a public key to a chosen host’s authorized_keys.

Security audit

The audit view runs on open and scores the local setup out of 100 with a grade, plus counts for Critical, Warnings and Info findings. A severity filter narrows the list. Each finding shows its description and a copyable remediation command.

The checks cover StrictHostKeyChecking being disabled, overly permissive permissions on the config directory and database file, private keys readable by other users, connections with no identity key and no Kerberos, and connections unused for more than 90 days. Fix permissions tightens the config directory, database file, ~/.ssh and key files that are group/other-accessible, then re-runs the audit. Re-scan re-runs it manually.

History

Session history is grouped by day (Today, Yesterday, then dates) and shows Host, Started (relative, with an absolute tooltip in the configured timezone), Duration, Status and Exit code. Rows are badged Succeeded, Failed or Running; the status chips (All, Succeeded, Failed) and the search box filter the table. Export CSV downloads every recorded session.

Settings

Settings has its own sidebar of sections. Every control saves immediately; there is no Save button.

  • Profiles & workspace — switch or manage profiles, choose how hosts are ranked (usage-ranked Bayesian scoring or fuzzy matching), set the OpenSSH config file used for imports, import hosts, encrypt/export/restore backups, and see the config, profile and database paths.

  • SSH agent — start the agent on launch, point at a custom agent socket, and set the default user and port used by hosts that don’t define their own.

  • Kerberos — monitor ticket expiry and set the warning threshold in minutes.

  • Terminal — font family, size and line height, cursor style and blinking, scrollback size, copy-on-select, snippet confirmation, and whether the file browser lists dotfiles.

  • History — record session history, cap the number of stored entries, and set the backend log level.

  • Appearance — pick a theme and the timezone used for dates in history, logs and audit. Themes: Graphite, Midnight, OLED black and Slate.

  • Features — turn the SFTP file browser and the Tunnels section on or off; the sidebar updates immediately.

  • Updates — shows the current version and handles updates for the way the app was installed.

    In-app updates apply to the AppImage and to the bayesian-ssh-desktop .deb/.rpm from GitHub releases: Check for updates reports a newer signed build, and Install and restart downloads, verifies and installs it. Open terminal sessions are closed and the app restarts. Snap installs are updated by the Snap Store (the section shows sudo snap refresh bayesian-ssh), and other installs (raw binaries from install.sh or release tarballs, the unified CLI+GUI packages, and source builds) are updated the way they were installed.

Modals and dialogs

  • Command palette (Ctrl/Cmd + K) — searches hosts and commands. It groups results into Hosts, Actions, Navigation and Themes; with an empty query it shows the first hosts plus the actions and navigation entries. Host entries connect on selection; actions cover New host, Run command on hosts, Ping all hosts, Manage sessions and Fix key permissions; navigation entries jump to a view; theme entries switch the theme.
  • Batch run — runs one command on several hosts in parallel. Pick hosts (with filter, tag chips and All/None/Invert/Prod/Non-prod shortcuts), enter a command, optionally save it as a template, choose dry run and a timeout, then preview or run. Results list each host with its exit code, duration and stdout/stderr, and can be exported as Markdown, CSV, JSON or plain text. A History tab keeps recent runs (for inspection or repeat) and a Templates tab manages built-in and custom commands. Ctrl/Cmd + Enter runs, Ctrl/Cmd + Shift + D toggles dry run.
  • Snippets — saved commands with a title, category, description and command. Search and category chips narrow the list; snippets can be created, edited, deleted, copied, or sent to the active terminal tab (Run in terminal, which asks for confirmation unless that is disabled in settings; with no active terminal the command is copied instead).
  • Profiles — list profiles, create a new one, and delete any profile that is neither active nor the default. Switching profiles happens from the sidebar profile menu, which also opens this dialog through Manage profiles….
  • SSH agent — shows the agent socket and the loaded keys with their fingerprints, and adds a private key file to the agent.
  • Kerberos — shows ticket status (principal, realm, remaining lifetime, renew time, cache and config paths) and acquires a ticket with kinit, or renews the existing one (kinit -R, with an optional password). Ticket options cover forwardable, proxiable, lifetime and renewal lifetime.
  • About — version, active profile, config and database paths, license, and links to the source code, release notes and issue tracker.
  • Confirmations — host deletion, all-session close, and quitting with open sessions all use in-app dialogs instead of native prompts.

Keyboard shortcuts

Press ?, F1 or Ctrl/Cmd + / to open the shortcut sheet. Single-key shortcuts work when no text field is focused.

ActionKeys
Command paletteCtrl/Cmd + K
Keyboard shortcuts?, F1, Ctrl/Cmd + /
Close a dialog or clear the filterEsc
Hosts / Terminals / Keys / Audit / History / Settings1 / 2 / 3 / 4 / 5 / 6
Focus the host filter/
New hostN, Ctrl/Cmd + N
Move host selection↑/↓, j/k
Connect to the selected hostEnter
Edit the selected hostCtrl/Cmd + E
Terminal font size (when the terminal is not focused)Ctrl/Cmd + + / - / 0

Import & Export

Import from SSH Config

Import existing connections from your ~/.ssh/config file:

# Import from default location
bayesian-ssh import

# Import from a specific file
bayesian-ssh import --file /path/to/ssh/config

This reads your SSH config and creates Bayesian SSH connections for each host entry, preserving hostname, user, port, identity file, and proxy settings.

Export Connections

Export your connections for sharing or backup:

bayesian-ssh export

Enterprise Environments

Enterprise Bastion Configuration

Default Configuration

# Set up default enterprise configuration
bayesian-ssh config \
  --default-user currentuser \
  --default-bastion bastion-server.company.priv \
  --use-kerberos

# Add production servers with tags (will use default bastion)
bayesian-ssh add "Web Prod" web-prod.company.com --tags production,web
bayesian-ssh add "DB Prod" db-prod.company.com --tags production,database
bayesian-ssh add "App Prod" app-prod.company.com --tags production,application

# Quick connection to production
bayesian-ssh connect "Web Prod"

Bastion Management Strategies

1. Default Bastion for Internal Servers

# These will automatically use your default bastion
bayesian-ssh add "Internal Web" internal-web.company.com --tags internal,production
bayesian-ssh add "Internal DB" internal-db.company.com --tags internal,production

2. Direct Connections for Cloud Instances

# Force direct connection, bypassing default bastion
bayesian-ssh add "EC2 Web" ec2-web.company.com \
  --user ubuntu \
  --kerberos false \
  --key ~/.ssh/ec2-key.pem \
  --no-bastion \
  --tags ec2,production,web

3. Custom Bastion for Specific Networks

# Override default bastion with specific one
bayesian-ssh add "DMZ Server" dmz.company.com \
  --bastion dmz-bastion.company.com \
  --tags dmz,production

4. Mixed Environment Setup

# Internal servers (use default bastion)
bayesian-ssh add "App Server" app.company.com --tags internal,production

# Cloud servers (direct connection)
bayesian-ssh add "Cloud App" cloud.company.com --no-bastion --tags cloud,production

# Special network (custom bastion)
bayesian-ssh add "Special Server" special.company.com \
  --bastion special-bastion.company.com \
  --tags special,production

Multi-Environment Management

# Development environment
bayesian-ssh add "Web Dev" web-dev.company.com \
  --user dev-user \
  --bastion dev-bastion.company.com \
  --tags development,web

# Staging environment
bayesian-ssh add "Web Staging" web-staging.company.com \
  --user staging-user \
  --bastion staging-bastion.company.com \
  --tags staging,web

# Production environment
bayesian-ssh add "Web Prod" web-prod.company.com \
  --user prod-user \
  --bastion prod-bastion.company.com \
  --tags production,web

# List by environment
bayesian-ssh list --tag production
bayesian-ssh list --tag development

Network Segmentation

DMZ Servers

# Web servers in DMZ
bayesian-ssh add "DMZ Web" dmz-web.company.com \
  --user web-user \
  --kerberos false \
  --tags dmz,web,production

# API servers in DMZ
bayesian-ssh add "DMZ API" dmz-api.company.com \
  --user api-user \
  --kerberos false \
  --tags dmz,api,production

Internal Network

# Internal application servers
bayesian-ssh add "Internal App" internal-app.company.com \
  --user app-user \
  --bastion internal-bastion.company.com \
  --tags internal,application,production

# Database servers
bayesian-ssh add "Internal DB" internal-db.company.com \
  --user db-user \
  --bastion internal-bastion.company.com \
  --tags internal,database,production

High Availability and Load Balancing

Load Balancer Backends

# Primary load balancer
bayesian-ssh add "LB Primary" lb-primary.company.com \
  --user currentuser \
  --tags loadbalancer,primary,production

# Secondary load balancer
bayesian-ssh add "LB Secondary" lb-secondary.company.com \
  --user currentuser \
  --tags loadbalancer,secondary,production

# Backend servers
bayesian-ssh add "Backend 1" backend-1.company.com \
  --user app-user \
  --bastion lb-primary.company.com \
  --tags backend,production,web

bayesian-ssh add "Backend 2" backend-2.company.com \
  --user app-user \
  --bastion lb-secondary.company.com \
  --tags backend,production,web

Backup and Recovery

# Primary backup server
bayesian-ssh add "Backup Primary" backup-primary.company.com \
  --user backup \
  --kerberos true \
  --tags backup,primary,production

# Secondary backup server
bayesian-ssh add "Backup Secondary" backup-secondary.company.com \
  --user backup \
  --kerberos true \
  --tags backup,secondary,production

# Disaster recovery server
bayesian-ssh add "DR Server" dr.company.com \
  --user dr-user \
  --kerberos true \
  --tags disaster-recovery,production

Cloud Infrastructure

AWS EC2

Direct EC2 Instances

# Web server instance
bayesian-ssh add "Web EC2" ec2-web.company.com \
  --user ubuntu \
  --kerberos false \
  --key ~/.ssh/ec2-web-key.pem \
  --tags ec2,production,web

# Database instance
bayesian-ssh add "DB EC2" ec2-db.company.com \
  --user ec2-user \
  --kerberos false \
  --key ~/.ssh/ec2-db-key.pem \
  --tags ec2,production,database

# Application instance
bayesian-ssh add "App EC2" ec2-app.company.com \
  --user currentuser \
  --kerberos false \
  --key ~/.ssh/ec2-app-key.pem \
  --tags ec2,production,application

EC2 via Bastion (Private Subnets)

# Private subnet instances
bayesian-ssh add "Private Web" private-web.company.com \
  --user ubuntu \
  --kerberos false \
  --key ~/.ssh/private-key.pem \
  --bastion bastion.company.com \
  --tags ec2,private,production,web

# VPC instances
bayesian-ssh add "VPC App" vpc-app.company.com \
  --user ec2-user \
  --kerberos false \
  --key ~/.ssh/vpc-key.pem \
  --bastion vpc-bastion.company.com \
  --tags ec2,vpc,production,application

Multi-Cloud Setup (AWS + Azure + GCP)

# AWS instances (direct connection)
bayesian-ssh add "AWS Web" aws-web.company.com \
  --user ubuntu \
  --kerberos false \
  --key ~/.ssh/aws-key.pem \
  --no-bastion \
  --tags aws,production,web

# Azure VMs (direct connection)
bayesian-ssh add "Azure DB" azure-db.company.com \
  --user azureuser \
  --kerberos false \
  --key ~/.ssh/azure-key.pem \
  --no-bastion \
  --tags azure,production,database

# GCP instances (direct connection)
bayesian-ssh add "GCP App" gcp-app.company.com \
  --user gcp-user \
  --kerberos false \
  --key ~/.ssh/gcp-key.pem \
  --no-bastion \
  --tags gcp,production,application

Kubernetes and Container Environments

Pod Access

# Access to Kubernetes pods
bayesian-ssh add "K8s Web Pod" web-pod.namespace.svc.cluster.local \
  --user root \
  --kerberos false \
  --tags kubernetes,pod,web

# Service access
bayesian-ssh add "K8s Service" web-service.namespace.svc.cluster.local \
  --user currentuser \
  --kerberos false \
  --tags kubernetes,service,web

Docker Containers

# Development container
bayesian-ssh add "Dev Container" dev-container.company.com \
  --user developer \
  --port 2222 \
  --kerberos false \
  --tags docker,development

# Production container
bayesian-ssh add "Prod Container" prod-container.company.com \
  --user operator \
  --port 2222 \
  --kerberos false \
  --tags docker,production

Development Workflows

Feature Branch Development

# Feature development server
bayesian-ssh add "Feature Dev" feature-dev.company.com \
  --user developer \
  --bastion dev-bastion.company.com \
  --tags development,feature

# Integration testing
bayesian-ssh add "Integration Test" integration.company.com \
  --user tester \
  --bastion test-bastion.company.com \
  --tags testing,integration

# Staging for QA
bayesian-ssh add "QA Staging" qa-staging.company.com \
  --user qa-user \
  --bastion qa-bastion.company.com \
  --tags testing,staging,qa

CI/CD Pipeline Access

# Jenkins server
bayesian-ssh add "Jenkins" jenkins.company.com \
  --user jenkins \
  --kerberos false \
  --tags ci,jenkins,production

# GitLab server
bayesian-ssh add "GitLab" gitlab.company.com \
  --user git \
  --kerberos false \
  --tags ci,gitlab,production

# Artifactory server
bayesian-ssh add "Artifactory" artifactory.company.com \
  --user artifact \
  --kerberos false \
  --tags ci,artifactory,production

Environment Workflow

Use tags to organize connections by environment and quickly switch contexts:

# Add servers for each environment
bayesian-ssh add "Web Dev" web-dev.company.com --tags development,web
bayesian-ssh add "Web Staging" web-staging.company.com --tags staging,web
bayesian-ssh add "Web Prod" web-prod.company.com --tags production,web

# List all development servers
bayesian-ssh list --tag development

# List all staging servers
bayesian-ssh list --tag staging

# Quickly connect to any environment
bayesian-ssh connect "Web Dev"
bayesian-ssh connect "Web Staging"
bayesian-ssh connect "Web Prod"

Troubleshooting Connections

# Test basic connectivity
bayesian-ssh connect "Test Server" --debug

# Test with specific user
bayesian-ssh connect "Test Server" --user test-user

# Test with specific key
bayesian-ssh connect "Test Server" --key ~/.ssh/test-key

Security & Compliance

Kerberos Authentication

Bayesian SSH integrates with Kerberos for enterprise authentication:

# Enable Kerberos by default
bayesian-ssh config --use-kerberos

# Per-connection Kerberos
bayesian-ssh add "Secure Server" secure.company.com --kerberos true

Kerberos Ticket Management

# Check current ticket status
klist

# Create new forwardable ticket
kinit -f -A

# Verify ticket creation
klist -s

Bayesian SSH will automatically verify tickets before connecting and create new ones when needed.

Audit and Monitoring

# Audit servers
bayesian-ssh add "Audit Server" audit.company.com \
  --user auditor \
  --kerberos true \
  --tags audit,compliance,production

# Monitoring servers
bayesian-ssh add "Monitoring" monitoring.company.com \
  --user monitor \
  --kerberos true \
  --tags monitoring,production

# Log servers
bayesian-ssh add "Log Server" logs.company.com \
  --user logger \
  --kerberos true \
  --tags logging,production

Compliance Environments

SOX Compliance

bayesian-ssh add "SOX Server" sox.company.com \
  --user sox-user \
  --kerberos true \
  --tags sox,compliance,production

PCI Compliance

bayesian-ssh add "PCI Server" pci.company.com \
  --user pci-user \
  --kerberos true \
  --tags pci,compliance,production

HIPAA Compliance

bayesian-ssh add "HIPAA Server" hipaa.company.com \
  --user hipaa-user \
  --kerberos true \
  --tags hipaa,compliance,production

Security Features

Authentication

  • Kerberos integration - Enterprise authentication with automatic ticket management
  • SSH key management - Secure key handling per connection
  • Bastion support - Secure jump host connections

Access Control

  • Audit logging - Complete connection history with timestamps and outcomes
  • Tag-based organization - Categorize servers by compliance requirements
  • Session tracking - Monitor active sessions with PID-level tracking

Best Practices

  1. Always use Kerberos for internal/enterprise servers
  2. Use separate SSH keys per environment (dev, staging, production)
  3. Route internal servers through bastion hosts
  4. Review session history regularly with bayesian-ssh history
  5. Use --force carefully on destructive operations
  6. Backup your database regularly with bayesian-ssh backup

Performance Optimization

Connection Pooling

# High-performance servers
bayesian-ssh add "Perf Server 1" perf-1.company.com \
  --user perf-user \
  --kerberos false \
  --tags performance,production

bayesian-ssh add "Perf Server 2" perf-2.company.com \
  --user perf-user \
  --kerberos false \
  --tags performance,production

Load Distribution

# Round-robin load distribution
bayesian-ssh add "Load 1" load-1.company.com \
  --user load-user \
  --kerberos false \
  --tags load,production

bayesian-ssh add "Load 2" load-2.company.com \
  --user load-user \
  --kerberos false \
  --tags load,production

bayesian-ssh add "Load 3" load-3.company.com \
  --user load-user \
  --kerberos false \
  --tags load,production

Database Optimization

Bayesian SSH uses SQLite with several optimizations:

  • Indexed queries - Fast lookups by name and tags
  • Connection pooling - Efficient database connection reuse
  • Batch operations - Grouped database operations for bulk changes

Memory Management

The Rust implementation provides:

  • Zero-copy operations - Minimized memory allocations
  • Efficient serialization - Fast JSON operations via serde
  • Resource cleanup - Proper cleanup of file descriptors and processes

Release Build Optimizations

The release binary is built with:

  • opt-level=3 - Maximum optimization
  • Thin LTO - Link-time optimization
  • Single codegen unit - Better whole-program optimization
  • Stripped symbols - Smaller binary size
  • panic=abort - Reduced binary size

Monitoring Connection Performance

# Check latency
bayesian-ssh ping "Server Name"

# View connection statistics
bayesian-ssh stats

# Review session history for patterns
bayesian-ssh history --days 30

Technical Architecture

Overview

Bayesian SSH is built with a modular architecture that separates concerns and provides a clean, maintainable codebase.

Core Components

1. CLI Layer (src/cli/)

  • Command parsing: Uses clap for robust command-line argument handling
  • Command modules: Each command is implemented in its own module
  • Shell completions: Automatic generation for bash, zsh, fish, and more
  • Shared utilities: Common CLI patterns extracted into src/cli/utils.rs

2. Configuration Management (src/config/)

  • AppConfig: Central configuration structure
  • File-based config: JSON configuration stored in ~/.config/bayesian-ssh/
  • Environment overrides: Support for environment variable configuration

3. Data Models (src/models/)

  • Connection: Represents SSH connection configuration
  • Session: Tracks active SSH sessions
  • Serialization: Full serde support for JSON operations

4. Database Layer (src/database/)

  • SQLite integration: Using rusqlite for data persistence
  • Modular design: Split into submodules - connection, alias, session, search
  • Migration support: Schema versioning and updates

5. Services (src/services/)

  • SSH Service: Core SSH connection logic
  • Kerberos integration: Automatic ticket management
  • Process management: Safe process spawning and monitoring

Database Schema

Connections Table

CREATE TABLE connections (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL UNIQUE,
    host TEXT NOT NULL,
    user TEXT NOT NULL,
    port INTEGER NOT NULL,
    bastion TEXT,
    bastion_user TEXT,
    use_kerberos BOOLEAN NOT NULL,
    key_path TEXT,
    created_at TEXT NOT NULL,
    last_used TEXT,
    tags TEXT NOT NULL
);

Sessions Table

CREATE TABLE sessions (
    id TEXT PRIMARY KEY,
    connection_id TEXT NOT NULL,
    started_at TEXT NOT NULL,
    ended_at TEXT,
    status TEXT NOT NULL,
    pid INTEGER,
    exit_code INTEGER
);

Connection Workflow

1. Parse command line arguments
2. Load application configuration
3. Search for connection in database (Bayesian-ranked or fuzzy)
4. Check aliases if no direct match
5. Verify Kerberos ticket (if enabled)
6. Automatically create ticket if needed
7. Build SSH command with proper flags
8. Execute SSH process
9. Monitor session status
10. Update database with results
11. Handle cleanup on exit

Error Handling

Error Types

  • Configuration errors: Invalid settings or missing files
  • Database errors: Connection issues or schema problems
  • SSH errors: Connection failures or authentication issues
  • Kerberos errors: Ticket creation or renewal failures

Error Recovery

  • Graceful degradation: Fallback to basic SSH if features fail
  • Automatic retry: Retry failed operations with exponential backoff
  • User feedback: Clear error messages with suggested solutions

Performance Considerations

Database Optimization

  • Indexed queries for fast lookups by name and tags
  • Efficient connection reuse
  • Batch operations for grouped database operations

Memory Management

  • Zero-copy operations to minimize memory allocations
  • Efficient serialization with serde
  • Proper cleanup of file descriptors and processes

Security Features

Kerberos Integration

  • Ticket verification before use
  • Automatic renewal when needed
  • Forwardable ticket support

SSH Security

  • Secure handling of SSH keys
  • Bastion host support for jump connections
  • Safe process spawning and monitoring

Testing Strategy

Unit Tests

  • Model validation and data structure integrity
  • Service logic tested in isolation
  • Error condition and recovery testing

Integration Tests

  • Full database workflow testing
  • SSH connection testing
  • Configuration file handling

Future Architecture

Plugin System

  • Extension points with hook system for custom functionality
  • Stable plugin API for third-party extensions
  • Runtime plugin loading and management

API Layer

  • REST API for remote management
  • WebSocket support for real-time session monitoring
  • Secure API access control

Distribution

How Bayesian SSH reaches each channel, which repository secret or variable turns it on, and how to set that channel up for the first time.

Every channel is optional and independent. A tag push with none of the secrets below set behaves exactly like the first release: it builds both architectures, publishes the GitHub release, SHA256SUMS and its signature, and skips the rest — each skipped job leaves a ::notice:: in the run summary saying which secret is missing. Nothing here requires a secret to exist, and no secret is ever echoed to the log.

Channels at a glance

ChannelJobTurns on withPublishes
GitHub releasebuild, packages, releaseGITHUB_TOKEN (built in)Binaries, bundles, unified .deb/.rpm, SHA256SUMS, provenance attestations, and latest.json when updater keys are set
In-app updatergate, buildTAURI_SIGNING_PRIVATE_KEY secret and BAYESIAN_SSH_UPDATER_PUBLIC_KEY variableSigned updater artifacts and latest.json
Unified packagespackagesnonebayesian-ssh_<version>_amd64.deb and .rpm combining the CLI and the desktop GUI
Snap StoresnapSNAPCRAFT_STORE_CREDENTIALSAn amd64 + arm64 snap on the Snap Store (stable, or candidate for prereleases)
Flatpak / Flathub— (manual)noneA PR to Flathub from the manifest in the repo

A tag with a pre-release suffix (v2.6.0-rc.1) is published as a GitHub pre-release, and its snap goes to the candidate channel instead of stable. Everything else is identical.

All Linux builds run on ubuntu-24.04 / ubuntu-24.04-arm pinned runners: the snap base (core24) must ship the glibc those binaries were linked against.

Release signing

Every file in the release is covered twice:

  • Build provenance — actions/attest-build-provenance@v2 records a Sigstore attestation for each asset (id-token: write, attestations: write). It proves the file was built by this workflow at this ref.
  • Keyless blob signing — cosign sign-blob signs SHA256SUMS and stores the bundle as SHA256SUMS.sigstore.json. The signing identity is the workflow at the tag, recorded in the Rekor transparency log, so there is no key to store or rotate.

Verify a download before installing it:

# 1. The checksum manifest itself is signed by this repository's release workflow.
cosign verify-blob --bundle SHA256SUMS.sigstore.json \
  --certificate-identity-regexp '^https://github\.com/abdoufermat5/bayesian-ssh/\.github/workflows/release\.yml@refs/(tags/v.+|heads/main)$' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS

# 2. Every file you downloaded matches the signed manifest.
sha256sum --ignore-missing -c SHA256SUMS

# 3. Optional: check a single file's build provenance.
gh attestation verify bayesian-ssh-linux-x86_64.tar.gz --repo abdoufermat5/bayesian-ssh

install.sh runs the cosign verify-blob check automatically when cosign is installed, and always verifies SHA256SUMS with sha256sum -c.

In-app updater

The desktop app checks for updates under Settings → Updates and installs them in place. Only the formats the updater can replace in place are updatable: the .AppImage and the bayesian-ssh-desktop .deb/.rpm from the GitHub release (the Tauri bundler stamps the bundle type into the binary). Everything else reports who owns updates and never even registers the updater plugin:

  • the snap — read-only squashfs, the Snap Store delivers updates; update with sudo snap refresh bayesian-ssh;
  • raw binaries (install.sh, release tarballs), the unified bayesian-ssh .deb/.rpm, and source builds — updating would overwrite them with an AppImage or install a second, conflicting package, so the app says to update the way it was installed.

Installing an update closes open terminal sessions and restarts the app. The downloaded artifact is verified against the minisign public key embedded at compile time; there is no unsigned or default-key path.

Keys

Generate the updater keypair once:

npx tauri signer generate -w ~/.tauri/bayesian-ssh-updater.key

Keep the private key offline (the maintainer’s copy is never in the repo) and configure the two sides:

  • secret TAURI_SIGNING_PRIVATE_KEY — the private key content;
  • secret TAURI_SIGNING_PRIVATE_KEY_PASSWORD — its passphrase;
  • variable BAYESIAN_SSH_UPDATER_PUBLIC_KEY — the public key content (a single line).

The gate job enforces that the signing key and the public key are set together or left empty together: a signing key without a matching public key would publish artifacts no installed app could ever verify, so the release fails early instead. The public key is only written into the build when both sides are present; otherwise the release is published without updater artifacts or latest.json and a ::notice:: says so.

Rotating the key

  1. Generate a new keypair with npx tauri signer generate.
  2. Replace the TAURI_SIGNING_PRIVATE_KEY / TAURI_SIGNING_PRIVATE_KEY_PASSWORD secrets and the BAYESIAN_SSH_UPDATER_PUBLIC_KEY variable with the new values.

Old installs only trust the key compiled into them, so rotation strands them: they keep checking for updates with the old public key, the new signatures do not verify, and they can never install a release signed with the new key. They must reinstall a build that embeds the new key before in-app updates work again.

Snap Store

Users install and wire up the snap with:

sudo snap install bayesian-ssh
sudo snap connect bayesian-ssh:ssh-keys   # read-only ~/.ssh and /etc/ssh
sudo snap alias bayesian-ssh bssh         # optional short alias

The snap is built for amd64 and arm64 on a core24 base under strict confinement. It ships two apps: bayesian-ssh (the CLI) and bayesian-ssh.gui (the desktop app, also in the app menu).

Confinement limits:

  • ~/.ssh is mounted read-only, so known_hosts is not updated and key generate cannot write keys into ~/.ssh.
  • The app’s config and database live under ~/snap/bayesian-ssh/current/.config/bayesian-ssh and are not shared with a native install or with install.sh.
  • ssh, kinit and klist are the snap’s own copies.
  • Kerberos stays optional: the sidebar indicator and expiry warnings only appear once a connection uses Kerberos. Inside the snap:
    • tickets obtained from the app (Kerberos dialog) or with bayesian-ssh-launched kinit live in the snap’s private /tmp and work with no extra step;
    • to reuse tickets created on the host, run sudo snap connect bayesian-ssh:kerberos-tickets. This only works for file caches with KRB5CCNAME=FILE:/tmp/krb5cc_* set explicitly on the host (snapd rewrites it to the host path); KCM/KEYRING caches (SSSD, Fedora/RHEL defaults) are not reachable from a strict snap — use a native install (install.sh, .deb, .rpm or AppImage) in that case.
  • The in-app updater is disabled in the snap — the Snap Store owns updates.

Publishing needs a store credential. One-time setup:

sudo snap install snapcraft --classic   # if snapcraft is not installed yet
snapcraft login
snapcraft register bayesian-ssh         # also reserves the name (global)

snapcraft export-login --snaps=bayesian-ssh \
  --acls package_access,package_push,package_update,package_release - \
  | gh secret set SNAPCRAFT_STORE_CREDENTIALS

release.yml calls .github/workflows/snap.yml after publishing every release. To package an already-released version (or rebuild a tag), dispatch it directly:

gh workflow run snap.yml -f tag=v2.6.0

The snap repackages the published release assets rather than rebuilding: scripts/snap-stage.sh downloads the CLI tarball and the desktop .deb, checks them against the release’s SHA256SUMS, and renders the __VERSION__ token in packaging/snap/snapcraft.yaml. Build it locally the same way:

scripts/snap-stage.sh v2.6.0 amd64        # or: make snap-build TAG=v2.6.0
cd target/snap && snapcraft pack

Without SNAPCRAFT_STORE_CREDENTIALS the snap is still built and kept as a workflow artifact; the publish step leaves a ::notice:: instead.

Store listing

On every stable release the snap workflow runs snapcraft upload-metadata, which pushes the icon, summary and description from the snap. It does not touch the links or screenshots; those are kept on the listing (https://snapcraft.io/bayesian-ssh/listing) and survive each sync:

  • links: website https://abdoufermat5.github.io/bayesian-ssh/, source https://github.com/abdoufermat5/bayesian-ssh, issues/contact https://github.com/abdoufermat5/bayesian-ssh/issues;
  • screenshots: 1280×800 captures of the desktop app (Hosts, Terminals, command palette, Files, Audit), taken against the e2e mocked backend so they hold only demo data.

Change either on the dashboard, and keep the snapcraft.yaml link fields in step.

Flatpak (Flathub)

Flatpak is the one channel with no automation secret: Flathub builds from a manifest in its own repository, so each release is a pull request. The manifest lives at packaging/flatpak/com.bayesianssh.App.yml, with the desktop entry in com.bayesianssh.App.desktop and the AppStream metadata in com.bayesianssh.App.metainfo.xml.

Per release, in the manifest: point the .deb and desktop-entry URLs at the new tag and update their sha256 values, then open a PR against the Flathub app repository. Test locally with flatpak-builder --force-clean --ccache --install-deps-from=flathub target/flatpak-build packaging/flatpak/com.bayesianssh.App.yml (or make flatpak-build).

Cutting a release

The gate job refuses a tag whose version does not match the app, so bump all four version files first:

  • crates/cli/Cargo.toml
  • crates/gui/Cargo.toml
  • crates/gui/tauri.conf.json
  • desktop/package.json

Then refresh the lockfile and move the changelog:

cargo update -w                 # refresh crates/* version entries in Cargo.lock
# Roll the CHANGELOG's [Unreleased] section into "## [X.Y.Z] - <date>".

git add -A
git commit -m "Release vX.Y.Z"
git tag vX.Y.Z
git push --follow-tags

The tag runs the gate checks, builds both architectures, folds the unified .deb/.rpm into the release, signs and publishes everything, and then runs whichever optional channels are configured. Keep the version numbers in the CHANGELOG, README and docs examples in step with the tag.

Troubleshooting

Quick Diagnostic

Run bssh doctor before changing connection settings. It checks the active environment, configuration, SQLite database, SSH client, SSH config, ssh-agent socket, and Kerberos helper tools.

bssh doctor
bssh --env staging doctor

Use any FAILED entry as the first fix target. WARN entries are non-blocking, but they explain why features such as ssh-agent, SSH config import, or Kerberos may not work in the current environment.

Kerberos Authentication Problems

No Valid Kerberos Ticket Found

# Check current ticket status
klist

# Create new forwardable ticket
kinit -f -A

# Verify ticket creation
klist -s

Symptoms:

  • Error: “No valid Kerberos ticket found”
  • SSH connection fails with authentication error
  • klist shows no tickets or expired tickets

Solutions:

  1. Check ticket status: klist -s
  2. Create new ticket: kinit -f -A
  3. Verify realm configuration: Check /etc/krb5.conf
  4. Check DNS resolution: Ensure realm DNS is working

Ticket Expired or Invalid

# Check ticket expiration
klist

# Renew existing ticket
kinit -R

# Create new ticket if renewal fails
kinit -f -A

Symptoms:

  • Error: “Ticket expired”
  • Authentication fails after some time
  • klist shows expired tickets

Solutions:

  1. Automatic renewal: kinit -R
  2. Manual renewal: kinit -f -A
  3. Check clock sync: Ensure system time is correct
  4. Verify KDC: Check KDC server availability

SSH Connection Issues

Connection Refused

# Test basic connectivity
telnet server.company.com 22

# Check SSH service status
ssh -v server.company.com

# Verify firewall rules
sudo iptables -L

Symptoms:

  • Error: “Connection refused”
  • SSH connection times out
  • Port 22 unreachable

Solutions:

  1. Check SSH service: systemctl status sshd
  2. Verify port: Ensure SSH is listening on correct port
  3. Check firewall: Verify firewall allows SSH traffic
  4. Network connectivity: Test basic network reachability

Authentication Failed

# Test with verbose output
ssh -v user@server.company.com

# Check key permissions
ls -la ~/.ssh/
chmod 600 ~/.ssh/id_rsa

# Test specific key
ssh -i ~/.ssh/id_rsa user@server.company.com

Symptoms:

  • Error: “Permission denied”
  • Authentication fails with valid credentials
  • Key-based authentication fails

Solutions:

  1. Check key permissions: chmod 600 ~/.ssh/id_rsa
  2. Verify key format: Ensure key is in correct format
  3. Check server configuration: Verify authorized_keys setup
  4. Test manually: Use standard SSH command to test

Bastion Host Problems

Bastion Connection Fails

# Test bastion connectivity
ssh -t -A -K user@bastion.company.com

# Test with specific port
ssh -p 2222 user@bastion.company.com

# Verify bastion configuration
bayesian-ssh show "Server Name"

# Force direct connection (bypass bastion)
bayesian-ssh connect "Target Server" --no-bastion

# Check if connection is using default bastion
bayesian-ssh show "Target Server"

Symptoms:

  • Error: “Bastion connection failed”
  • Cannot reach target through bastion
  • Bastion authentication fails

Solutions:

  1. Test bastion directly: ssh user@bastion.company.com
  2. Check bastion port: Verify correct port (default: 22)
  3. Verify user permissions: Ensure bastion user has access
  4. Check network path: Verify bastion is reachable

Target Host Unreachable via Bastion

# Test from bastion to target
ssh -t -A -K user@bastion.company.com "ssh user@target.company.com"

# Check routing on bastion
ssh user@bastion.company.com "route -n"

# Verify target accessibility
ssh user@bastion.company.com "ping target.company.com"

Symptoms:

  • Bastion connects but target is unreachable
  • Error: “No route to host”
  • Connection times out to target

Solutions:

  1. Check bastion routing: Verify bastion can reach target
  2. Verify target firewall: Ensure target allows bastion traffic
  3. Check network segmentation: Verify network policies
  4. Test manually: Connect to bastion and test target manually

Unexpected Bastion Usage

# Check if connection is using default bastion
bayesian-ssh show "Server Name"

# Force direct connection
bayesian-ssh connect "Server Name" --no-bastion

# Re-add connection with explicit no-bastion flag
bayesian-ssh remove "Server Name"
bayesian-ssh add "Server Name" hostname.com --no-bastion --tags production

Symptoms:

  • Connection unexpectedly goes through bastion
  • Want direct connection but getting bastion routing
  • Default bastion being used when not intended

Solutions:

  1. Use --no-bastion flag: Explicitly disable bastion for specific connections
  2. Check connection details: Use bayesian-ssh show to see bastion configuration
  3. Re-add connection: Remove and re-add with correct bastion settings
  4. Verify configuration: Check if default bastion is set in config

Database Issues

Database Connection Failed

# Check database file
ls -la ~/.config/bayesian-ssh/

# Verify permissions
chmod 755 ~/.config/bayesian-ssh/
chmod 644 ~/.config/bayesian-ssh/history.db

# Recreate database
rm ~/.config/bayesian-ssh/history.db
bayesian-ssh stats

Symptoms:

  • Error: “Database connection failed”
  • Cannot save or retrieve connections
  • Application crashes on database operations

Solutions:

  1. Check file permissions: Ensure proper ownership and permissions
  2. Verify disk space: Check available disk space
  3. Recreate database: Remove corrupted database file
  4. Check SQLite version: Ensure compatible SQLite version

Database Schema Issues

# Check database schema
sqlite3 ~/.config/bayesian-ssh/history.db ".schema"

# Verify table structure
sqlite3 ~/.config/bayesian-ssh/history.db "SELECT * FROM connections LIMIT 1;"

Symptoms:

  • Error: “Table not found”
  • Database operations fail with schema errors
  • Missing tables or columns

Solutions:

  1. Check schema: Verify table structure
  2. Recreate database: Remove and recreate database
  3. Check migrations: Ensure schema is up to date
  4. Verify SQLite: Check SQLite version compatibility

Configuration Problems

Configuration File Not Found

# Check configuration directory
ls -la ~/.config/bayesian-ssh/

# Create default configuration
bayesian-ssh config

# Verify configuration
cat ~/.config/bayesian-ssh/config.json

Symptoms:

  • Error: “Configuration file not found”
  • Application uses default values
  • Configuration changes not saved

Solutions:

  1. Create directory: mkdir -p ~/.config/bayesian-ssh/
  2. Generate config: Run bayesian-ssh config to create default
  3. Check permissions: Ensure directory is writable
  4. Verify path: Check configuration file path

Invalid Configuration Values

# View current configuration
bayesian-ssh config

# Reset to defaults
rm ~/.config/bayesian-ssh/config.json
bayesian-ssh config

# Validate configuration
cat ~/.config/bayesian-ssh/config.json | jq .

Symptoms:

  • Error: “Invalid configuration”
  • Application fails to start
  • Configuration values ignored

Solutions:

  1. Validate JSON: Check JSON syntax
  2. Reset configuration: Remove and recreate config file
  3. Check values: Verify configuration parameter values
  4. Use defaults: Start with minimal configuration

Performance Issues

Slow Connection Establishment

# Check DNS resolution time
time nslookup server.company.com

# Test connection speed
time ssh -o ConnectTimeout=10 user@server.company.com

# Profile application
bayesian-ssh --log-level debug connect "Server Name"

Symptoms:

  • Long connection times
  • Slow response to commands
  • High latency

Solutions:

  1. Check DNS: Verify DNS resolution speed
  2. Network latency: Test network performance
  3. Server load: Check target server performance
  4. Optimize configuration: Review connection settings

High Memory Usage

# Check memory usage
ps aux | grep bayesian-ssh

# Monitor resource usage
top -p $(pgrep bayesian-ssh)

Symptoms:

  • High memory consumption
  • Application becomes unresponsive
  • System memory pressure

Solutions:

  1. Check for leaks: Monitor memory usage over time
  2. Optimize queries: Review database query efficiency
  3. Limit connections: Reduce concurrent connections
  4. Update dependencies: Ensure latest library versions

Network and Firewall Issues

Firewall Blocking Connections

# Check local firewall
sudo ufw status
sudo iptables -L

# Test port accessibility
telnet server.company.com 22
nmap -p 22 server.company.com

Symptoms:

  • Connection blocked by firewall
  • Port 22 unreachable
  • Network policy violations

Solutions:

  1. Check local firewall: Verify local firewall settings
  2. Corporate policies: Contact network administrator
  3. Alternative ports: Use non-standard SSH ports
  4. VPN access: Connect through corporate VPN

DNS Resolution Issues

# Check DNS resolution
nslookup server.company.com
dig server.company.com

# Test with IP address
ssh user@192.168.1.100

# Check /etc/hosts
cat /etc/hosts

Symptoms:

  • Hostname not found
  • DNS resolution failures
  • Connection timeouts

Solutions:

  1. Check DNS servers: Verify DNS configuration
  2. Use IP addresses: Connect directly with IP
  3. Check /etc/hosts: Verify local host entries
  4. Network configuration: Check network settings

Application Crashes

Panic Errors

# Enable backtrace
RUST_BACKTRACE=1 bayesian-ssh connect "Server"

# Check logs
tail -f ~/.config/bayesian-ssh/bayesian-ssh.log

# Run with verbose output
bayesian-ssh --log-level debug

Symptoms:

  • Rust panic errors
  • Application terminates unexpectedly
  • Error messages with backtraces

Solutions:

  1. Enable backtraces: Set RUST_BACKTRACE=1
  2. Check logs: Review application logs
  3. Update to latest version: Bug may be fixed in newer release
  4. Report issue: File a bug report with backtrace on GitHub Issues

Getting Help

Debug Information

When reporting issues, include:

  • Error messages: Complete error output
  • Environment: OS version, Rust version, dependencies
  • Configuration: Relevant configuration files (redact sensitive data)
  • Steps to reproduce: Detailed reproduction steps
  • Logs: Application and system logs

Useful Diagnostic Commands

# Enable debug logging
bayesian-ssh --log-level debug

# Check system information
uname -a
rustc --version
cargo --version

# Verify dependencies
ldd $(which bayesian-ssh)

# Check file permissions
ls -la ~/.config/bayesian-ssh/
ls -la ~/.ssh/

Changelog

All notable changes to Bayesian SSH will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[2.5.0] - 2026-08-23

Security

  • Content Security Policy: The desktop app previously shipped with csp: null (no policy). A strict CSP is now enforced (default-src 'self', IPC/asset protocols only, object-src 'none', frame-ancestors 'none'), so a renderer compromise can no longer invoke arbitrary Tauri commands or load remote content.
  • ProxyCommand injection fix: Bastion host, bastion user, and key path are now POSIX shell-quoted before interpolation into ssh -o ProxyCommand=… (which runs through sh -c). A connection with a malicious bastion/key path could previously execute arbitrary shell commands on the local machine.
  • Connection input validation: add_connection/edit_connection reject control characters, whitespace, quotes, shell metacharacters, and @ in host/user/bastion fields, and control characters in key paths — preventing ssh argv/ProxyCommand breakage and injection via imported or hand-edited connection data.
  • Key generation hardening: generate_ssh_key validates the key name (no paths, no traversal) and whitelists ssh-keygen key types.
  • Notification injection fixes: Desktop notifications sanitize control characters, escape AppleScript string literals (macOS) and PowerShell single quotes (Windows), closing both script-injection vectors.
  • Terminal web links restricted: Only http/https URLs from terminal output reach the system browser; file:, smb:, and other schemes are ignored.

Fixed

  • UTF-8 panic in detached-session buffer: String::drain panicked when the 512 KB replay-buffer trim cut a multi-byte character (non-ASCII output), killing the PTY reader thread and silently freezing the session. Trimming now clamps to char boundaries.
  • PTY output flooding: The read loop emitted one IPC event per 4 KB chunk, freezing the UI during high-throughput output (yes, cat bigfile). Output is now coalesced (32 KB batches, partial reads flush early) and the frontend batches term.write calls per frame.
  • Dropped terminal output on pop-out/reattach: The pop-out window registered its output listener after writing the replay buffer, losing output in between; reattach could lose output while the xterm instance was being created. Both races are fixed (listener-first registration; per-tab pending output queue flushed on mount).
  • WebGL context loss left a blank terminal: The renderer now falls back to the Canvas renderer on context loss instead of going permanently blank.
  • Write-after-dispose crashes: term.write/term.input calls are now guarded against terminals disposed concurrently.
  • Mutex poisoning crash: All PTY session-map locks survive poisoning (lock_sessions) instead of unwrap()-panicking the whole app.
  • Resize storm: Terminal fitting is throttled to one fit + resize IPC per animation frame, and hidden tabs are skipped.
  • Ghost tabs: Detach/pop-out failures now clean up or keep the tab with an explicit error instead of leaving broken state; failed spawns auto-close the error tab after 8 s.
  • Listener init race: Terminal event listeners can no longer leak or double-register during startup/teardown races.
  • Sequential ping storm: “Ping All” now pings hosts concurrently (bounded at 16) instead of serially (N × 3 s worst case).

Changed

  • Design system foundation: The desktop UI now runs on a documented, token-driven design system (tokens.css / base.css / components.css, see docs/design/design-system.md): semantic color tokens (including new panel, running, error, on-accent, overlay), a disciplined 10–20 px type scale, radius rules, and shared component primitives (buttons, inputs, tags/badges, status dots, kbd, alerts, empty states, skeletons, toasts, modals). Shared chrome (title bars, sidebar, modals, toasts, custom select, loader, shortcut sheet, delete dialog) already consumes the primitives; raw palette colors in views were mapped to semantic tokens.
  • Focus & motion: All interactive elements now show a visible accent focus ring on keyboard focus; status dots gained a calm pulse; the app loader was de-cluttered (no gradients/glow/bounce) and honors prefers-reduced-motion.
  • Terminal palette fidelity: xterm now also reads the theme’s --text-primary/--text-secondary/--text-muted/--surface-input variables (they were previously undefined, so the terminal silently fell back to fixed colors).
  • Dead styles fixed: bg-surface-card (used by SFTP/tunnel/snippets panels) and scrollbar-none were silently no-ops; both are now defined tokens/utilities.
  • Full ANSI terminal palette: xterm now uses a complete 16-color palette + selection color derived from the active app theme (previously the CSS variables were undefined and the terminal silently fell back to fixed colors).
  • Linux font fallbacks: Terminal font stack now includes Ubuntu Mono / DejaVu Sans Mono / Liberation Mono so the terminal looks right on distros without JetBrains Mono.
  • Snippets “Execute in PTY” now actually executes: The command is sent to the active terminal (with the configured confirmation gate) instead of showing a no-op toast.
  • Copy button fixed: In the Hosts list, the copy button copies the ssh command (it was wired to Edit); the duplicate-then-edit flow no longer selects the wrong host.

[2.4.0] - 2026-08-15

Added

  • Optional SFTP and Tunneling: Desktop Settings > Features can hide SFTP and tunneling UI when those workflows are unused.
  • Terminal rendering upgrades: WebGL GPU acceleration, SIXEL/iTerm2 image rendering, Unicode 11, and scrollback export.
  • Terminal settings: Dedicated settings section with live font preview, plus xterm web-links, in-terminal search, and OSC52 clipboard support.

Changed

  • CLI TUI modularization: Split search, models, modal input, overlay rendering, and SFTP task spawning into domain-scoped modules.
  • Shared backend services: Agent, Kerberos, PTY, security audit, and subprocess SFTP now live in the CLI crate and are reused by the desktop GUI.
  • Desktop UI structure: Settings panels, modal chrome (ModalShell), and connection/session/settings stores are extracted from monolithic views.

Fixed

  • SFTP over bastion + Kerberos: Interactive marker-based stdin/stdout path for listings and transfers.
  • Desktop shortcuts: Keyboard shortcuts and tab navigation activate reliably across views.

[2.1.2] - 2026-07-15

Added

  • Kerberos client detection: Desktop app reads krb5.conf to detect configured realms, infer default principals, and resolve credential cache paths even when no ticket is present.
  • Branded app icon sync: Tray, title bar, and favicon now use the custom Bayesian SSH icon consistently.

Fixed

  • Kerberos Heimdal compatibility: Fixed false “no ticket” detection when valid tickets use Credentials cache: output from Heimdal klist.
  • Modal accessibility: Resolved Svelte a11y warnings in Kerberos, Detached Sessions, and Onboarding modals; replaced deprecated <svelte:component> in Sidebar.
  • Tray icon embedding: Use dedicated 32×32 compile-time icon asset for reliable Linux system tray display.

[2.1.1] - 2026-07-14

Added

  • System Tray Support: Desktop app minimizes to the system tray instead of quitting on close; tray menu provides Open and Quit actions.
  • Graceful Quit Flow: Dedicated quit button and confirmation dialog when active terminal sessions are open.
  • Shared Terminal Utilities: Extracted xterm I/O, clipboard key handling, and Linux IME composition guard into reusable helpers.

Fixed

  • PTY Terminal Rendering: Set TERM=xterm-256color for spawned PTY sessions so vim, nano, and htop render correctly.
  • PTY Resize: Implemented resize_pty to propagate terminal dimensions to the backend PTY master.
  • Terminal Focus Handling: Global keyboard shortcuts no longer steal keys when the xterm terminal has focus; active tab auto-focuses on switch.

[2.1.0] - 2026-07-13

Added

  • Core Modularization and Refactoring: Comprehensive codebase-wide refactoring of the CLI parser, TUI state management, and TUI keyboard input dispatcher. Splits monolithic modules into clear, domain-scoped files (sftp.rs, tunnels.rs, tabs.rs, modals.rs).
  • Tauri Backend Commands Modularization: Restructured the monolithic commands.rs into a dedicated domain-scoped commands/ directory resolving connections, PTY terminal spawning, settings, env, ssh-agent, native file dialogues, and configuration imports.
  • Svelte 5 App State custom store: Extracted reactive UI states, properties, and async Tauri IPC invocations from the presentation page component (+page.svelte) into a custom store (appState.svelte.ts).
  • Terminal UI/UX Improvements: Dynamic computed-style terminal themes matching Zinc/OLED/Slate/Cyberpunk backgrounds, auto-retheming on settings update using MutationObservers, keyboard and mouse-wheel font size zoom shortcuts (Ctrl + + / - / 0), extra viewport padding, custom scrollbars, and active tab glows.
  • Detached GUI Subcommand: Added bssh desktop (and bssh gui) command to spawn the Tauri desktop client detached in the background and release prompt control instantly.

Fixed

  • Terminal Clipboard Handling: Custom event handlers intercepting copy/paste shortcuts to resolve character scrambling, duplicate pasting, and SIGINT conflicts when copying active selections.

[2.0.0] - 2026-07-13

Added

  • Multi-host Split View and Session Management: Connect to multiple hosts concurrently without closing current terminal sessions. Features a sidebar for quickly searching and spawning terminal sessions in tabs.
  • Popout Terminals & Detached Session Management:
    • Move active terminal sessions into standalone windows with full drag-and-drop / tab reattachment support.
    • Custom DetachedSessionsModal lists all background/popped-out terminal processes with close, reattach, and kill actions.
  • Kerberos Ticket Management:
    • Implemented real-time ticket checks, acquisition, and renewal scripts (kerberos.rs).
    • Added warning thresholds on ticket expiry and a dedicated interactive KerberosModal for quick credential refreshes.
  • App Onboarding Process: Guided start workflow using an OnboardingModal setting up default users, workspace profiles, paths, and themes on first run.
  • Session Logging: Capture and audit stdout/stderr terminal session logs securely in sqlite history database.
  • SSH Agent & Configuration Defaults:
    • Automatically detect and pre-fill the custom SSH agent socket from the live SSH_AUTH_SOCK environment variable.
    • Option to set a default identity file (SSH private key) globally in Settings.
  • Premium Custom Modals: Beautiful dialog overlays replacing ugly browser native confirm() prompts for connection or profile deletions. Features high-fidelity pulsing warning animations and full dark mode compatibility.
  • Frictionless Connection Cloning:
    • Fixed arguments parsing issue with add_connection on duplicate.
    • Automatically highlights newly duplicated connections using a brief cyan flash animation.
    • Immediately opens the Connection edit drawer upon duplication to prompt for value updates.
  • Aesthetics & Accessibility: Enhanced sidebar toggle navigation, custom titlebar controls, responsive grid/list styling, and refactored overall project component modularity.

Fixed

  • PTY Session Isolation: Kept master PTY descriptor alive in PtySession state to prevent closing one tab from triggering EOF and closing all active SSH connections.
  • Manual Close Race: Suppressed duplicate exit signals when manually closing terminal windows or PTY processes via close_pty.

[1.6.0] - 2026-07-12

Added

  • Tauri Desktop Application: A beautiful, performance-oriented standalone desktop GUI built using SvelteKit, TypeScript, and Rust. Features:
    • Vercel/Linear-inspired sleek dark mode layout with Grid and List views.
    • Collapsible drawer sidebar with tags and profiles selection.
    • Interactive tabbed inline terminals powered by Xterm.js and a backend PTY process broker.
    • Connection logs audit table and system metrics monitoring.
    • Global keyboard shortcuts and search bar navigation.
    • Agnostic build and installation system integrated via Makefile and install.sh scripts.
  • Refactored core shared library: Modularized bayesian-ssh into a reusable Rust library target.
  • doctor command: Diagnose the active environment, configuration, SQLite database, SSH client, SSH config, ssh-agent socket, and Kerberos helper tools from the CLI.

Changed

  • Actionable CLI errors: Command failures now print a stable Error: line and targeted Suggestion: hints for common recovery paths.

[1.5.0] - 2026-04-18

Added

  • Native russh SSH transport: Pure-Rust SSH transport layer (SshTransport trait) with russh backend, known-hosts verification, and multi-method authentication — replaces subprocess-based SSH for supported operations
  • SFTP via russh-sftp: Full SFTP session implementation (RusshSftpSession) built on the native transport for file operations without shelling out
  • exec, upload, download commands: New CLI commands backed by TransferService for remote command execution and file transfers
  • Recursive upload and download: SFTP upload and download now support entire directory trees recursively
  • Local port-forward tunnel: bssh forward -L establishes SSH local port-forwarding tunnels
  • SOCKS5 dynamic proxy: bssh proxy -D creates a SOCKS5 dynamic forwarding proxy through the SSH connection
  • TUI SFTP file browser: New Files tab for browsing remote file systems, uploading, deleting, creating directories, and renaming files interactively
  • TUI Tunnels tab: Start, list, and stop port-forward tunnels directly from the TUI

Changed

  • Transport architecture refactored: Extracted SshTransport trait with SubprocessTransport and RusshTransport implementations behind a dispatcher for flexible backend selection

Fixed

  • CLI argument short flags: Corrected conflicting short flags for SSH commands

[1.4.1] - 2026-03-05

Added

  • TUI tab-based navigation: Three tabs — Connections (1), History (2), Config (3) — switchable with number keys or Tab/Shift+Tab
  • Add new connection form: Press a in the Connections tab to create a connection directly from the TUI with a 9-field overlay (Name, Host, User, Port, Bastion, Bastion User, Key Path, Kerberos, Tags) and validation
  • Session history view: History tab with sortable columns (Date, Name, Duration, Status), connection name filter (/), failed-only toggle (f), and reconnect on Enter
  • Environment management: Config tab listing all environments with the active one highlighted; switch (Enter), create (a), or delete (d) environments without leaving the TUI
  • Connection grouping by tag: Toggle grouped view with f — connections are organized under collapsible tag headers
  • Quick-connect bar: Press : and type [user@]host[:port] to connect to an ad-hoc host without saving it
  • Multi-select and batch operations: Space to toggle selection, Ctrl+A to select all, x to batch-delete selected connections with a confirmation dialog
  • SSH command preview: Press p to see the full SSH command that would be executed, broken down by component (host, port, user, bastion, kerberos flags, key path)
  • Async TCP ping with status indicators: Press P to ping the selected connection in the background; results appear as colored indicators — green ● with round-trip time for reachable, red ● for unreachable, yellow ◌ while checking
  • Shared ping service (services/ping.rs): Lightweight TCP-level reachability check using tokio::net::TcpStream with timeout — no external process spawning; pings bastion host on port 22 for bastion connections

Changed

  • TUI modular architecture: Split monolithic tui/app.rs (721 lines) and tui/ui.rs (572 lines) into focused modules:
    • models.rs — enums and small types (Tab, AppMode, EditState, PingStatus, etc.)
    • state.rs — App struct and state management with tokio::sync::mpsc ping channel
    • input.rs — keyboard handlers dispatched per tab and mode
    • event_loop.rs — terminal setup/teardown and main loop with async ping result draining
    • ui/mod.rs — draw dispatcher routing to tab views and overlays
    • ui/header.rs, ui/list.rs, ui/detail.rs, ui/status.rs, ui/overlays.rs, ui/history.rs, ui/config.rs, ui/helpers.rs
  • Edit overlay extended: Now supports 9 fields (added Key Path); EditState includes is_new flag and validate() for required-field checks
  • Ping indicators use distinct colors: Reachable (green), unreachable (red), checking (yellow) — previously all rendered identically

Fixed

  • Unreachable key binding patterns: Ctrl+A select-all now correctly takes precedence over plain a add-connection; removed dead g grouping arm that was shadowed by go-to-top

[1.4.0] - 2026-03-05

Added

  • New commands: bssh backup, bssh restore, bssh duplicate, bssh env, bssh export, bssh groups, bssh ping — expand the CLI with backup/restore, connection cloning, environment management, export, grouping, and latency checks
  • Multi-environment configuration: Manage separate configs per environment with the --env flag; environment name shown in TUI header and logs
  • TUI detail pane: Side panel (toggled with Enter/d) displaying all connection fields, full SSH command preview, and contextual hints
  • TUI inline editing: Edit any connection directly from the TUI with e — 8-field overlay with cursor navigation, written back to the database on save
  • TUI sorting: Cycle sort field with s (Name → Host → Last Used → Created) and toggle direction with S; indicator shown in header
  • TUI compact view: Toggle single-line vs two-line row display with v
  • Optimized release profile: opt-level=3, thin LTO, single codegen unit, stripped symbols, panic=abort

Changed

  • Database module split: database/ refactored into separate submodules (connection, alias, session, search) for better organisation
  • Core error handling: Internal refactoring of error types and propagation across modules
  • TUI visual refresh: Alternating row backgrounds, [B]/[K] bastion/Kerberos badges, mode-coloured status bar badge, improved help overlay (50-col popup)

Fixed

  • TUI log corruption: Tracing output is now routed to ~/.local/share/bayesian-ssh/tui.log when the TUI is active, preventing log lines from corrupting the alternate-screen display

Removed

  • SCP command: Removed bssh scp and the associated config fields bastion_scp_mode and bastion_scp_wrapper

[1.3.2] - 2025-12-31

Fixed

  • Log level configuration ignored: The log_level setting in config file was not being applied because logging was initialized before loading the configuration. Now the config is loaded first and the tracing subscriber respects the configured log level (trace, debug, info, warn, error, off/none).

[1.3.1] - 2025-12-30

Added

  • Bayesian-ranked search: Smart connection ranking combining:
    • Prior probability (usage frequency with Laplace smoothing)
    • Likelihood (match quality: exact, prefix, word-boundary, contains)
    • Recency (exponential decay based on last use)
    • Success rate (connections that work get boosted)- Configurable search mode (bssh config --search-mode bayesian|fuzzy)
    • bayesian: Smart ranking based on usage patterns (default)
    • fuzzy: Simple pattern matching
  • Assets: SVG icons, banner, architecture and workflow diagrams

Changed

  • Default search mode is now “bayesian” for smarter results

[1.3.0] - 2025-12-30

Added- Interactive TUI mode (bssh tui): Full-screen terminal interface with ratatui

  • Browse, search, and connect to servers
  • Keyboard navigation (vim-style j/k, arrows, PgUp/PgDn)
  • Tag filtering, help overlay, confirmation dialogs
  • Session history command (bssh history): View connection history with statistics
    • Success/failure rates, average duration
    • Filter by connection, days, failed-only
  • Connection aliases (bssh alias): Create shortcuts for connections
    • bssh alias add db prod-database → bssh connect db
    • Aliases work transparently with connect command
  • Close/kill sessions (bssh close): Manage active SSH sessions
    • List active sessions with PID and stale detection
    • Close specific sessions or all at once
    • --cleanup to remove stale sessions (PIDs no longer running)
  • Configurable search mode (bssh config --search-mode bayesian|fuzzy)
    • bayesian: Smart ranking based on usage patterns (default)
    • fuzzy: Simple pattern matching

Changed

  • Connect command now checks aliases before fuzzy search
  • Session tracking improved with accurate active/stale detection
  • Default search mode is now “bayesian” for smarter results

Dependencies

  • Added ratatui and crossterm for TUI
  • Enabled signal feature in nix for process management

[1.2.0] - 2025-12-22

Added

  • --force flag for remove command: Skip confirmation prompt with -f or --force
  • --clear-bastion flag for config command: Clear default bastion settings
  • Shared CLI utilities module: New src/cli/utils.rs for consistent UX across commands
  • Working “search again” feature: The ‘s’ option in interactive selection now performs actual recursive search
  • Contextual help messages: Suggestions like “Use ‘bssh list’ to see all connections” when no matches found

Changed

  • Single-match auto-connect: Connect and show commands now auto-select when only one fuzzy match is found (improved UX)
  • Default yes for non-destructive operations: Confirmation prompts now default to Yes [Y/n] for show/connect
  • Simplified remove confirmation: Changed from typing full connection name to simple y/n prompt (use --force to skip)

Fixed

  • Config update bug: Fixed double-wrapping of Option<Option<String>> for bastion settings that prevented clearing values
  • DateTime parsing panic: Graceful handling of malformed dates in database instead of crashing
  • Home directory panic: Better error message when $HOME is not set during SSH config import

Technical

  • Major code deduplication: Extracted ~300 lines of duplicated code from connect.rs, edit.rs, remove.rs, show.rs into shared utilities
  • Reduced file sizes: connect.rs (257→65 lines), edit.rs (360→70 lines), remove.rs (235→70 lines), show.rs (246→35 lines)

[1.1.1] - 2025-08-29

Fixed

  • Configuration defaults: Changed default user from hardcoded “admin” to current system user
  • Kerberos default: Disabled Kerberos by default (changed from true to false)
  • Documentation: Updated all examples to reflect new sensible defaults

Changed

  • Default configuration: Application now uses current Linux username instead of “admin”
  • Kerberos behavior: Kerberos authentication is now opt-in rather than default
  • Documentation examples: Updated configuration commands and JSON examples across all docs

Technical

  • Dependencies: Added whoami crate for system user detection
  • Configuration: Updated AppConfig::default() implementation
  • Documentation: Updated README.md, docs/README.md, and docs/advanced-usage.md

[1.1.0] - 2025-08-28

Added

  • Intelligent fuzzy search across all commands - Find connections by partial names, tags, or patterns
    • Enhanced connect command with fuzzy search and interactive selection
    • Enhanced edit command with fuzzy search for connection management
    • Enhanced show command with fuzzy search for connection details
    • Enhanced remove command with fuzzy search and extra confirmation

Fuzzy Search Features

  • Smart pattern matching: Handles hyphens, underscores, and separators (webprod → web-prod-server)
  • Tag-based search: Search within connection tags
  • Recent connections fallback: Shows recently used connections when no matches found
  • Interactive selection: Numbered menus for multiple matches with user-friendly prompts
  • Relevance ranking: Prioritizes recently used and exact matches

Enhanced Safety

  • Extra confirmation for destructive operations: remove command requires typing full connection name
  • Graceful error handling: Clear messages and helpful suggestions
  • Backwards compatibility: All existing functionality preserved

Documentation

  • Updated README with fuzzy search examples across all commands
  • Enhanced user guide with practical usage scenarios
  • Improved feature descriptions and examples

Technical Improvements

  • Enhanced database layer with fuzzy search algorithms
  • Improved error handling and user feedback
  • Better code organization and maintainability

[1.0.0] - 2024-08-23

Added

  • Initial release of Bayesian SSH
  • Basic SSH connection management
  • Kerberos authentication support
  • Bastion host routing
  • Tag-based organization
  • SQLite database persistence
  • Connection history and statistics

Core Features

  • One-click connections to servers
  • Automatic Kerberos ticket management
  • Smart bastion host routing
  • Tag-based organization for easy management
  • Complete connection history with statistics
  • SQLite database for persistence

Types of changes

  • Added for new features
  • Changed for changes in existing functionality
  • Fixed for any bug fixes
  • Removed for now removed features
  • Security in case of vulnerabilities

Design System

How the desktop GUI looks and how new screens must be built. The implementation lives in desktop/src/lib/styles/:

FileContents
tokens.cssColor, type, radius, shadow, motion and layout tokens, plus per-theme overrides
base.cssReset, typography defaults, focus ring, scrollbars, keyframes, reduced-motion guard
components.cssEvery shared primitive (buttons, inputs, tables, modals, settings rows, …)
app.cssEntry point: Tailwind, bundled fonts, the files above

Fonts are bundled (@fontsource-variable/inter, @fontsource-variable/jetbrains-mono): the app’s CSP blocks remote fonts, and the UI must look the same offline.


Principles

  • Calm, dense, technical. Neutral surfaces, one accent, real data in front. No gradients, glow, glassmorphism or decorative icon tiles.
  • One title per screen. The view header is the page header; there is no global top bar repeating it.
  • One primary action per view or modal. Everything else is secondary or ghost.
  • Keyboard first. Every action is reachable by keyboard and shortcuts are visible (.kbd).
  • Motion only signals change (100–220 ms, --ease-out). prefers-reduced-motion disables it.
  • Cheap to render. No backdrop-filter (expensive in WebKitGTK, especially over the terminal’s WebGL canvas), no transition-all, no colored shadows.

Layout

┌ titlebar (bg-chrome, 38px): brand · command search (Ctrl/⌘K) · help · window buttons ┐
├ sidebar (bg-chrome, 224px / 56px collapsed) ┬ workspace (bg-surface, rounded top-left) ┤
│ profile switcher                            │ .view                                   │
│ Hosts · Terminals · Files · Tunnels         │   .view-header  title + count │ actions │
│ Security: Keys · Audit                      │   .view-toolbar search · chips · sort   │
│ Activity: History · Snippets                │   .view-body    scrolling content       │
│ … agent / Kerberos status · Settings        │                                         │
└─────────────────────────────────────────────┴─────────────────────────────────────────┘

Every top-level view uses exactly this skeleton:

<div class="view">
  <header class="view-header">
    <div class="flex min-w-0 items-baseline gap-2.5">
      <h1 class="view-title">Hosts</h1>
      <span class="text-sm tabular-nums text-muted">12</span>
    </div>
    <div class="view-actions">
      <button type="button" class="btn btn-ghost">Secondary</button>
      <button type="button" class="btn btn-primary">Primary</button>
    </div>
  </header>
  <div class="view-toolbar"><!-- optional filters --></div>
  <div class="view-body"><!-- content --></div>
</div>

Settings keeps the .view-header but replaces toolbar and body with .settings-shell (its own section nav plus a .settings-page).

Long lists are windowed: the Hosts table mounts only the rows in view (spacer rows keep the scroll height) and the grid mounts cards page by page as you scroll. Reuse that approach for any list that can reach hundreds of rows.


Tokens

All colors are semantic --color-* tokens, which generate Tailwind utilities (bg-panel, text-muted, border-border, …). Components never use raw hex values. The one exception is the theme preview swatches in Appearance settings.

TokenRole
chromeTitle bar and sidebar: the frame around the workspace
surfaceWorkspace / view background
panelCards, tables, settings groups
surface-raisedPopovers, menus, modals, toasts
surface-inputInputs, selects, search boxes
surface-hover / surface-activeRow hover / selected row, active nav item
surface-terminalxterm background
border / border-subtle / border-hover / border-strongHairlines, from quietest to strongest
border-focusFocused control border and focus ring
primary / secondary / muted / faintText, from content down to disabled/decorative
accent / accent-hover / accent-muted / on-accentPrimary action, selection, focus; text on accent fills
success / warning / error (running, danger aliases)State only: never decoration
overlayModal backdrop

The default theme sets these in the @theme block. Each html.theme-* class overrides them and also defines the xterm palette variables (--bg-terminal, --accent-*, --text-*, --selection-bg) that lib/utils/theme.ts reads.

Theme id (persisted)Name in UICharacter
zincGraphiteNeutral graphite, periwinkle accent (default)
cyberpunkMidnightDeep navy, cyan accent
oledOLED blackTrue black, white accent
slateSlateCool blue-grey, sky accent

Type scale (Inter; JetBrains Mono for data): 2xs 11px for badges and kbd · xs 12px for meta · sm 13px for body/default · base 14px for view titles · lg 16px for modal/settings titles · xl 20px for figures. Weights are 400/500/600 only. Arbitrary sizes (text-[10px]) are not allowed. Use tabular-nums (or .tabular) for numbers that line up.

Radius: md 6px for controls · lg 8px for cards and tables · xl 10px for modals.

Layout: --titlebar-h 38px · --header-h 52px · --sidebar-w 224px / --sidebar-w-collapsed 56px.


Primitives

GroupClassesNotes
Buttons.btn + -primary -secondary -ghost -danger -danger-ghost; .btn-sm (28px) .btn-lg (36px); .btn-icon .btn-icon-sm .btn-icon-dangerDefault height 32px. Icon buttons need aria-label.
Inputs.input .input-mono .input-invalid, textarea.input; .search-box > inputFocus shows the border-focus border plus an accent-muted ring
Fields.field .field-label .field-hint .field-errorValidation messages sit inline under the control
Togglesinput.switch (on/off settings), input.checkbox (selection)Native checkboxes, restyled
SelectCustomSelect component (label, size="sm"|"md")Keyboard navigable listbox
Badges.badge + -neutral -accent -success -warning -error, .badge-mono; .count; .tag; .kbdBadges are 20px, rounded-sm, never pills
Status.status-dot + -success -warning -error -offline -running, -sm, .status-dot-live
Text.section-label, .link, .mono, .code-block
Containers.panel (+ -header -title -body), .card .card-interactive .card-selected, .stat (+ -label -value), .divider
Tables.table-wrap > table.data-table; tr.is-selected; .row-actionsActions are revealed on row hover, selection and focus
Navigation.segmented .segmented-item(-active), .chip(-active), .tabs .tab(-active)
Menus.popover, .menu-item(-active)(-danger), .menu-separator
Feedback.alert + -error -warning -success -info, .alert-title; .toast; .skeleton; .spinner
Empty states.empty-state -icon -title -desc -actionOne sentence of description, at most two actions
ModalModalShell component → .modal-overlay .modal-panel; .modal-header .modal-title .modal-subtitle .modal-close .modal-body .modal-footerFooter: secondary, then primary, right-aligned
Settings.settings-shell .settings-sidebar .settings-nav-item(-active) .settings-content .settings-page .settings-heading .settings-desc .settings-group .settings-group-title .setting-row .setting-row-main .setting-title .setting-meta .setting-control .system-valueRows inside a group are separated automatically

If a pattern repeats in markup, it belongs in components.css; don’t add a local variant.


Writing

  • Sentence case everywhere (“New host”, “Run security audit”).
  • Labels say what happens; avoid marketing terms (“high-performance”, “studio”).
  • Errors say what failed and what to do next. Prefer an inline .field-error to a toast when the problem is in a form field.
  • Relative times (“5 min ago”) in lists, with the absolute date in the title tooltip (formatRelative / formatDateTime in lib/utils/timezone.ts).

Accessibility

  • Every interactive element is a real <button type="button">, link or input, and gets the global :focus-visible ring.
  • Icon-only buttons have aria-label. Toggles and segmented controls expose aria-pressed / aria-checked.
  • Modals trap focus, close on Escape and restore focus on close (ModalShell).
  • Never encode meaning in color alone: status dots carry a title, and badges carry text.