Skip to content

Sn0wAlice/sshm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

99 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SSHM

A fast, modern SSH + container manager for your terminal.


SSHM is a TUI & CLI tool written in Rust to manage and connect to SSH hosts, Docker containers, Incus instances, and Kubernetes pods — all from one keyboard-driven interface.

Built for developers, sysadmins, pentesters, and homelab folks who live in a terminal.

What's in the box

Hosts (SSH)

  • Host management — add, edit, delete, tag, organize into nested folders
  • Clone hosty duplicates the selected host (tunnels included) and drops you straight into the editor
  • Fuzzy search + prefix filterstag:prod host:10.* user:ubuntu, fzf-style scoring
  • Tunnels — saved per-host port forwards (local -L, remote -R, dynamic SOCKS -D); start them in the background and watch / stop them from the t dashboard
  • Multi-hop ProxyJumpbastion1,bastion2, each entry resolves against your saved hosts automatically
  • Identity management — push SSH public keys, generate new keys (ed25519, ed25519-sk FIDO2, ecdsa, rsa), load into ssh-agent
  • ForwardAgent (-A) per host — opt-in with a visible warning, badged in the list
  • Mosh per host — opt-in toggle; connects via mosh instead of ssh, forwarding port / identity / ProxyJump automatically
  • Per-host notes — free-text reminder shown in the detail panel
  • Hardware key detection[HW] badge for *-sk keys
  • Frecency sort + Recently Useds cycles name → MRU → most-used → favorites → frecency
  • Group by tagg toggles between folder view and tag view
  • Bulk actionsSpace selects, T adds tags to selection, D deletes, C clears
  • Fan-outX runs one command on every selected host over SSH, with per-host output and an ok/failed summary
  • Quick connect1-9 connects to the Nth visible host
  • Health probes — periodic TCP + SSH banner check, latency in ms, banner version (OpenSSH_9.6) shown inline

Kluster — Docker, Incus, k8s/k3s

A dedicated tab between Hosts and Identities to manage containers and pods:

  • Docker (local) — auto-detected if docker is on PATH and the daemon is up
  • Docker (remote) — pick any saved SSH host, sshm sets DOCKER_HOST=ssh://... and tunnels everything natively. No port to open, no TLS, no socket setup
  • Incus (local) — auto-detected, lists containers and VMs
  • Incus (remote) — auto-imported from incus remote list
  • Kubernetes / K3s — auto-imported from every context in ~/.kube/config and $KUBECONFIG
  • One Enter to shell into any container / pod / instance — /bin/sh directly, no bash dance
  • One l to follow logsCtrl+C returns to the TUI cleanly (no app exit)
  • Lifecycle controls starts/stops and R restarts Docker containers and Incus instances right from the list
  • Pod cleanupd on a Succeeded/Failed pod runs kubectl delete pod
  • Section folding — clusters collapsed by default, Enter on a header toggles
  • Live filter/ fuzzy-filters containers, pods and instances across every section (force-expands while filtering)
  • Live discovery — background worker polls every kluster_refresh_secs (configurable in Settings)

Quality of life

  • i18n — UI strings translatable; English + French bundled. Pick via SSHM_LANG=fr
  • Themes — fully customizable colors via theme.toml, with an optional transparent background that uses the terminal's own
  • Toast notifications — non-intrusive feedback for actions
  • Desktop notifications — native OS alerts (notify-send / osascript) when a background tunnel drops or a host changes reachability
  • Open in a new terminalo launches the SSH session in a separate terminal window (auto-detected, or set external_terminal)
  • Auto-export — optionally writes a clean ~/.ssh/config on every save
  • CLI mode — scriptable commands for automation

Installation

Homebrew (macOS / Linux)

brew tap Sn0wAlice/sshm https://github.com/Sn0wAlice/sshm
brew install sshm

Download pre-built binary

Grab the latest binary from the Releases page.

Linux (amd64)

curl -sL https://github.com/Sn0wAlice/sshm/releases/latest/download/sshm-linux-amd64.tar.gz | tar xz
sudo mv sshm /usr/local/bin/

Linux (arm64)

curl -sL https://github.com/Sn0wAlice/sshm/releases/latest/download/sshm-linux-arm64.tar.gz | tar xz
sudo mv sshm /usr/local/bin/

macOS (Apple Silicon)

curl -sL https://github.com/Sn0wAlice/sshm/releases/latest/download/sshm-darwin-arm64.tar.gz | tar xz
sudo mv sshm /usr/local/bin/

Build from source

git clone https://github.com/Sn0wAlice/sshm.git
cd sshm
cargo build --release
sudo cp target/release/sshm /usr/local/bin/

