Decrypt Termius's local data offline and convert your hosts, SSH keys, port forwards and proxy settings into configuration for other SSH clients.
Termius does not need to be running. No debug port, no launching the app — the tool reads the IndexedDB directly off disk, so it works on a cold backup directory too.
Not affiliated with, endorsed by, or supported by Termius. "Termius" is used here only to identify the application whose data format this tool reads.
Termius's own "Export" pushes your public key to a server. It is not a way to get your data out as files. There is no official path for moving your hosts and keys to another client.
| Format | File | Verification |
|---|---|---|
| OpenSSH client config | sshconfig |
roundtrip |
| Normalized JSON model | export.json |
roundtrip |
| Generic CSV (Termius import template columns) | hosts.csv |
roundtrip |
| Tabby profiles | tabby-profiles.yaml |
upstream-source |
| Termix bulk-import payload | termix-hosts.json |
roundtrip |
| OpenSSH known_hosts | known_hosts |
roundtrip |
Note that Termix can import an SSH config, but its parser only reads Host / HostName /
User / Port / IdentityFile / ProxyJump — proxies, port forwards and all credentials are
dropped. Use termix-hosts.json instead; it carries them.
Private keys are written to keys/. Keys not referenced by any host are not discarded — they
go to keys-unlinked/ with a note explaining that "not referenced" is not the same as "not in
use".
roundtrip means the output was read back and validated by the target's own parser.
upstream-source means fields follow the target client's type definitions in its source code, but
no import round-trip was performed. Writers based purely on guesswork are not shipped — see
CLAUDE.md.
nix develop # or: pip install -e .
python -m termius_export --out outAuto-detects the data directory, reads the decryption key from the OS keyring, emits every format and self-verifies.
On macOS, expect a keychain prompt. The key was written by Termius, so reading it from
anything else raises the system's Allow / Always Allow / Deny dialog, once per keychain entry.
The export waits for it. Choosing Always Allow grants the trust to /usr/bin/security — that
is, to anything on the machine that shells out to it — and is revocable in Keychain Access under
the entry's Access Control tab.
# only some formats, and no plaintext passwords
python -m termius_export --format openssh,tabby --no-secrets
# work from a backup
python -m termius_export --data-dir ~/termius-backup/Termius --out outSee --help for all options.
The decryption key (localKey) normally comes from the OS keyring. Two things can go wrong,
and they need different fixes — the tool tells you which one you hit.
No keyring client installed. On Linux the key is read via secret-tool from
libsecret. If it is missing, install it:
sudo dnf install libsecret # Fedora / RHEL
sudo apt install libsecret-tools # Debian / Ubuntu
sudo pacman -S libsecret # Arch
nix shell nixpkgs#libsecret # nix, no install neededOr bypass the keyring entirely. Read the key yourself and pass it in:
# Linux
secret-tool lookup service termius-app account localKey > localkey.txt
# macOS
security find-generic-password -s Termius -a localKey -w > localkey.txt
chmod 600 localkey.txt
python -m termius_export --local-key-file localkey.txt --out outThe service name is termius-app for snap installs and Termius on macOS — Termius uses its
executable name, which is why looking up "Termius" on a snap install finds nothing. Any keyring
browser (Seahorse, KWalletManager) can show the entry too.
If your machine holds more than one Termius key, which happens on macOS when the App Store and DMG builds have both been installed, the export sorts it out by itself: it tries each entry against the data and keeps the one that decrypts. On macOS that can mean one extra keychain prompt, since every entry it has to try asks for authorization.
It stops only if none of them fit — usually a --data-dir from an install whose key is not in
this keyring. To see what you have, and pass the right one in explicitly:
security dump-keychain | grep -i termius # macOS
secret-tool search --all service termius-app # Linux
python -m termius_export --local-key-file localkey.txt --out outOn Windows the key lives in Credential Manager under the target Termius/localKey. The tool
reads it directly via CredReadW; to confirm the entry exists:
cmdkey /list | Select-String termiuscmdkey lists targets but never prints the blob, so there is no one-line way to dump the key
by hand. If the automatic read fails, report the target name it shows.
localkey.txt is the master decryption key. Delete it once the export is done.
The generated sshconfig points IdentityFile at this export's keys/ directory. Two ways to
adopt it:
Keep it separate and include it. Least invasive — your existing config is untouched:
OUT=$(pwd)/out # wherever you pointed --out
mkdir -p ~/.ssh/config.d
cp "$OUT/sshconfig" ~/.ssh/config.d/termius
cp -r "$OUT/keys" ~/.ssh/termius-keys && chmod 700 ~/.ssh/termius-keys
# repoint IdentityFile at the new key location
sed -i "s|$OUT/keys/|$HOME/.ssh/termius-keys/|g" ~/.ssh/config.d/termius
# Include must come BEFORE any Host block, so prepend it
printf 'Include config.d/*\n' | cat - ~/.ssh/config > ~/.ssh/config.tmp \
&& mv ~/.ssh/config.tmp ~/.ssh/configRelative Include paths resolve against ~/.ssh/, which is why config.d/* works.
Or merge it in. Append out/sshconfig to ~/.ssh/config and fix the IdentityFile paths
the same way.
Then verify a host resolves as expected before trusting the rest:
ssh -G <alias> | head -5
ssh <alias>Other outputs:
known_hosts— merge into~/.ssh/known_hosts, or use with-o UserKnownHostsFile=hosts.csv— import into Termius or another managertabby-profiles.yaml— merge into theprofileslist in~/.config/tabby/config.yamltermix-hosts.json— import from the Termix Hosts panelexport.json— the normalized model, for scripting against. Every host carries anascii_alias, empty unless itsaliasis one somesshrefuses; the key is always present so the schema does not vary per host. Prefer it when you need a name that works everywherekeys-unlinked/— private keys no host referenced; read itsREADME.txtbefore deleting any
- Output directory
0700, private keys0600, processumask 077 - On Windows POSIX modes do not apply:
os.chmodthere can only toggle the read-only attribute, so a0600private key would be protected by nothing. The output directory is restricted withicaclsto the current user's SID and files inherit it. If that fails the run warns rather than letting you assume the keys are protected - Decrypted data exists only in memory; no extra plaintext intermediate files are written
- The tool is read-only and never modifies or deletes Termius data
- Output contains plaintext credentials.
.gitignorecovers every output path, but handle the directory accordingly
- Verified on Linux (snap install), on Windows, and on macOS — both the DMG and App Store builds, on a machine carrying the two side by side. The Windows and macOS runs were real 213-host, 22-key profiles: the key read from the platform credential store, the data directory auto-detected, all six formats emitted and self-checked
- Whether
sshaccepts a non-ASCII alias depends on the C library, not on OpenSSH: glibc takes one under any locale, macOS refuses one under any UTF-8 locale. Since a generatedsshconfigis meant to be portable, hosts whose name is not ASCII get a secondHostpattern that works everywhere —Host 生产服务器 192.0.2.30. The ASCII name comes from the label where something survives stripping and from the address where nothing does, which is what Termius itself displays for a host you never named. The original is kept and listed first, so nothing changes for anyone whose names were already ASCII - Hardware-backed keys (Apple Secure Enclave, Windows TPM) cannot be exported — the private key never leaves the hardware and must be regenerated
- Passphrase-protected PKCS#1 PEM keys (
BEGIN RSA PRIVATE KEYwithProc-Type: 4,ENCRYPTED) are exported normally but cannot be self-verified: that format encrypts the public modulus too, sossh-keygencannot fingerprint them without the passphrase. They are reported as skipped, never as passed. Keys in the modern OpenSSH format are unaffected
Implementation details, the reverse-engineering write-up, the traps to avoid, and how to add a new writer are all in CLAUDE.md.
ZeroP27/termius-local-export — a Node tool that decrypts Termius data by delegating to the application's own bundled native module. This project started from it and then diverged completely: once the cipher format was reverse-engineered there was no need to run Termius at all, which allowed the direct-LevelDB read and the rewrite in Python. No code is shared.
MIT — see LICENSE.