vssh — AI-native, drop-in ssh replacement (CLI binary + Python SDK)
Project description
vssh
An sshd-free, AI-native remote execution daemon for private networks.
vssh runs commands, transfers files, and manages long-running jobs on remote
nodes over your private network (Tailscale, WireGuard/Wire, or LAN) — without
running sshd on the target. It speaks a typed protocol over TLS 1.3 with
Ed25519 key authentication, and every result comes back as structured evidence
(stdout, stderr, exit code, duration, transport), which makes it a natural
execution layer for AI agents and automation as well as for humans.
# Install (Linux/macOS, amd64/arm64)
curl -fsSL https://raw.githubusercontent.com/meshpop/vssh/main/install.sh | bash
# or
pip install vssh
Why vssh
Use vssh when the operator is often an AI agent or automation runtime, not
a person typing into a terminal, and when you want execution that is:
- sshd-free — the target runs
vssh server, not OpenSSH. - typed & auditable — structured
stdout/stderr/exit/durationevidence, not a raw text stream. - policy-gated — classify and block dangerous commands before they run.
- durable — start/poll/cancel long-running jobs and collect artifacts.
- name-routed — address nodes by name and capability, not raw IPs.
If all you need is an interactive human shell on a box that already runs sshd,
use ssh directly — vssh deliberately does not wrap OpenSSH. See
docs/WHY_VSSH.md.
Out of scope: operating the VPN mesh and fleet dashboards. Use Wire for the network layer and mpop for monitoring.
Install
One-line installer (recommended)
curl -fsSL https://raw.githubusercontent.com/meshpop/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. Options:
# Install a specific version
curl -fsSL .../install.sh | VSSH_VERSION=0.7.36 bash
# Install to a custom directory
curl -fsSL .../install.sh | INSTALL_DIR=/usr/local/bin bash
pip (CLI + Python SDK)
pip install vssh
This installs both the vssh CLI (the Go binary is fetched and checksum-verified
for your platform on first run, cached under ~/.vssh/bin) and the Python
SDK (from vssh import VSSH). See Python SDK.
From source
git clone https://github.com/meshpop/vssh && cd vssh
make build # builds ./vssh
make install # installs to /usr/local/bin (sudo)
Requires Go 1.25+.
Quick start
On the target node, start the daemon (no shared secret — auth is key-based):
vssh server # listens on :48291
Authorize a client by adding its public key to the server's
~/.vssh/authorized_keys (run vssh pubkey on the client to print it). 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 output
vssh web1 # open an interactive shell
vssh put ./app web1:/tmp/ # upload a file
vssh get web1:/var/log/x . # download a file
vssh # fleet dashboard
vssh doctor --json # diagnose binary, secret, config, peers, MCP
vssh run returns an evidence envelope — exit code, durations, transport, and
the endpoints it tried — not just raw text.
For a multi-node fleet,
scripts/enroll.sh <node>installs the daemon and cross-authorizes keys from a controller. See Security.
How it works
vssh client ──TLS 1.3──▶ vssh server ──▶ typed exec / file / job / RPC APIs ──▶ structured evidence
(Ed25519 pinned) (:48291)
- Transport — TLS 1.3 with the daemon's Ed25519 public key pinned
(raw-key, not a CA). The client is TLS-first; set
VSSH_REQUIRE_TLS=1to refuse plaintext entirely. - Host identity — the client verifies it reached the intended host by checking the daemon key against a trusted registry (on by default since 0.7.33), so a name can't be silently misrouted to the wrong machine.
- Authentication — per-node Ed25519 challenge–response (VAUTH1) only.
There is no shared secret; a client is authorized by listing its public key in
the server's
~/.vssh/authorized_keys(or/etc/vssh/authorized_keys). - Policy — commands can be classified and gated before execution; per-key allow/deny lists, path scoping, and rate limits are available opt-in.
CLI reference
Execution
vssh <node> Interactive PTY shell
vssh shell <node> Interactive PTY shell
vssh run <node> <cmd> Run a command (structured result)
vssh exec <node> <cmd> Alias for run
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
vssh rpc-many <nodes> <method> [json] RPC across nodes
vssh facts <node> Typed host facts
vssh facts-many <nodes> Facts across nodes
Jobs (long-running)
vssh job-start <node> <cmd> Start a background job
vssh job-status <node> <id> Job status
vssh job-logs <node> <id> Job logs
vssh job-cancel <node> <id> Cancel a job
vssh artifact-collect <node> Collect output artifacts
Files
vssh put <local> <node:path> Upload
vssh get <node:path> <local> Download
Fleet & ops
vssh Dashboard (default)
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 mcp Run the MCP server (for AI agents)
vssh setup First-run self-configuration
vssh version Show version
vssh help Full help
MCP server (for AI agents)
The MCP server is built into the binary — no separate install:
vssh mcp
It exposes typed tools that return execution evidence, so an agent can route, gate, and run work with an audit trail:
| Tool | Purpose |
|---|---|
vssh_doctor |
Diagnose binary, secret source, config, peers, MCP readiness |
vssh_hosts_list |
List known hosts (addresses, tags, capabilities, health) for routing |
vssh_route_select |
Pick the best host by capability, tag, and health |
vssh_exec_safe |
Run read-only/diagnostic commands (dangerous ones blocked) |
vssh_exec |
Run with policy checks; allow_dangerous after explicit approval |
vssh_exec_routed |
Route first, then execute with policy + evidence |
vssh_policy_check |
Classify a command before running it |
vssh_status, vssh_list |
Status and peer inventory |
Commands matching destructive patterns (rm -rf, shutdown, reboot,
docker rm, kubectl delete, systemctl restart, …) are blocked unless the
caller sets allow_dangerous: true after explicit human approval. Every response
is an evidence envelope with timestamps, the policy decision, target, command,
and the structured result. See docs/CODEX_ORCHESTRATION.md.
Python SDK
from vssh import VSSH
client = VSSH(secret="a-long-random-value")
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.
Full method list: exec, exec_many, rpc, rpc_many, facts, facts_many,
job_start, job_status, job_logs, job_cancel, artifact_collect,
doctor. 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 automatically from a Wire coordinator, Tailscale, and a
local cache. Do not commit a real servers.json, hostnames, VPN IPs, or
secrets — keep inventory outside the repo and use example values in docs.
Per-host users — ~/.wire/users.json
{ "web1": "deploy", "db1": "postgres" }
Environment variables
| Variable | Purpose |
|---|---|
VSSH_PORT |
Daemon listen port (default 48291). |
VSSH_REQUIRE_TLS |
1 = refuse non-TLS connections. |
VSSH_NO_HOSTKEY_VERIFY |
1 = opt out of host-identity verification (not recommended). |
VSSH_BIN |
(pip wrapper) use this binary instead of downloading. |
VSSH_VERSION |
(pip wrapper / installer) pin the binary release to fetch. |
VSSH_HOME |
Override the ~/.vssh directory. |
Security
- The native 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; authorize clients via
~/.vssh/authorized_keys. Enforce encryption withVSSH_REQUIRE_TLS=1. - The VPN (WireGuard/Tailscale) encrypts the tunnel but is not a substitute for vssh authentication — always set a strong secret or enroll keys, and firewall the listen port.
- Report vulnerabilities privately via GitHub Security Advisories.
Full model and hardening steps: SECURITY.md.
Documentation
- Why vssh — positioning and scope
- Python SDK — SDK reference
- Codex / agent orchestration — MCP usage for agents
- SECURITY.md · CONTRIBUTING.md · CHANGELOG.md
- 한국어 README
Building & contributing
make build # build ./vssh
make test # go test ./... + Python SDK tests
make release # cross-compile linux/darwin × amd64/arm64 into dist/
See CONTRIBUTING.md.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vssh-4.3.1.tar.gz.
File metadata
- Download URL: vssh-4.3.1.tar.gz
- Upload date:
- Size: 182.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
72b0d4e81ea195a1a97f28eb55a817bba1b5dc434caa6541b73bdbafa4c224ac
|
|
| MD5 |
f9625564aa16ccefb4e30ebdea055ae2
|
|
| BLAKE2b-256 |
62bdf24985e39fda517b7fe14c8d7672ba1e3b56e96ab9a2a021d2c487bb83f1
|
File details
Details for the file vssh-4.3.1-py3-none-any.whl.
File metadata
- Download URL: vssh-4.3.1-py3-none-any.whl
- Upload date:
- Size: 11.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
957828a39351aaeaf696db4c2f6cbee5dcee4a7994bd0a3483ade3e318457d9c
|
|
| MD5 |
f142b24264feb66a980ff73c10455ea6
|
|
| BLAKE2b-256 |
76dff40491eb421ca9f871b2d3cece498ba5dbeb4bb585df25ebd87772207c96
|