Requirements:

  • Rust stable toolchain (build only)
  • ssh client (always)
  • docker CLI on PATH (for the Docker section of the Kluster tab — local and remote)
  • kubectl on PATH (for k8s/k3s clusters)
  • incus CLI on PATH (for Incus instances)
  • A terminal with UTF-8 & ANSI support

The Kluster tab degrades gracefully — sections show (unavailable) when the corresponding CLI / daemon isn't reachable.

Usage

TUI (recommended)

sshm

The TUI has 6 tabs ( / to switch):

Tab Purpose
Hosts SSH host list with folders, tags, tunnels, identity management
Kluster Docker / Incus / k8s containers and pods
Identities Local SSH keys (~/.ssh), generate, push, load into agent
Settings Defaults, health-check intervals, kluster refresh, etc.
Theme Pick / customize TUI colors
Help In-app help

Kluster Docker remote — quickstart

  1. Add an SSH host in the Hosts tab pointing at a machine where Docker runs.
  2. Make sure your SSH user is in the docker group on that host (ssh user@host docker ps should work).
  3. In the Kluster tab, navigate to the Docker (local) header and press n.
  4. Pick the host from the list. Done — sshm tunnels every docker call over SSH.

No ports opened, no TLS to set up, no dockerd socket exposed.

CLI commands

sshm list [--filter "expr"]              # list hosts (filter: tag:foo host:1.* user:bar name:*xyz*)
sshm connect <name> [ssh-options...]     # connect to a host (alias: c)
sshm create                              # interactively create a host
sshm edit                                # edit an existing host
sshm delete                              # delete a host
sshm tag add <name> <tag1,tag2>          # add tags
sshm tag del <name> <tag1,tag2>          # remove tags
sshm load_local_conf                     # import hosts from ~/.ssh/config
sshm export [path]                       # export DB as ~/.ssh/config format
sshm add-identity <name?> [--pub key]    # push pubkey to authorized_keys
sshm help                                # full CLI reference

Keyboard shortcuts

Global

Key Action
/ Switch tabs
q Quit

Hosts tab — list navigation

Key Action
/ Navigate
Enter Connect to host / expand-collapse folder
/ or any letter Activate fuzzy filter
1-9 Quick-connect to Nth visible host
s Cycle sort mode (name / MRU / most used / favorites / frecency)
g Toggle group-by-folder ⇆ group-by-tag
f Toggle favorite on selected host
c One-shot health check on selected host

Hosts tab — actions

Key Action
a Add a host (or folder when on a folder row)
e Edit selected host
y Clone selected host (full copy, opens the editor)
d Delete selected host / folder
p Open port-forward menu — start a tunnel in the background (f runs it foreground)
t Background-tunnels dashboard — d/x stop a tunnel, o open a local tunnel's URL
o Open the SSH session in a new terminal window
i Push identity to selected host
r Rename folder
Space Toggle host in bulk selection
T (Shift+t) Bulk-add tags to selected hosts
D (Shift+d) Bulk-delete selected hosts (with confirm)
C (Shift+c) Clear bulk selection
X (Shift+x) Fan-out: run a command on every selected host

Kluster tab

The available actions depend on what's under the cursor.

Key When Action
/ j/k always Navigate
/ always Fuzzy-filter containers / pods / instances (Esc clears)
Enter on a header Expand / collapse the section
Enter on a container / pod / instance Open /bin/sh (Ctrl+D to exit)
l on a container / pod / instance Stream logs -f (Ctrl+C returns to TUI)
s on a Docker container / Incus instance Start it if stopped, stop it if running
R (Shift+r) on a Docker container / Incus instance Restart it
r always Force a refresh now
n on a Docker header Pick a saved host → register a Docker remote
n elsewhere Add a new k8s/k3s cluster (TUI form)
e on a Cluster header Edit cluster (kubeconfig / context / namespace)
d on a Cluster header Unlink cluster from sshm (cluster itself untouched)
d on a Docker remote header Unlink Docker remote (host still in Hosts tab)
d on a Succeeded / Failed pod kubectl delete pod (with confirm)

Identities tab

Key Action
/ Navigate keys in ~/.ssh
/ Fuzzy-filter keys by file name / type / comment (Esc clears)
g Generate a new key (interactive: ed25519 / ed25519-sk / ecdsa / rsa)
p Push selected pubkey to a host
a Add selected key to ssh-agent
x Remove selected key from ssh-agent
K (Shift+k) Clean a hostname from ~/.ssh/known_hosts
r Rescan ~/.ssh

Configuration

Files

Path Purpose
~/.config/sshm/host.json Hosts, folders, tunnels, ProxyJump
~/.config/sshm/kluster.json Saved clusters + Incus remotes + Docker remotes
~/.config/sshm/settings.toml Defaults, health & kluster intervals
~/.config/sshm/theme.toml TUI color theme (optional)
~/.config/sshm/tunnels/<pid>.json Live background tunnels per running instance — used to clean up after a crash

Settings

The Settings tab (Tab → Settings) exposes:

  • Default Port / Default Username / Default Identity File — used when creating new hosts
  • Export Path — where to write the auto-exported ~/.ssh/config (empty = disabled)
  • Auto Health Check — toggle the background SSH probe
  • Health Refresh / Cache TTL — seconds between probe rounds
  • Probe Connect Timeout — TCP connect timeout in ms (banner read uses ~1/3)
  • Kluster Refresh Interval — seconds between Docker / kubectl / Incus refreshes
  • Kluster Log Tail — default --tail N for l (logs)
  • Desktop notifications — toggle native OS alerts (tunnel dropped, host up/down)

The Settings tab groups these into labelled sections (Defaults, Export, Health checks, Kluster, Notifications).

All values are live: hit Save and the background workers pick up the new TTL on the next tick.

external_terminal — a settings.toml-only key (not shown in the Settings tab). It's the command prefix used by the o hotkey to open a session in a new terminal window; the SSH command is appended to it. Leave it empty to auto-detect (wezterm, kitty, alacritty, gnome-terminal, konsole, xterm, or Terminal.app on macOS). Examples:

external_terminal = "kitty -e"
external_terminal = "wezterm start --"
external_terminal = "gnome-terminal --"

notification_icon — another settings.toml-only key: a path (~ allowed) to a custom icon for desktop notifications.

notification_icon = "~/.config/sshm/icon.png"

On Linux it's passed straight to notify-send -i. On macOS the default osascript notification cannot override its icon (it's always osascript's) — install terminal-notifier (brew install terminal-notifier) and SSHM will use it automatically to honour the custom icon.

Theme example

bg = "#1e1e2e"
fg = "#cdd6f4"
accent = "#89b4fa"
muted = "#6c7086"
error = "#f38ba8"
success = "#a6e3a1"
transparent_bg = false

Set transparent_bg = true (or tick Transparent background in the Theme tab) to drop the bg colour entirely and let your terminal's own background — including any transparency / blur — show through. The bg hex is kept on disk so unticking the box restores it.

Localization

SSHM_LANG=fr sshm     # French
SSHM_LANG=en sshm     # English (default)

Falls back to the value of LC_ALL / LANG if SSHM_LANG is unset. Unknown locales fall back to English silently.

Architecture

src/
├── main.rs               # CLI dispatch
├── lib.rs                # crate root
├── models.rs             # Host, Tunnel, Database
├── history.rs            # frecency, sort modes
├── i18n.rs               # localization
├── locales/              # en.toml, fr.toml
├── filter/               # fuzzy + prefix-token matcher
├── config/               # io, path, settings, export
├── ssh/                  # client, keys, agent, known_hosts, proxy
├── import/               # ~/.ssh/config parser
├── kluster/              # Docker / Incus / kubectl wrappers
│   ├── docker.rs         #   docker ps / exec / logs (local + DOCKER_HOST=ssh://)
│   ├── incus.rs          #   incus list / exec / logs (local + remotes)
│   ├── kube.rs           #   kubectl get/exec/logs/delete pod
│   ├── shell.rs          #   /bin/sh constant
│   └── db.rs             #   kluster.json + bootstrap from kubeconfig + incus remotes
├── tui/
│   ├── app/              # main loop + worker submodules
│   │   ├── health_worker.rs
│   │   ├── kluster_worker.rs
│   │   ├── kluster_actions.rs
│   │   ├── cluster_form.rs
│   │   ├── host_form.rs
│   │   └── key_flows.rs
│   ├── tabs/             # one file per tab
│   ├── ssh/              # host detail box, modals, toast, port forward
│   └── theme.rs
└── commands/             # CLI subcommands

Contributing

PRs welcome — especially for:

  • Terminal UX polish
  • New runtime backends (LXD, Podman, ...)
  • Platform support (Windows is currently best-effort)
  • More translations (just drop a src/locales/<code>.toml)

Run cargo test before sending a PR — the suite covers parsers (filter, kubeconfig, ssh_config, JSON migrations) and a handful of pure logic units (frecency, ssh banner, ProxyJump resolver, etc.).


Made by Sn0wAlice

About

sshm is a command-line tool written in Rust that makes it easy to manage a list of SSH hosts. It allows you to list, create, edit, delete, and connect to SSH hosts through an interactive terminal interface.

Topics

Resources

License

Stars

4 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages