Metadata-Version: 2.4
Name: vssh
Version: 4.3.1
Summary: vssh — AI-native, drop-in ssh replacement (CLI binary + Python SDK)
Project-URL: Homepage, https://github.com/meshpop/vssh
Project-URL: Repository, https://github.com/meshpop/vssh
Author: MeshPop
License: MIT
Keywords: ai-agents,mcp,remote-execution,ssh,tailscale,vssh
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# 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.

```bash
# 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/duration` evidence, 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](docs/WHY_VSSH.md).

**Out of scope:** operating the VPN mesh and fleet dashboards. Use
[Wire](https://github.com/meshpop/wire) for the network layer and
[mpop](https://github.com/meshpop/mpop) for monitoring.

---

## Install

### One-line installer (recommended)

```bash
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](https://github.com/meshpop/vssh/releases/latest),
**verifies its SHA-256 against the published `checksums.txt`**, and installs to
`~/bin`. Options:

```bash
# 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)

```bash
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](#python-sdk).

### From source

```bash
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):

```bash
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:

```bash
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](#security).

---

## How it works

```text
vssh client ──TLS 1.3──▶ vssh server ──▶ typed exec / file / job / RPC APIs ──▶ structured evidence
            (Ed25519 pinned)   (:48291)
```

1. **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=1` to
   refuse plaintext entirely.
2. **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.
3. **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`).
4. **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:

```bash
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](docs/CODEX_ORCHESTRATION.md).

---

## Python SDK

```python
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](docs/PYTHON_SDK.md).

---

## Configuration

### Node inventory — `~/.vssh/servers.json`

```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`

```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 with `VSSH_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](https://github.com/meshpop/vssh/security/advisories/new).

Full model and hardening steps: [SECURITY.md](SECURITY.md).

---

## Documentation

- [Why vssh](docs/WHY_VSSH.md) — positioning and scope
- [Python SDK](docs/PYTHON_SDK.md) — SDK reference
- [Codex / agent orchestration](docs/CODEX_ORCHESTRATION.md) — MCP usage for agents
- [SECURITY.md](SECURITY.md) · [CONTRIBUTING.md](CONTRIBUTING.md) · [CHANGELOG.md](CHANGELOG.md)
- [한국어 README](README.ko.md)

## Building & contributing

```bash
make build      # build ./vssh
make test       # go test ./... + Python SDK tests
make release    # cross-compile linux/darwin × amd64/arm64 into dist/
```

See [CONTRIBUTING.md](CONTRIBUTING.md).

## License

MIT
