Skip to main content

vssh — AI-native, drop-in ssh replacement (CLI binary + Python SDK)

Project description

vssh

Remote execution for the AI era — structured, scoped, and audited. No sshd.

When an AI agent needs to run things across your servers, SSH hands it a full interactive shell — anything the logged-in user can do, as raw text, with no record of intent. vssh hands it a bounded contract instead:

  • Scoped. Each key is limited to exact capabilities (exec / file / rpc / forward) and an optional policy (command allowlist, path scope, rate limit) — deny-first, fail-closed, enforced by the daemon. The safe-exec path also flags curl … | bash and credential-file reads for approval.
  • Structured. Every result is typed evidence — stdout, stderr, exit code, duration, transport — not text to scrape.
  • Audited. Every action is hash-chain recorded and attributed to a key.
  • Keyed, not shelled. TLS 1.3 + per-node Ed25519, no shared secret, and no sshd on the target.

It's just as good for humans (vssh run, shell, file transfer, tunnels, jobs) and ships an MCP server inside the binary, so Claude, Cursor, Codex, and Gemini attach in one command.

# Install (Linux x86-64/arm64/arm/386/riscv64/ppc64le/s390x, macOS; FreeBSD experimental)
curl -fsSL https://raw.githubusercontent.com/zeus-kim/vssh/main/install.sh | bash
# or
pip install vssh

Why vssh

vssh is not "SSH with a shorter command" — it is a different abstraction for when the operator is often an AI agent or automation runtime, not a person at a terminal. A quick comparison:

OpenSSH vssh
Target prerequisite sshd + users + host keys + PAM one static binary, no sshd
Auth passwords / keys via PAM per-node Ed25519 VAUTH1; no shared secret
Transport SSH protocol TLS 1.3 + Ed25519 raw-key pinning
Host identity known_hosts TOFU pinned registry, default-on, anti-misroute
Command result raw text stream typed evidence (stdout/stderr/exit/duration/transport)
Authorization shell = full access per-key capabilities + command policy
Audit none built in hash-chained record per action
Long-running work tmux / nohup job_start/status/logs/cancel + artifacts
Multi-node scripting native fan-out + capability/health routing
AI / automation parse text MCP-native typed tools
Fleet state none signed fleet snapshot, replicable to nodes
Onboarding manual zero-touch auto-setup; one-command MCP attach

vssh doesn't wrap OpenSSH: for a one-off shell on a box where vssh isn't installed, plain ssh is the zero-setup option. On a fleet already running vssh, the CLI covers interactive shells (vssh shell), file transfer, and tunnels too — no ssh needed. Full rationale: docs/WHY_VSSH.md.

Out of scope: operating the VPN mesh and the monitoring dashboards — bring your own (e.g. Tailscale or WireGuard for the network layer, your own metrics/monitoring stack).


Attach to your AI client (one command)

vssh is an MCP server built into the binary. Attach it to an AI client without hand-editing any JSON:

vssh mcp-install --client claude     # or: claude-code, cursor, gemini, codex
# preview/print instead of writing:
vssh mcp-config  --client claude

This merges a vssh MCP server entry (pointing at the absolute binary path) into the client's config, preserving existing servers. Restart the client and the AI can route, gate, and run fleet work with an audit trail. On first use, vssh auto-provisions host-identity verification (zero-touch) — no manual setup step.


Install

One-line installer (recommended)

curl -fsSL https://raw.githubusercontent.com/zeus-kim/vssh/main/install.sh | bash

The installer detects your OS/arch, downloads the matching binary from the latest GitHub release, verifies its SHA-256 against the published checksums.txt, and installs to ~/bin. Releases cover Linux amd64/arm64/arm/386/riscv64/ppc64le/s390x and macOS amd64/arm64 (FreeBSD is an experimental build).

curl -fsSL .../install.sh | VSSH_VERSION=0.7.42 bash   # pin a version
curl -fsSL .../install.sh | INSTALL_DIR=/usr/local/bin bash

pip (CLI + Python SDK)

pip install vssh

Installs the vssh CLI (the Go binary is fetched + checksum-verified for your platform on first run, cached under ~/.vssh/bin) and the Python SDK (from vssh import VSSH).

From source

git clone https://github.com/zeus-kim/vssh && cd vssh
make build          # builds ./vssh   (Go 1.25+)
make install        # installs to /usr/local/bin (sudo)

Quick start

On the target node, start the daemon (key-only auth — nothing to configure):

vssh server                  # listens on :48291

