A drop-in Rust replacement for sshpass with secure password storage via macOS Keychain and 1Password.
# Install
cargo install sshpassx
# Store a password once, use it everywhere
sshpassx --store user@host # prompts for password, saves to Keychain
sshpassx -k ssh user@host # looks up stored password automatically
sshpassx -k scp user@host:f . # works with scp, rsync, sftp, etc.
# Or pass a password directly (original sshpass style)
sshpassx -p mypass ssh user@hostUsing 1Password instead of macOS Keychain?
export SSHPASSX_BACKEND=op
sshpassx --store user@host
sshpassx -k ssh user@host# From crates.io
cargo install sshpassx
# Or from source
cargo install --path .
# Or build manually
cargo build --release
# Binary at: target/release/sshpassxsshpassx supports two password storage backends, controlled by environment variables:
| Variable | Values | Description |
|---|---|---|
SSHPASSX_BACKEND |
op, 1password |
Use 1Password instead of macOS Keychain. Unset = macOS Keychain. |
SSHPASSX_VAULT |
vault name | 1Password vault to use (optional, defaults to personal vault) |
Backward compatibility: The legacy
SSHPASS_RS_BACKENDandSSHPASS_RS_VAULTenvironment variables are still accepted as fallbacks.
macOS Keychain (default) — passwords are stored in the system Keychain, encrypted at rest, protected by your login password.
1Password — passwords are stored as tagged items in your 1Password vault. Requires the 1Password CLI (op) to be installed and authenticated.
On non-macOS platforms, macOS Keychain is unavailable. Set
SSHPASSX_BACKEND=opto use 1Password instead.
export OP_SERVICE_ACCOUNT_TOKEN=ops_...
export SSHPASSX_BACKEND=op
sshpassx -k ssh user@hostUse -v to see which backend is selected, what commands are executed, and how password resolution proceeds:
sshpassx -v -k ssh user@hostSSHPASSX: checking SSHPASSX_BACKEND environment variable
SSHPASSX: selected OS keychain backend
SSHPASSX: using keychain with key 'user@host'
SSHPASSX: querying backend for key 'user@host'
SSHPASSX: key 'user@host' found in backend
SSHPASS searching for password prompt using match "assword:"
SSHPASS detected password prompt
SSHPASS sending password
sshpassx [OPTIONS] <command> [args...]
sshpassx --store <key>
sshpassx --delete <key>
sshpassx --list
sshpassx --help
Mutually exclusive — only one per invocation.
| Flag | Description |
|---|---|
-p <password> |
Pass the password directly as an argument |
-f <filename> |
Read the password from a file (first line) |
-d <number> |
Read the password from a file descriptor |
-e |
Read the password from the SSHPASS environment variable |
-k |
Look up the password from the configured backend, auto-deriving the key from the SSH command |
No flag = read password from stdin (original sshpass behavior).
Standalone operations — no wrapped command needed.
| Flag | Description |
|---|---|
--store <key> |
Prompt for a password and store it under <key> |
--delete <key> |
Delete the stored entry for <key> |
--list |
List all entries managed by sshpassx |
--key <value> |
Explicit key name for -k (overrides auto-detection) |
| Flag | Default | Description |
|---|---|---|
-P <prompt> |
assword: |
Prompt pattern to match in PTY output |
-v |
off | Verbose mode — diagnostic output to stderr |
-h, --help |
Context-sensitive help (try --store --help, --list --help, -k --help) |
With -k (no --key), the key is derived from the wrapped command using a two-stage fallback:
-
Direct parsing — extracts
user@hostfrom SSH arguments:ssh user@host→ key =user@hostssh -l user host→ key =user@host
-
SSH config resolution — if direct parsing fails, runs
ssh -G <destination>to resolve aliases:ssh myalias→ resolvesUserandHostNamefrom~/.ssh/config→ key =resolved_user@resolved_hostssh -W %h:%p gw→ resolvesgwalias → key =resolved_user@resolved_host- Propagates
-F <config>if present in the SSH arguments
Use --key <name> to override auto-detection entirely.
If both stages fail, you're prompted interactively.
# Direct password
sshpassx -p hunter2 ssh user@host
# From file
sshpassx -f ~/.ssh/mypassword ssh user@host
# From environment variable
SSHPASS=hunter2 sshpassx -e ssh user@host
# From stdin
echo "hunter2" | sshpassx ssh user@host# Store once
sshpassx --store user@host
# Use everywhere
sshpassx -k ssh user@host
sshpassx -k scp user@host:/remote/file ./local/
sshpassx -k rsync -avz user@host:/data/ ./backup/
# Explicit key name
sshpassx --key myserver ssh -l user host
# Manage stored entries
sshpassx --list
sshpassx --delete user@hostexport SSHPASSX_BACKEND=op
# Store in 1Password
sshpassx --store user@host
# Use from 1Password
sshpassx -k ssh user@host
# Use a specific vault
SSHPASSX_VAULT=Production sshpassx -k ssh user@host
# List / delete
sshpassx --list
sshpassx --delete user@hostUse sshpassx as a ProxyCommand in ~/.ssh/config to automate password-based jump hosts. Aliases are resolved automatically via ssh -G.
# First, store the jump host password
sshpassx --store user@gateway.example.com# ~/.ssh/config
# Jump host that requires keyboard-interactive auth
Host gw
Hostname gateway.example.com
User user
PreferredAuthentications keyboard-interactive
PubkeyAuthentication no
# Internal hosts via the jump host — alias "gw" is auto-resolved
Host internal-server1
HostName 10.0.0.1
User admin
ProxyCommand sshpassx -k ssh -W %h:%p gw
GSSAPIAuthentication no
Host internal-server2
HostName 10.0.0.2
User admin
ProxyCommand sshpassx -k ssh -W %h:%p gw
GSSAPIAuthentication no
How it works: sshpassx runs
ssh -G gwto resolve the alias, extractingUserandHostNamefrom your SSH config to construct the keychain keyuser@gateway.example.com. Use--key <name>to override if needed.
# Custom prompt pattern (non-English systems)
sshpassx -p hunter2 -P "Passwort:" ssh user@host
# Verbose diagnostics
sshpassx -v -k ssh user@host| Code | Name | Meaning |
|---|---|---|
| 0 | Success | Command completed successfully |
| 1 | InvalidArguments | Missing required argument |
| 2 | ConflictingArguments | Multiple password sources or bad flags |
| 3 | RuntimeError | PTY, spawn, keychain, or I/O error |
| 4 | ParseError | Could not parse child process output |
| 5 | IncorrectPassword | SSH rejected the password |
| 6 | HostKeyUnknown | Host key not in known_hosts |
| 7 | HostKeyChanged | Host key changed since last connection |
| Property | Original sshpass | sshpassx |
|---|---|---|
Password in args (-p) |
Visible in ps |
Visible in ps (same) |
Password in file (-f) |
Plaintext file | Plaintext file (same) |
Password in env (-e) |
Cleared before exec | Cleared before exec |
| Keychain storage | Not supported | macOS Keychain (encrypted at rest) |
| 1Password storage | Not supported | op CLI (encrypted vault, biometric unlock) |
| In-memory handling | Plain string | SecretString (zeroized on drop) |
Recommendation: avoid -p — the password is visible in ps. Use -k with stored passwords for scripts and automation.
-p,-f,-d,-epassword sources-Pprompt pattern matching-vverbose mode- Exit codes 0–7
- Works with any PTY-based password prompt (
ssh,scp,sftp,rsync, etc.)
-k/--key— backend-backed password lookup--store/--delete/--list— password management- 1Password backend via
SSHPASSX_BACKEND=op - Auto-detection of
user@hostfrom SSH arguments - Interactive fallback when key is missing
- Context-sensitive
--help - Cross-platform backend guard (non-macOS → requires 1Password)
- macOS Keychain backend is macOS-only. On other platforms, use
SSHPASSX_BACKEND=op. - 1Password backend requires the
opCLI. - No config file — all options via command line and environment variables.