Metadata-Version: 2.4
Name: vssh
Version: 4.3.2
Summary: vssh — AI-native, drop-in ssh replacement (CLI binary + Python SDK)
Project-URL: Homepage, https://github.com/zeus-kim/vssh
Project-URL: Repository, https://github.com/zeus-kim/vssh
Author: zeus
License: Apache-2.0
License-File: LICENSE
License-File: NOTICE
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

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

```bash
# 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](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:

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

```bash
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](https://github.com/zeus-kim/vssh/releases/latest),
**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).

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

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

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

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

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

```text
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=1` — `vssh_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](docs/MANUAL.md).

---

## Python SDK

```python
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](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 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](docs/KEY_ROTATION.md).
- Report vulnerabilities via
  [GitHub Security Advisories](https://github.com/zeus-kim/vssh/security/advisories/new).

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

---

## Documentation

- [Why vssh](docs/WHY_VSSH.md) — positioning, ssh vs vssh, what ships today
- [Python SDK](docs/PYTHON_SDK.md) · [Usage manual (CLI + MCP)](docs/MANUAL.md)
- [Key rotation & recovery](docs/KEY_ROTATION.md) · [Security audit package](docs/SECURITY_AUDIT_PACKAGE.md)
- [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 9 targets (7 linux arches + macOS amd64/arm64) into dist/
```

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

## License

MIT