Authorize a client by adding its public key to the server's ~/.vssh/authorized_keys (run vssh keygen on the client to print its key). For a fleet, scripts/enroll.sh <node> does this from a controller automatically. Then, from the client:

vssh run web1 "df -h"        # run a command, get structured evidence
vssh web1                    # interactive shell (PTY)
vssh put ./app web1:/tmp/    # upload a file
vssh get web1:/var/log/x .   # download a file
vssh fwd web1 -L 8080:localhost:80   # local port-forward (also -R, -D SOCKS)
vssh                         # fleet dashboard
vssh doctor --json           # diagnose binary, auth model, config, peers, MCP

vssh run returns an evidence envelope — exit code, durations, transport, and the endpoints it tried — not just raw text.


How it works

vssh client ──TLS 1.3──▶ vssh server ──▶ typed exec / file / job / tunnel / RPC ──▶ structured evidence
            (Ed25519 pinned)   (:48291)
  1. Transport — TLS 1.3 with the daemon's Ed25519 public key pinned (raw-key, not a CA). TLS-first; VSSH_REQUIRE_TLS=1 refuses plaintext.
  2. Host identity — the client verifies it reached the intended host by checking the daemon key against a trusted registry (default-on since 0.7.33), so a name can't be silently misrouted to the wrong machine.
  3. Authentication — per-node Ed25519 challenge–response (VAUTH1) only. No shared secret; a client is authorized by listing its public key in ~/.vssh/authorized_keys (or /etc/vssh/authorized_keys). The daemon rejects any non-VAUTH1 auth line.
  4. Policy + audit — commands are classified and can be gated before execution (per-key allow/deny, path scope, rate, capabilities), and every action is written to a hash-chained audit log attributed to the key.
  5. Fleet state — the controller can build a signed, timestamped snapshot of the whole fleet and replicate read-only copies to nodes for durability.

CLI reference

Execution
  vssh <node>                 Interactive PTY shell
  vssh run <node> <cmd>       Run a command (structured result)
  vssh run-many <n1,n2> <cmd> Run across comma-separated nodes
  vssh run-batch <node> ...   Run multiple commands on one session

Typed APIs
  vssh rpc <node> <method> [json]   Call a typed daemon RPC (incl. node_info)
  vssh rpc-many <nodes> <method>    RPC across nodes
  vssh facts <node>                 Typed host facts
  vssh facts-many <nodes>           Facts across nodes

Jobs (long-running)
  vssh job-start <node> <cmd>  / job-status / job-logs / job-cancel
  vssh artifact-collect <node> Collect output artifacts

Files & tunnels
  vssh put <local> <node:path> Upload
  vssh get <node:path> <local> Download
  vssh fwd <node> -L [bind:]<lport>:<host>:<port>   Local forward
  vssh fwd <node> -R [bind:]<rport>:<host>:<port>   Reverse forward
  vssh fwd <node> -D [bind:]<port>                  Dynamic SOCKS5

Identity & fleet state
  vssh keygen [--rotate]       Show (or rotate) this host's Ed25519 identity
  vssh pubkey                  Print this host's public key
  vssh fleet-state [build [--live]|show|verify]   Signed fleet snapshot (--live probes reachability)

Setup & MCP
  vssh mcp                     Run the MCP server (for AI agents)
  vssh mcp-install --client <c>  Attach vssh to an AI client (claude|claude-code|cursor|gemini|codex)
  vssh mcp-config  [--client c]  Print the MCP config snippet
  vssh setup                   First-run self-configuration

Fleet & ops
  vssh / vssh status           Dashboard
  vssh list                    List known nodes
  vssh doctor [--json]         Diagnose local setup
  vssh deploy <node>           Atomic binary install + restart + verify
  vssh server                  Run the daemon
  vssh version / help

MCP server (for AI agents)

vssh mcp exposes typed tools that return execution evidence. Current tools:

Group Tools
Discovery / state vssh_setup, vssh_doctor, vssh_hosts_list, vssh_list, vssh_status, vssh_fleet_state
Routing vssh_route_select, vssh_exec_routed
Execution vssh_exec, vssh_exec_safe, vssh_exec_many, vssh_policy_check
Typed RPC / facts vssh_rpc_call, vssh_rpc_many, vssh_facts, vssh_facts_many
Jobs / artifacts vssh_job_start, vssh_job_status, vssh_job_logs, vssh_job_cancel, vssh_artifact_collect
Tunnels vssh_tunnel (start/list/stop local, reverse, SOCKS forwards)
Config (gated) vssh_config_list, and — with VSSH_ALLOW_CONFIG_WRITE=1vssh_config_authorize_key, vssh_config_revoke_key, vssh_config_set_node, vssh_config_pin_node

Destructive commands (rm -rf, shutdown, reboot, docker rm, kubectl delete, systemctl restart, …) are blocked unless the caller sets allow_dangerous: true after explicit human approval. Config-mutating tools are off by default and require VSSH_ALLOW_CONFIG_WRITE=1 (an explicit operator opt-in to let the AI manage local config). Every response is an evidence envelope. See docs/MANUAL.md.


Python SDK

from vssh import VSSH

client = VSSH()                            # key-only auth; no secret to configure
client.exec("web1", "uptime")              # -> ExecResult(stdout, exit_code, ...)
client.exec_many(["web1", "db1"], "uptime")
client.facts("web1")                        # typed host facts
job = client.job_start("web1", "long-task")
client.job_status("web1", job["job_id"])
client.doctor()

The SDK is a thin client over the installed vssh binary (it does not reimplement the protocol), so it inherits the same transport, auth, and policy. See docs/PYTHON_SDK.md.


Configuration

Node inventory — ~/.vssh/servers.json

{
  "web1": { "ip": "192.0.2.10", "user": "deploy", "tags": ["linux", "web"], "capabilities": ["docker"] },
  "gpu1": { "ip": "192.0.2.20", "user": "ubuntu", "tags": ["gpu"], "capabilities": ["cuda", "ollama"], "monitor_port": 8721 }
}

Nodes are also discovered from a Wire coordinator, Tailscale, and a local cache. Do not commit a real servers.json, hostnames, VPN IPs, or keys — keep inventory outside the repo and use example values in docs.

Environment variables

Variable Purpose
VSSH_PORT Daemon listen port (default 48291).
VSSH_REQUIRE_TLS 1/true/yes = refuse non-TLS (plaintext) connections; matches the state node_info reports.
VSSH_REQUIRE_VAUTH 1 = require per-node key auth (enforced fleet-wide).
VSSH_NO_HOSTKEY_VERIFY 1 = opt out of host-identity verification (not recommended).
VSSH_NO_AUTOSETUP 1 = disable zero-touch auto-setup on first MCP exec.
VSSH_ALLOW_CONFIG_WRITE 1 = allow the gated AI config-management MCP tools.
VSSH_VERSION (installer / pip wrapper) pin the binary release to fetch.
VSSH_HOME Override the ~/.vssh directory.

Security

  • The daemon grants authorized peers command execution and file transfer as the configured user — treat access as root-equivalent.
  • Authentication is per-node Ed25519 keys (VAUTH1) only; there is no shared secret. Authorize clients via ~/.vssh/authorized_keys; enforce encryption with VSSH_REQUIRE_TLS=1 and key-auth with VSSH_REQUIRE_VAUTH=1.
  • The VPN (WireGuard/Tailscale) encrypts the tunnel but is not a substitute for vssh authentication — authorize keys and firewall the listen port.
  • Rotate keys with vssh keygen --rotate + scripts/rotate_authorized_key.sh; see docs/KEY_ROTATION.md.
  • Report vulnerabilities via GitHub Security Advisories.

Full model and the external-audit package: SECURITY.md · docs/SECURITY_AUDIT_PACKAGE.md.


Documentation

Building & contributing

make build      # build ./vssh
make test       # go test ./... + Python SDK tests
make release    # cross-compile 9 targets (7 linux arches + macOS amd64/arm64) into dist/

See CONTRIBUTING.md.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vssh-4.3.2.tar.gz (225.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vssh-4.3.2-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file vssh-4.3.2.tar.gz.

File metadata

  • Download URL: vssh-4.3.2.tar.gz
  • Upload date:
  • Size: 225.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for vssh-4.3.2.tar.gz
Algorithm Hash digest
SHA256 f68bdfc3dc9ea73c4b1bdafd91d112e2cd52fb4f3b5c77c4d976b47c2d9526fa
MD5 510ca0e7ab1277d2ac8bdcea5a28fba6
BLAKE2b-256 6a2a7f3efaa46b07e679512627898b97d02b2517881ef32d18771e33c9db4011

See more details on using hashes here.

File details

Details for the file vssh-4.3.2-py3-none-any.whl.

File metadata

  • Download URL: vssh-4.3.2-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for vssh-4.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 6923bbe3431dfba3f9cb0ef35f12a781bac29b6b78235a47f8b9a1919f5d770e
MD5 ba46d44fb5c4b1186e332a4c4c7c81d7
BLAKE2b-256 484df9bb00e813ed52e04db66d560637e6798a4e469d71804e0c0fe80b04031c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page