Bayesian SSH
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,downloadwithout 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
Option 1: One-liner Install (Recommended)
# 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
| Command | Description |
|---|---|
bayesian-ssh add | Add a new connection |
bayesian-ssh connect | Connect to a server (fuzzy search) |
bayesian-ssh list | List all connections |
bayesian-ssh show | Show connection details |
bayesian-ssh edit | Edit a connection |
bayesian-ssh remove | Remove a connection |
bayesian-ssh import | Import from SSH config |
bayesian-ssh desktop | Launch the desktop GUI |
bayesian-ssh history | View session history |
bayesian-ssh alias | Manage connection aliases |
bayesian-ssh config | View/update configuration |
bayesian-ssh stats | View statistics |
bayesian-ssh close | Manage active sessions |
bayesian-ssh backup | Backup database |
bayesian-ssh restore | Restore from backup |
bayesian-ssh ping | Check server latency |
Fuzzy Search
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"
}
| Option | Default | Description |
|---|---|---|
default_user | System user | Default SSH user for new connections |
default_bastion | None | Default bastion host for all connections |
default_bastion_user | System user | Default user for bastion connections |
use_kerberos_by_default | false | Enable Kerberos authentication by default |
log_level | "info" | Log verbosity: trace, debug, info, warn, error, off |
auto_save_history | true | Automatically save session history |
max_history_size | 1000 | Maximum 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 case | Command |
|---|---|
| Run a one-off command and capture output | exec |
| Copy one or more files | upload / download |
| Reach a single remote port from your laptop | forward |
| Reach many internal hosts/ports through one SSH session | proxy |
| Interactive shell session | connect |
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,.rpmand AppImage bundles are built under thebayesian-ssh-desktopproduct name; the.deband.rpmadd an entry to the application menu; - in the snap, the
bayesian-ssh.guiapp 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:portwhen 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 upand 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 (Enternext match,Shift+Enterprevious). - Font size buttons adjust the terminal text;
Ctrl/Cmd+ mouse wheel zooms, andCtrl/Cmd++/-/0adjust 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
.txtfile), 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
(
Enterloads,Esccancels). - Parent directory and Refresh buttons sit in the toolbar, next to quick
bookmarks for
/,~,/var/www,/etc,/var/logand/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 600command 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/.rpmfrom 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 showssudo snap refresh bayesian-ssh), and other installs (raw binaries frominstall.shor 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+Enterruns,Ctrl/Cmd+Shift+Dtoggles 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.
| Action | Keys |
|---|---|
| Command palette | Ctrl/Cmd + K |
| Keyboard shortcuts | ?, F1, Ctrl/Cmd + / |
| Close a dialog or clear the filter | Esc |
| Hosts / Terminals / Keys / Audit / History / Settings | 1 / 2 / 3 / 4 / 5 / 6 |
| Focus the host filter | / |
| New host | N, Ctrl/Cmd + N |
| Move host selection | ↑/↓, j/k |
| Connect to the selected host | Enter |
| Edit the selected host | Ctrl/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
- Always use Kerberos for internal/enterprise servers
- Use separate SSH keys per environment (dev, staging, production)
- Route internal servers through bastion hosts
- Review session history regularly with
bayesian-ssh history - Use
--forcecarefully on destructive operations - 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
clapfor 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
rusqlitefor 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
| Channel | Job | Turns on with | Publishes |
|---|---|---|---|
| GitHub release | build, packages, release | GITHUB_TOKEN (built in) | Binaries, bundles, unified .deb/.rpm, SHA256SUMS, provenance attestations, and latest.json when updater keys are set |
| In-app updater | gate, build | TAURI_SIGNING_PRIVATE_KEY secret and BAYESIAN_SSH_UPDATER_PUBLIC_KEY variable | Signed updater artifacts and latest.json |
| Unified packages | packages | none | bayesian-ssh_<version>_amd64.deb and .rpm combining the CLI and the desktop GUI |
| Snap Store | snap | SNAPCRAFT_STORE_CREDENTIALS | An amd64 + arm64 snap on the Snap Store (stable, or candidate for prereleases) |
| Flatpak / Flathub | — (manual) | none | A 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@v2records 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-blobsignsSHA256SUMSand stores the bundle asSHA256SUMS.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 unifiedbayesian-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
- Generate a new keypair with
npx tauri signer generate. - Replace the
TAURI_SIGNING_PRIVATE_KEY/TAURI_SIGNING_PRIVATE_KEY_PASSWORDsecrets and theBAYESIAN_SSH_UPDATER_PUBLIC_KEYvariable 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:
~/.sshis mounted read-only, soknown_hostsis not updated andkey generatecannot write keys into~/.ssh.- The app’s config and database live under
~/snap/bayesian-ssh/current/.config/bayesian-sshand are not shared with a native install or withinstall.sh. ssh,kinitandklistare 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-launchedkinitlive in the snap’s private/tmpand 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 withKRB5CCNAME=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,.rpmor AppImage) in that case.
- tickets obtained from the app (Kerberos dialog) or with
- 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/, sourcehttps://github.com/abdoufermat5/bayesian-ssh, issues/contacthttps://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.tomlcrates/gui/Cargo.tomlcrates/gui/tauri.conf.jsondesktop/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
klistshows no tickets or expired tickets
Solutions:
- Check ticket status:
klist -s - Create new ticket:
kinit -f -A - Verify realm configuration: Check
/etc/krb5.conf - 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
klistshows expired tickets
Solutions:
- Automatic renewal:
kinit -R - Manual renewal:
kinit -f -A - Check clock sync: Ensure system time is correct
- 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:
- Check SSH service:
systemctl status sshd - Verify port: Ensure SSH is listening on correct port
- Check firewall: Verify firewall allows SSH traffic
- 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:
- Check key permissions:
chmod 600 ~/.ssh/id_rsa - Verify key format: Ensure key is in correct format
- Check server configuration: Verify
authorized_keyssetup - 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:
- Test bastion directly:
ssh user@bastion.company.com - Check bastion port: Verify correct port (default: 22)
- Verify user permissions: Ensure bastion user has access
- 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:
- Check bastion routing: Verify bastion can reach target
- Verify target firewall: Ensure target allows bastion traffic
- Check network segmentation: Verify network policies
- 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:
- Use
--no-bastionflag: Explicitly disable bastion for specific connections - Check connection details: Use
bayesian-ssh showto see bastion configuration - Re-add connection: Remove and re-add with correct bastion settings
- 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:
- Check file permissions: Ensure proper ownership and permissions
- Verify disk space: Check available disk space
- Recreate database: Remove corrupted database file
- 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:
- Check schema: Verify table structure
- Recreate database: Remove and recreate database
- Check migrations: Ensure schema is up to date
- 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:
- Create directory:
mkdir -p ~/.config/bayesian-ssh/ - Generate config: Run
bayesian-ssh configto create default - Check permissions: Ensure directory is writable
- 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:
- Validate JSON: Check JSON syntax
- Reset configuration: Remove and recreate config file
- Check values: Verify configuration parameter values
- 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:
- Check DNS: Verify DNS resolution speed
- Network latency: Test network performance
- Server load: Check target server performance
- 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:
- Check for leaks: Monitor memory usage over time
- Optimize queries: Review database query efficiency
- Limit connections: Reduce concurrent connections
- 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:
- Check local firewall: Verify local firewall settings
- Corporate policies: Contact network administrator
- Alternative ports: Use non-standard SSH ports
- 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:
- Check DNS servers: Verify DNS configuration
- Use IP addresses: Connect directly with IP
- Check /etc/hosts: Verify local host entries
- 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:
- Enable backtraces: Set
RUST_BACKTRACE=1 - Check logs: Review application logs
- Update to latest version: Bug may be fixed in newer release
- 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 throughsh -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_connectionreject 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_keyvalidates the key name (no paths, no traversal) and whitelistsssh-keygenkey 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/httpsURLs from terminal output reach the system browser;file:,smb:, and other schemes are ignored.
Fixed
- UTF-8 panic in detached-session buffer:
String::drainpanicked 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 batchesterm.writecalls 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.inputcalls are now guarded against terminals disposed concurrently. - Mutex poisoning crash: All PTY session-map locks survive poisoning (
lock_sessions) instead ofunwrap()-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, seedocs/design/design-system.md): semantic color tokens (including newpanel,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-inputvariables (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) andscrollbar-nonewere 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.confto 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-256colorfor spawned PTY sessions so vim, nano, and htop render correctly. - PTY Resize: Implemented
resize_ptyto 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.rsinto a dedicated domain-scopedcommands/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(andbssh 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
DetachedSessionsModallists 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
KerberosModalfor quick credential refreshes.
- Implemented real-time ticket checks, acquisition, and renewal scripts (
- App Onboarding Process: Guided start workflow using an
OnboardingModalsetting 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_SOCKenvironment variable. - Option to set a default identity file (SSH private key) globally in Settings.
- Automatically detect and pre-fill the custom SSH agent socket from the live
- 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_connectionon 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.
- Fixed arguments parsing issue with
- 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
PtySessionstate 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-sshinto a reusable Rust library target. doctorcommand: 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 targetedSuggestion:hints for common recovery paths.
[1.5.0] - 2026-04-18
Added
- Native russh SSH transport: Pure-Rust SSH transport layer (
SshTransporttrait) withrusshbackend, 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,downloadcommands: New CLI commands backed byTransferServicefor 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 -Lestablishes SSH local port-forwarding tunnels - SOCKS5 dynamic proxy:
bssh proxy -Dcreates 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
SshTransporttrait withSubprocessTransportandRusshTransportimplementations 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 orTab/Shift+Tab - Add new connection form: Press
ain 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 onEnter - 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:
Spaceto toggle selection,Ctrl+Ato select all,xto batch-delete selected connections with a confirmation dialog - SSH command preview: Press
pto 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
Pto 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 usingtokio::net::TcpStreamwith 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) andtui/ui.rs(572 lines) into focused modules:models.rs— enums and small types (Tab,AppMode,EditState,PingStatus, etc.)state.rs—Appstruct and state management withtokio::sync::mpscping channelinput.rs— keyboard handlers dispatched per tab and modeevent_loop.rs— terminal setup/teardown and main loop with async ping result drainingui/mod.rs— draw dispatcher routing to tab views and overlaysui/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);
EditStateincludesis_newflag andvalidate()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+Aselect-all now correctly takes precedence over plainaadd-connection; removed deadggrouping 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
--envflag; 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 withS; 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.logwhen the TUI is active, preventing log lines from corrupting the alternate-screen display
Removed
- SCP command: Removed
bssh scpand the associated config fieldsbastion_scp_modeandbastion_scp_wrapper
[1.3.2] - 2025-12-31
Fixed
- Log level configuration ignored: The
log_levelsetting 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 connectionsbssh 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
--cleanupto 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
ratatuiandcrosstermfor TUI - Enabled
signalfeature innixfor process management
[1.2.0] - 2025-12-22
Added
--forceflag for remove command: Skip confirmation prompt with-for--force--clear-bastionflag for config command: Clear default bastion settings- Shared CLI utilities module: New
src/cli/utils.rsfor 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
--forceto 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
$HOMEis 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
truetofalse) - 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
whoamicrate 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
connectcommand with fuzzy search and interactive selection - Enhanced
editcommand with fuzzy search for connection management - Enhanced
showcommand with fuzzy search for connection details - Enhanced
removecommand with fuzzy search and extra confirmation
- Enhanced
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:
removecommand 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
Addedfor new featuresChangedfor changes in existing functionalityFixedfor any bug fixesRemovedfor now removed featuresSecurityin 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/:
| File | Contents |
|---|---|
tokens.css | Color, type, radius, shadow, motion and layout tokens, plus per-theme overrides |
base.css | Reset, typography defaults, focus ring, scrollbars, keyframes, reduced-motion guard |
components.css | Every shared primitive (buttons, inputs, tables, modals, settings rows, …) |
app.css | Entry 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-motiondisables it. - Cheap to render. No
backdrop-filter(expensive in WebKitGTK, especially over the terminal’s WebGL canvas), notransition-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.
| Token | Role |
|---|---|
chrome | Title bar and sidebar: the frame around the workspace |
surface | Workspace / view background |
panel | Cards, tables, settings groups |
surface-raised | Popovers, menus, modals, toasts |
surface-input | Inputs, selects, search boxes |
surface-hover / surface-active | Row hover / selected row, active nav item |
surface-terminal | xterm background |
border / border-subtle / border-hover / border-strong | Hairlines, from quietest to strongest |
border-focus | Focused control border and focus ring |
primary / secondary / muted / faint | Text, from content down to disabled/decorative |
accent / accent-hover / accent-muted / on-accent | Primary action, selection, focus; text on accent fills |
success / warning / error (running, danger aliases) | State only: never decoration |
overlay | Modal 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 UI | Character |
|---|---|---|
zinc | Graphite | Neutral graphite, periwinkle accent (default) |
cyberpunk | Midnight | Deep navy, cyan accent |
oled | OLED black | True black, white accent |
slate | Slate | Cool 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
| Group | Classes | Notes |
|---|---|---|
| Buttons | .btn + -primary -secondary -ghost -danger -danger-ghost; .btn-sm (28px) .btn-lg (36px); .btn-icon .btn-icon-sm .btn-icon-danger | Default height 32px. Icon buttons need aria-label. |
| Inputs | .input .input-mono .input-invalid, textarea.input; .search-box > input | Focus shows the border-focus border plus an accent-muted ring |
| Fields | .field .field-label .field-hint .field-error | Validation messages sit inline under the control |
| Toggles | input.switch (on/off settings), input.checkbox (selection) | Native checkboxes, restyled |
| Select | CustomSelect component (label, size="sm"|"md") | Keyboard navigable listbox |
| Badges | .badge + -neutral -accent -success -warning -error, .badge-mono; .count; .tag; .kbd | Badges 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-actions | Actions 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 -action | One sentence of description, at most two actions |
| Modal | ModalShell component → .modal-overlay .modal-panel; .modal-header .modal-title .modal-subtitle .modal-close .modal-body .modal-footer | Footer: 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-value | Rows 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-errorto a toast when the problem is in a form field. - Relative times (“5 min ago”) in lists, with the absolute date in the
titletooltip (formatRelative/formatDateTimeinlib/utils/timezone.ts).
Accessibility
- Every interactive element is a real
<button type="button">, link or input, and gets the global:focus-visiblering. - Icon-only buttons have
aria-label. Toggles and segmented controls exposearia-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.