Skip to main content

sshvault

PyPI Release CI MIT

English | 中文

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 to bw on stdin. The original ssh-manager passed 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/config means you cannot log in anywhere; sshvault runs ssh -G -F <tmp> localhost on the candidate file and aborts if it does not parse.

Requirements

  • Linux
  • Bitwarden CLI (bw) on $PATH, logged in
  • ssh, ssh-keygen (and ssh-add for keys 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 bw on stdin, never argv.
  • bw list items returns private keys in memory; sshvault never prints, logs, or interpolates item contents.
  • Core dumps are disabled at startup and the tool refuses to run as root (override with --allow-root if you really mean it).
  • Private keys are written 0600, public keys 0644, 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/config normally does Include ~/.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.setrlimit and os.geteuid.
  • Bitwarden only. The 1Password and S3 backends of ssh-manager are not implemented.
  • keys load needs a running ssh-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)

Source distribution for sshvault 0.1.0
File Size Uploaded
sshvault-0.1.0.tar.gz 23.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sshvault 0.1.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page