Portable SSH manager with a local encrypted SQLite vault, a Cobra CLI, and a tcell TUI.
- Local-first vault lifecycle:
vault init|keychain enable|keychain disable|keychain status|migrate|change-password - Build metadata reporting with
version - Self-install command:
install [--dir PATH] - CRUD for
host,group,profile,identity,key,forward,workspace - Saved host key inspection with
known-host list|delete - Resolved host inspection with
inspect <host> - SFTP browsing and file transfer via
sftp ls|stat|get|put|mkdir|rm|rename - OpenSSH import via
import openssh --config ~/.ssh/config - Termius local export/import via
termius export|import - Termix backend registration via
backend termix add|list|show - Host-level direct key/password auth overrides
- SSH connect flow with:
- password, private key, and ssh-agent auth
- built-in known-host storage in the vault by default, with optional file fallback
ProxyJumpchains- outbound
SOCKS5/HTTP CONNECTproxies - saved and one-shot
-L/-R/-Dforwards - SSH-style debug verbosity via
-v,-vv,-vvv - remote command execution via
exec <host> <command>
- TUI CRUD tabs for
host,group,profile,identity,key,forward,workspacelayouts,known-host, plus split SSH workspace and SFTP tabs
$env:GOPROXY='https://goproxy.cn,direct'
go test ./...
go build ./...nermius version prints a build string in the form v{major}.{minor}.{patch}-{branch}-{commit}[-dirty].
CI derives the base v{major}.{minor}.{patch} from VERSION: patch is VERSION's patch plus the number of commits since VERSION was last changed. To reset patch for a new major/minor line, edit VERSION to the new epoch version, such as v0.1.0; that commit builds as v0.1.0, and the next commit builds as v0.1.1.
# 1. Initialize the local encrypted vault.
nermius vault init
# 2. Optionally inspect or enable system keychain enrollment.
nermius vault keychain status
nermius vault keychain enable
nermius vault keychain enable --require-presence
# 3. Optionally install the current binary into ~/.local/bin.
nermius install
# 4A. If you already have ~/.ssh/config, import it into the vault.
nermius import openssh --config ~/.ssh/config
# 4B. Or create records interactively instead of importing.
nermius key add -it
nermius identity add -it
nermius host add -it
nermius host add --title prod --hostname prod.example.com --identity ops --key deploy-key
# 5. Inspect the resolved host config before connecting.
nermius host list
nermius inspect my-host
nermius known-host list
nermius version
# 6. Open a shell or run a one-shot remote command.
nermius connect my-host
nermius -vv connect my-host
nermius exec my-host hostname
nermius forward start prod-db
nermius sftp ls my-host:/var/log
nermius sftp get my-host:/var/log/syslog .\syslog
# 7. Open the TUI for browsing objects, split SSH workspaces, and SFTP.
nermius tuiimport openssh and the interactive add -it flow are alternative ways to populate the vault. Use whichever matches your setup.
If you are moving from Termius Desktop on Windows, export every locally decryptable object before importing the normalized credentials:
nermius termius export --out termius-export.json --include-secrets
nermius termius import --file termius-export.jsonIf you have a self-hosted Termix instance, register it as an optional backend and bind future sync imports to a local profile:
nermius backend termix add lab --url http://localhost:8080 --token tmx_xxx --profile termix-lab --create-profile
nermius backend termix listNormal read/write commands now auto-unlock:
- first via the enrolled system keychain backend
- otherwise by prompting for the master password
There is no plaintext local session file anymore. On migrated/current vaults, object bodies and secrets are stored in encrypted records; legacy vaults must be upgraded explicitly with nermius vault migrate.
Inside the TUI object tabs:
HOST / GROUP / PROFILE / IDENTITY / KEY / FORWARD / LAYOUT / KNOWN-HOSTall support in-screen CRUD modalsEnteropens the selectedHOSTin the splitWORKSPACE, opens the selectedLAYOUT, toggles the selected tunnel onFORWARD, and opens a read-only detail view on every other tabdopens detail,eopens edit,aopens a create form- from
HOST,F7adds the selected host as a split-right workspace pane andF9adds it as a split-down workspace pane sopens a dual-pane SFTP browser for the selectedHOST;[assigns the selectedHOSTto the left SFTP pane and]assigns it to the right paneDeleteorxopens delete confirmation; referenced objects are blocked and show who still depends on them/opens a filter prompt for the current tab, andrreloads the lists- reference fields use searchable pickers, and ordered lists such as profiles, forwards, known-host host patterns, and identity auth methods use a dedicated list editor
FORWARDobjects are reusable-L/-R/-Dtunnel definitions with their own Host picker;EnterorSpacestarts/stops the selected tunnel for the lifetime of the current TUI process,rreconnects a disconnected forward, and the list showsSTATUSplus a separateREASON
Inside the TUI WORKSPACE view:
- connection prompts such as unknown host-key trust, password, username, and key passphrase stay inside the TUI as modals
- connection progress is shown in the status bar while resolving hosts, dialing, checking known hosts, authenticating, and opening the remote shell
- each workspace pane is an empty, pending, connected, disconnected, or reconnecting SSH terminal pane; SFTP remains a separate full-screen dual-pane tool
F7splits the focused pane to the right,F9splits it downward,F6focuses the next pane,Alt+Arrowmoves focus by direction,Ctrl+Alt+Arrowresizes the nearest split, andF11zooms/unzooms the focused paneF5picks a host for the focused empty pane; pressingEnteron a pending pane connects itF4saves the current split tree as a namedLAYOUT;F3opens a saved layout, and saved layouts restore pane shape and host refs as pending connections- plain terminal keys, including arrow keys and
q, are sent to the focused SSH pane; useF2or click a tab to leaveWORKSPACE, and useF10to quit Nermius - closing an active pane or quitting while SSH resources are open asks for confirmation to avoid accidental disconnects
- if an embedded SSH session disconnects unexpectedly, the TUI keeps the pane and scrollback, asks whether to reconnect, and retries up to 5 times after confirmation; press
ron a disconnected pane to try again - use the mouse wheel to scroll local shell history
- use
Shift+wheelto force local scrollback when the remote app has mouse tracking enabled - drag to select visible terminal text, including locally scrolled shell history
- use
Shift+dragto force local selection when the remote app has mouse tracking enabled - use
Ctrl+Shift+CandCtrl+Shift+Vfor local copy/paste - alt-screen apps stay isolated from local shell scrollback
- remote
OSC 52, focus reporting, bracketed paste, and cursor-shape changes are forwarded when the remote app enables them
Inside the TUI SFTP view:
- each pane can be empty, local, or a remote host;
sfromHOSTopens the quick layout with Local left and the selected remote host right - from
HOST,[assigns the selected host to the left SFTP pane and]assigns it to the right pane; replacing a non-empty pane asks for confirmation - in SFTP,
Tabswitches panes, arrow keys wrap at the top/bottom of the active pane,lmakes an empty active pane local,/searches the active pane,Enterenters directories,Backspacegoes to the parent directory,gjumps to a typed path, andrrefreshes uuploads from the local pane to the remote pane, andddownloads from the remote pane to the local pane; Local/Local and Remote/Remote transfer are not supported yet- remote
ncreates a directory,xorDeleteremoves a file or recursively removes a directory after confirmation, andRrenames a remote path - file overwrite and remote delete actions require confirmation; v1 transfers regular files only, not whole directories
- SFTP does not auto-reconnect or resume transfers yet; connection-class failures are reported as disconnected so the pane can be reopened deliberately
Typical interactive path:
nermius vault init
nermius vault keychain status
nermius key add -it
nermius identity add -it
nermius host add -it
nermius inspect my-host
nermius known-host list
nermius connect my-hostIf you created a vault with an older Nermius build, migrate it once before using regular commands:
nermius vault migrateHost records can also pin how host keys are stored and read:
# Keep host keys entirely inside the encrypted vault.
nermius host add --title prod --hostname prod.example.com --identity ops --known-hosts-backend vault
# Read from the vault first, then fall back to ~/.ssh/known_hosts.
nermius host add --title prod --hostname prod.example.com --identity ops --known-hosts strict --known-hosts-backend vault+fileHosts can also override the selected identity's auth methods directly:
# Reuse the identity username, but force this host to try a specific key first.
nermius host add --title prod --hostname prod.example.com --identity ops --key deploy-key
# Attach a host-specific password override.
nermius host add --title breakglass --hostname prod.example.com --identity ops --password super-secretnermius install
nermius install --dir ~/bin
nermius install --yesinstall copies the currently running executable into ~/.local/bin by default.
- If the target directory does not exist, it asks whether to create it.
- It reports whether that directory is already present in
PATH. - If a file with the same name already exists there, it compares size plus
SHA-256andSHA-512before deciding whether a copy is needed. - If the install directory is not reachable from
PATH, it prints a shell-specific hint for adding it.
Linux release assets also include unsigned nFPM packages. Replace amd64 with the package architecture you downloaded:
sudo dpkg -i nermius-linux-amd64.deb
sudo rpm -Uvh nermius-linux-amd64.rpm
sudo apk add --allow-untrusted nermius-linux-amd64.apk
sudo pacman -U nermius-linux-amd64.pkg.tar.zst
sudo opkg install nermius-linux-amd64.ipkThese packages install nermius to /usr/bin/nermius. Release assets do not include apt, yum, apk, pacman, or opkg repository indexes yet.
vault init still bootstraps the vault with a master password. That master password derives a KEK which wraps the random vaultKey; the vaultKey is what encrypts the actual vault records.
Useful vault commands:
nermius vault keychain status
nermius vault keychain enable
nermius vault keychain enable --require-presence
nermius vault keychain disable
nermius vault change-password
nermius vault migrateBehavior notes:
vault keychain enablestores the raw vault unlock material in the platform backend when supported.- by default, enrolled keychain unlocks do not require Windows Hello or another user-presence prompt, which keeps tests and normal commands non-interactive.
- on Windows,
vault keychain enable --require-presenceenrolls a Windows CNG high-protection key instead of the default DPAPI blob. The CNG private key is non-exportable, requires OS-mediated authorization when used, and wraps the vault unlock material with RSA-OAEP-SHA256. - switching between default keychain enrollment and
--require-presencere-enrolls the vault and removes the other local keychain blob. - normal commands auto-try the enrolled keychain backend first, then fall back to the master password.
vault keychain statusreports both the unlock-material backend and the user-presence backend.- on Windows, default unlock material is DPAPI-protected.
--require-presencemoves the protection boundary into Windows CNG, so changing Nermius' SQLite metadata alone cannot silently downgrade the existing strong enrollment into a DPAPI unlock. - if keychain unlock fails but the master password unlocks the vault, interactive CLI commands explain the keychain failure and offer to disable that local enrollment. This is equivalent to running
nermius vault keychain disable; it does not rewrite vault records or remove master-password access. - a malicious same-user process may still invoke Nermius or Windows APIs and cause an OS authorization prompt, deny access by deleting local key material, or wait for the user to approve a prompt. The strong mode is meant to prevent silent vault-key unwrap, not to defend against a fully compromised user session.
- current-schema vaults use whole-vault encrypted records, so hostnames, usernames, labels, known-host payloads, and secrets are no longer stored as plaintext rows in SQLite.
vault migrateis explicit and creates a backup at<vault>.bak.pre-schema-v2before converting a legacy vault.
The SQLite vault file is portable. The system keychain enrollment is not portable and must be recreated on each machine.
-
On the old machine, find the vault path:
nermius vault keychain status
The default path is usually
%APPDATA%\nermius\vault.dbon Windows,~/Library/Application Support/nermius/vault.dbon macOS, and~/.config/nermius/vault.dbon Linux. -
Copy
vault.dbto the new machine. -
On the new machine, either place it at the default path or pass it explicitly:
nermius --vault C:\path\to\vault.db vault keychain enable -
Enter the master password once to enroll the copied vault into the new machine's keychain.
After that, normal commands can auto-unlock through the new machine's keychain and fall back to the master password if needed.
Notes:
- imported private keys, passwords, identities, profiles, hosts, forwards, and vault-backed known hosts move with
vault.db. - agent-backed auth still depends on the new machine's SSH agent.
- external key file paths and
~/.ssh/known_hostsfile-backed entries are local machine state and must exist separately on the new machine.
nermius no longer depends on the system ssh client for host key storage. By default, resolved hosts use known_hosts.backend = vault+file, which means:
- existing host keys are looked up in the vault first
- then
~/.ssh/known_hostsis checked as a fallback - newly accepted keys are written back into the vault
Useful commands:
# Inspect all saved host keys from both backends.
nermius known-host list
# Only inspect the vault-backed host keys.
nermius known-host list --source vault
# Remove one host key by host, fingerprint, or file:<line> ID.
nermius known-host delete 192.168.1.202You can override storage per host or profile through the known_hosts object:
{
"policy": "strict",
"backend": "vault",
"path": "~/.ssh/known_hosts"
}Use backend: "vault" if you want the host to stay fully self-contained on machines that do not even have OpenSSH installed.
nermius backend termix add stores a Termix URL plus API token as a local backend document. The token is moved into the encrypted secret store and the saved backend can be bound to a local profile with --profile; pass --create-profile to create that profile during registration.
# Validate /health and /users/me before saving.
nermius backend termix add lab --url https://termix.example.com --token tmx_xxx --profile termix-lab --create-profile
# Save first when networking, reverse proxy, or certificate setup is not ready yet.
nermius backend termix add lab --url http://localhost:8080 --token tmx_xxx --no-validate
nermius backend termix show lab
nermius backend termix listThis is the first Termix integration slice: it models the remote backend and the local mapping boundary. Actual pull, seed, and two-way sync commands are intentionally separate future steps. Imported/synced objects will use external.backend_ref plus the remote Termix object kind and ID, while backend.target_profile_ref keeps Termix-owned hosts grouped under the selected local profile without putting URL or credentials directly in the profile.
nermius termius export reads the local Termius Desktop data directory, decrypts every locally decryptable payload, and writes a bundle with both parsed raw_objects and a normalized credentials view. Chromium IndexedDB records are read with an idb_cmp1-compatible comparator and decoded object-store metadata, so field-level encrypted values keep their record boundary, Termius table name in raw_objects[].store_name, and recoverable field name in raw_objects[].field. JSON payloads are parsed as objects or scalar values. Non-JSON decrypted text is still preserved and classified as passwords, passphrases, private keys, public keys, snippet scripts, secret text, or generic text. The bundle also includes a Termius file backup section so data that is not yet understood by Nermius is still preserved.
# Inventory only: no decrypted secret values are written.
nermius termius export --out termius-inventory.json
# Full export: includes raw JSON values, the Termius localKey, and base64 copies of Termius data files.
nermius termius export --out termius-export.json --include-secrets
# Maximum fidelity backup: keep raw objects only, even if Nermius does not understand them yet.
nermius termius export --out termius-raw.json --raw-only --include-secrets
# Import normalized credentials into the encrypted Nermius vault.
nermius termius import --file termius-export.json
# Read local Termius and import directly without writing a plaintext bundle.
nermius termius import --from-local --dry-run
nermius termius import --from-localThe full export file is sensitive plaintext. Treat it like a password manager export and remove it after importing unless you intentionally keep it as a backup. The backup section contains IndexedDB/file__0.indexeddb.leveldb, Local Storage/leveldb, Cache/Cache_Data, Preferences, window-state.json, and the Termius localKey when --include-secrets is set.
Current Termius local key reading is implemented only for Windows Credential Manager and looks for the Termius localKey. Linux and macOS local Termius export, and termius import --from-local, are not supported yet. You can override the Termius data root or pass a specific LevelDB directory with --source-dir; the default is %APPDATA%\Termius on Windows. Close Termius before exporting for the cleanest backup. If Termius is still running, Nermius copies LevelDB directories to a temporary location before trying to read them.
Import only writes normalized credentials that Nermius can map safely today: hosts, groups, identities, private keys, and passphrases. Export reconstructs these from Termius stores such as hosts, groups, tags, ssh_identities, keys, and snippets using Termius' own encrypted field names. Standalone decrypted secrets without a recovered store and field remain in raw_objects as secret_text candidates instead of being guessed into password fields. Port forwards, snippets, activity history, known hosts, and unknown Termius objects remain preserved in raw_objects and the backup section so future import logic can be improved without re-reading Termius.
connect, exec, and tui accept a global verbosity flag modeled after ssh:
nermius -v connect my-host
nermius -vv connect my-host
nermius -vvv exec my-host "uname -a"-v prints high-level SSH decisions such as host key acceptance. -vv adds host key backend details and preferred host key algorithms. -vvv also includes low-level auth method selection.
SFTP commands reuse the same host resolution and SSH transport as connect and exec, including profiles, identities, private keys, agent auth, known-host policy, ProxyJump chains, outbound proxies, and vault prompts. Remote paths use <host>:<path>:
nermius sftp ls prod:/var/log
nermius sftp stat prod:/var/log/syslog --json
nermius sftp get prod:/var/log/syslog .\syslog
nermius sftp put .\deploy.zip prod:/tmp/deploy.zip
nermius sftp mkdir prod:/tmp/releases -p
nermius sftp rename prod:/tmp/deploy.zip /tmp/releases/deploy.zip
nermius sftp rm prod:/tmp/releases/deploy.zip
nermius sftp rm prod:/tmp/releases --recursiveget and put transfer regular files. Existing destinations prompt before overwrite when running in an interactive terminal; pass --force for non-interactive overwrites. rm prompts before deletion unless --force is set, and directories require --recursive.
Saved FORWARD objects are standalone tunnels. Each one has a host_ref, which is the SSH transport/intermediate host used to establish the tunnel.
nermius forward add -it
nermius forward start prod-dbforward start runs in the foreground and keeps the tunnel open until Ctrl+C. If an established CLI tunnel disconnects unexpectedly, Nermius retries up to 5 times before surfacing the final error. TUI FORWARD toggles are process-local; unexpected disconnects are kept in the list, ask before reconnecting, and retry up to 5 times after confirmation. Saved Host/Profile.forward_ids remain visible in resolved config for organization and inspection, but saved forwards no longer auto-start when opening a shell. One-time CLI forwards still work with connect/exec:
nermius connect my-host -L 15432:db.internal:5432
nermius connect my-host -D 1080Host:
{
"title": "prod",
"hostname": "prod.example.com",
"profile_ids": ["profile-uuid"],
"identity_ref": "identity-uuid",
"key_ref": "key-uuid",
"password": "super-secret",
"forward_ids": ["forward-uuid"]
}Forward:
{
"name": "prod-db",
"host_ref": "host-uuid",
"type": "local",
"listen_host": "127.0.0.1",
"listen_port": 15432,
"target_host": "db.internal",
"target_port": 5432,
"enabled": true
}Workspace layout:
{
"name": "ops",
"root": {
"split": {
"axis": "horizontal",
"ratio": 0.5,
"first": {
"pane": {
"type": "ssh",
"host_ref": "prod-host-uuid",
"title": "prod"
}
},
"second": {
"pane": {
"type": "ssh",
"host_ref": "db-host-uuid",
"title": "db"
}
}
}
}
}Saved workspace layouts restore pane shape and host references as pending SSH panes; they do not persist remote shell processes, scrollback, or SFTP state.
Identity:
{
"name": "ops",
"username": "ubuntu",
"methods": [
{
"type": "key",
"key_id": "key-uuid"
},
{
"type": "agent"
}
]
}