ssh-cli 0.3.6

Native Rust CLI that gives LLMs (Claude Code, Cursor, Windsurf) the ability to operate remote servers via SSH over stdin/stdout
Documentation

ssh-cli

crates.io docs.rs MSRV license Contributor Covenant

Give any LLM remote SSH power in one memory-safe one-shot binary.

What is it?

One-shot multi-host SSH CLI for agents

  • Ship a single Rust binary with zero Node runtime and zero daemon.
  • Operate N VPS hosts from XDG storage without .env files.
  • Authenticate with password or private key per host.
  • Execute exec, sudo-exec, and su-exec as pure one-shot processes.
  • Capture stdout and stderr with structured JSON for orchestration.
  • Auto-detect locale between en-US and pt-BR.
  • Disable telemetry completely in every build.

Why ssh-cli?

Replace long-lived SSH processes with a die-after-run binary

  • Avoid resident Node processes that hold sockets open between tasks.
  • Cut RAM and CPU waste from long-lived SSH sessions.
  • Register multi-host credentials once under XDG with atomic writes.
  • Align command packing and dual maxChars semantics with the one-shot agent contract.
  • Trust host keys via TOFU known_hosts instead of always-trust.
  • Route errors with sysexits codes that agents classify reliably.

Superpowers

Capabilities that make agents productive

  • Multi-host CRUD with vps add|list|show|edit|remove|path|doctor|export|import
  • One-shot remote execution with exec, sudo-exec, su-exec
  • Safe sudo packing via sh -c and shell escape
  • Private key auth with optional passphrase
  • Dual limits max_command_chars and max_output_chars
  • Timeout with best-effort remote abort
  • Bounded tunnel via mandatory --timeout-ms
  • SCP upload and download
  • Health-check latency probe
  • Shell completions for bash zsh fish powershell
  • Secrets via stdin flags to avoid argv leaks
  • Default at-rest encryption (ChaCha20-Poly1305) with auto XDG secrets.key
  • Master-key UX: secrets status|init|reencrypt
  • TOFU known_hosts and atomic config writes with flock
  • Install crypto pins for clean cargo install --locked (GAP-014)

Quick Start

Install and run the first remote command

cargo install ssh-cli --locked
ssh-cli secrets init   # optional explicit master-key; auto-created on first secret write
printf %s s3cret | ssh-cli vps add \
  --name prod \
  --host prod.example.com \
  --port 22 \
  --user admin \
  --password-stdin
ssh-cli connect prod
ssh-cli exec prod "hostname" --json

Installation

Choose the install path that matches your environment

  • Prefer crates.io with lockfile: cargo install ssh-cli --locked (after 0.3.6 is published; until then install from git/path for full features).
  • Rebuild from a checkout: cargo install --path . --locked
  • Do not use install without --locked unless you verified the crypto pins resolve cleanly.
  • Force upgrade after a release: cargo install ssh-cli --locked --force
  • Build musl with allocator feature when targeting Alpine: --features musl-allocator
  • Require Rust MSRV 1.85.0 or newer

Usage

Register hosts then execute one-shot commands

  • Default at-rest encryption (ChaCha20-Poly1305): auto secrets.key on first secret write; override via SSH_CLI_SECRETS_KEY / _FILE / keyring; manage with ssh-cli secrets status|init|reencrypt. Opt-out: SSH_CLI_ALLOW_PLAINTEXT_SECRETS=1 (tests only).
  • Prefer --password-stdin / --key over argv secrets.
  • Add password hosts with vps add --password or --password-stdin.
  • Add key hosts with vps add --key ~/.ssh/id_ed25519.
  • Mark active host with connect <name>.
  • Run remote shells with exec <vps> "<cmd>".
  • Elevate with sudo-exec or su-exec when configured.
  • Diagnose XDG paths with vps doctor --json.
  • Export redacted inventory with vps export.

Commands

Product surface for humans and agents

Command Purpose
ssh-cli vps add Register a host (password or key)
ssh-cli vps list [--json] List hosts with secrets masked
ssh-cli vps show <name> [--json] Show one host masked
ssh-cli vps edit <name> Patch host fields
ssh-cli vps remove <name> Delete host
ssh-cli vps path Print config.toml path
ssh-cli vps doctor [--json] Show XDG layer schema and paths
ssh-cli vps export Export hosts (secrets redacted by default)
ssh-cli vps import --file Import hosts from TOML
ssh-cli connect <name> Write sibling active file
ssh-cli exec <vps> <cmd> One-shot remote command
ssh-cli sudo-exec <vps> <cmd> One-shot sudo with safe packing
ssh-cli su-exec <vps> <cmd> One-shot su - elevation
`ssh-cli scp upload download`
ssh-cli tunnel ... --timeout-ms N Bounded local port forward
ssh-cli health-check [<vps>] Connectivity probe
`ssh-cli secrets status init
ssh-cli completions <shell> Shell completion scripts

Environment Variables

Overrides allowed for tests and locales

Variable Description Example
SSH_CLI_HOME Override base config directory /tmp/ssh-cli-test
SSH_CLI_LANG Override locale pt-BR
SSH_CLI_SECRETS_KEY Master key as 64 hex chars (encrypt at rest) (never log)
SSH_CLI_SECRETS_KEY_FILE Path to file with 64 hex master key ~/.config/ssh-cli/secrets.key
SSH_CLI_USE_KEYRING Load/store master key in OS keyring 1
SSH_CLI_ALLOW_PLAINTEXT_SECRETS Opt-out of default encryption (tests only) 1
NO_COLOR Disable ANSI colors 1
CLICOLOR_FORCE Force ANSI colors 1
RUST_LOG Tracing filter override debug
  • Prefer CLI flags over environment for production agent runs.
  • Never put host passwords in environment variables; use registry + stdin.
  • Master-key env vars are for encryption of secrets at rest, not SSH passwords.

Integration Patterns

Wire agents with one-shot subprocesses only

  • Invoke ssh-cli as a subprocess with explicit argv.
  • Prefer --json or --output-format json for machine parsing.
  • Map non-zero exits with sysexits semantics before retry.
  • Store hosts once via vps add then call exec per task.
  • Pass secrets through --password-stdin when argv history is risky.
  • Read INTEGRATIONS.md for agent-specific notes.

Exit Codes

Sysexits-style codes agents must map before retry

Code Meaning
0 Success
1 General runtime error
64 Usage / invalid arguments
65 Data error (JSON/TOML/schema)
66 VPS or input file not found
73 Cannot create config/output
74 IO or SSH connection/timeout
77 Authentication rejected or host-key / sudo policy
130 SIGINT
143 SIGTERM
  • Prefer --json or auto JSON when stdout is not a TTY (--output-format overrides).
  • Retry only on transient IO/timeout (74), never on auth (77) or usage (64).

Performance

Cold start and memory goals

  • Target cold start under 100 ms on modern Linux hosts.
  • Keep process memory far below a resident long-lived Node SSH process.
  • Die after each command so RAM returns to the OS immediately.
  • Avoid long-lived tunnels without --timeout-ms.

Memory Requirements

Plan capacity for multi-host registries

  • Config TOML size grows with host count and path lengths.
  • Output buffers respect max_output_chars per stream.
  • Known_hosts file grows slowly with unique host:port pairs.
  • No embedding model and no Node heap are required.

Troubleshooting FAQ

Fix common install and runtime failures

  • Install fails on crypto RC drift: rerun with --locked or use release 0.3.6+ pins (scripts/verify_install_resolve.sh).
  • Auth fails on key-only hosts: set --key on vps add or pass --key / --password-stdin to exec.
  • Auth fails with passphrase keys: use --key-passphrase-stdin.
  • Host key changed: confirm legitimacy then rerun with --replace-host-key.
  • Command rejected as too long: raise max_command_chars or shorten the command.
  • Config has encrypted secrets but no key: run ssh-cli secrets init or restore secrets.key / env master-key.
  • sudo-exec disabled: remove --disable-sudo and set disable_sudo=false on the host.
  • macOS Gatekeeper blocks binary: run xattr -d com.apple.quarantine /path/to/ssh-cli.
  • Permission denied on config: ensure chmod 600 on the XDG config.toml and secrets.key.

Contributing

  • Read CONTRIBUTING.md before opening a pull request.
  • Follow the bilingual documentation framework for every public doc change.

Security

  • Read SECURITY.md for private vulnerability reporting.
  • Prefer stdin secret flags and key files over argv passwords.

Changelog

  • Read version history in CHANGELOG.md.
  • Do not paste release notes into this README.

License