sshvault
sshvault — Back up and restore your SSH keys and SSH config files with Bitwarden.
sshvault keeps a copy of ~/.ssh/keys/*, ~/.ssh/config and ~/.ssh/config.d/*
in your Bitwarden vault, so a new machine can be brought back to a working state
with one command. It is a small, dependency-free replacement for two previous
tools: ssh-manager (keys) and a hand-written config sync script.
$ sshvault status
== keys ==
ok work-ed25519 -
ok personal -
2 key(s) checked, no problems
== config ==
STATUS ITEM FILE
SAME SSHConfig__config config
SAME SSHConfig__config.d__cloud config.d/cloud
Why another one?
- One tool for keys and configs. The vault layout is nearly identical for both, so there is no reason to run two programs.
- Secrets never reach
ps. Payloads are piped tobwon stdin. The originalssh-managerpassed them as argv, where any local user could read your private keys out of the process table. - Nothing is overwritten silently. Every restore backs up the old file first, skips files that already match, and refuses to write anything it cannot verify.
- Config files are syntax-checked before they land. A malformed
~/.ssh/configmeans you cannot log in anywhere;sshvaultrunsssh -G -F <tmp> localhoston the candidate file and aborts if it does not parse.
Requirements
- Linux
- Bitwarden CLI (
bw) on$PATH, logged in ssh,ssh-keygen(andssh-addforkeys load)
Install
pipx install sshvault
or
pip install --user sshvault
Quickstart
# Unlock Bitwarden once; sshvault reuses BW_SESSION.
export BW_SESSION=$(bw unlock --raw)
# See what differs between the vault and this machine.
sshvault status
# Upload local keys and configs.
sshvault keys push
sshvault config push
# On a new machine, pull everything back.
sshvault keys pull
sshvault config pull --allow-new
Commands
sshvault status overview of keys and configs
sshvault keys list local vs vault keys
sshvault keys check keypair hygiene and vault parity
sshvault keys push [--dry-run] [--name N] upload local keys
sshvault keys pull [--dry-run] [--name N] restore keys locally
sshvault keys load [--dry-run] [--name N] add keys to a running ssh-agent
sshvault config status [--json] show config drift
sshvault config push [--dry-run] [--file REL]
sshvault config pull [--dry-run] [--file REL] [--allow-new] [--force]
Exit codes: 0 no drift / all succeeded, 1 drift or partial failure, 2
fatal error. status is read-only and exit-code friendly, so it works in a
shell prompt or a cron check.
Vault layout
Two item conventions, both login items (type: 1) whose notes field holds
base64:
| Item name | notes contains |
|---|---|
SSHKeys__<name> |
base64 of compact JSON [{"name":"public_key","value":…},{"name":"private_key","value":…}] |
SSHConfig__<path> |
base64 of the raw file, where <path> is relative to ~/.ssh with / replaced by __ |
Both formats are inherited from earlier tools and are preserved byte-for-byte, so an existing vault keeps working without migration.
Configuration
Every location is a flag and an environment variable; the flag wins.
| Flag | Environment | Default |
|---|---|---|
--ssh-dir |
SSHVAULT_SSH_DIR |
~/.ssh |
--keys-dir |
SSHVAULT_KEYS_DIR |
<ssh-dir>/keys |
--config-dir |
SSHVAULT_CONFIG_DIR |
<ssh-dir>/config.d |
--main-config |
SSHVAULT_MAIN_CONFIG |
config |
--keys-prefix |
SSHVAULT_KEYS_PREFIX |
SSHKeys__ |
--config-prefix |
SSHVAULT_CONFIG_PREFIX |
SSHConfig__ |
--unlock-cmd |
SSHVAULT_UNLOCK_CMD |
none |
Unlocking
sshvault uses BW_SESSION if it is already set and unlocks. Otherwise it runs
the configured unlock helper, which must print the path of a shell env file
containing BW_SESSION (and optionally BW_SESSION_EXPIRES_AT):
export SSHVAULT_UNLOCK_CMD="my-bw-unlock" # prints /run/user/1000/bw-session.env
The session file is rejected unless it is a regular, non-symlink file owned by
you with mode 0600.
Security notes
- Vault payloads are written to
bwon stdin, never argv. bw list itemsreturns private keys in memory;sshvaultnever prints, logs, or interpolates item contents.- Core dumps are disabled at startup and the tool refuses to run as root
(override with
--allow-rootif you really mean it). - Private keys are written
0600, public keys0644, via a temp file in the same directory plus an atomic rename. - Restores back up the previous file first. Backups of config files are
dot-prefixed on purpose:
~/.ssh/confignormally doesInclude ~/.ssh/config.d/*, and that glob skips leading dots, so a corrupt backup can never be fed to ssh's parser.
Limitations
- Linux only. The code uses
fcntl,resource.setrlimitandos.geteuid. - Bitwarden only. The 1Password and S3 backends of
ssh-managerare not implemented. keys loadneeds a runningssh-agent; if you do not use one, ignore it.
License
MIT. See LICENSE.
Metadata
Release files for sshvault 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sshvault-0.1.0.tar.gz | 23.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sshvault-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 43.7 kB
Release files / sshvault-0.1.0.tar.gz
| Download URL | sshvault-0.1.0.tar.gz |
|---|---|
| Size | 23.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b1ce74f51c6f8b8326b4b0f8351bb04a63052de3ed34ab10ba0511321f9bff9c
|
|
BLAKE2b-256 checksum How to use checksums |
968c4c6257bf6840b2057b3a30ce30d7d42106b72aef7e8dd4fe6debbe93a3c8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / sshvault-0.1.0-py3-none-any.whl
| Download URL | sshvault-0.1.0-py3-none-any.whl |
|---|---|
| Size | 20.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9c820fc855837d79c92bc54ec542452a0b35ae2e6bea27dcf666ee23a1d8ba1b
|
|
BLAKE2b-256 checksum How to use checksums |
6dde40eb087198e1a86c25afc6844686b90ea5208ebfa3bbf17ffb32a4a95c61
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency log