Metadata-Version: 2.4
Name: vssh
Version: 3.7.5
Summary: Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server
Author-email: MeshPOP <mpop@mpop.dev>
License: MIT
Project-URL: Homepage, https://github.com/meshpop/vssh
Project-URL: Repository, https://github.com/meshpop/vssh
Keywords: ssh,scp,tailscale,p2p,vpn,mcp,remote
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: System :: Networking
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# vssh

**Fast SSH alternative for server fleets — no key management, instant connections, AI-assisted.**

```bash
pip install vssh
```

No external dependencies. Pure Python standard library. Works on Linux and macOS.

---

## Why vssh?

### The problem with SSH at scale

SSH is the industry standard for remote access — but it was designed for single-server use. Managing a fleet with SSH means:

**Key management sprawl:**
```bash
# Adding a new admin to 20 servers
for server in web{1..10} db{1..5} worker{1..5}; do
  ssh-copy-id -i ~/.ssh/newadmin.pub root@$server
done
# When they leave: remove the key from all 20 servers
# Key rotation: touch every server again
```

**IP address bookkeeping:**
```bash
# You have to remember or look up IPs constantly
ssh root@10.0.1.42   # which server is this again?
ssh -i ~/.ssh/id_rsa root@10.0.1.42 "df -h"
# Different key for different servers? Different user? Port?
```

**Connection overhead:**
SSH does a full TLS-style handshake on every connection — key exchange, algorithm negotiation, authentication. For a fleet-wide operation this adds up to seconds of overhead per server.

**No unified view:**
There's no built-in way to see which servers are reachable right now, or to run commands across multiple servers with clean output.

### Before vssh

```bash
# Deploy nginx config to 10 servers and reload
for ip in 10.0.1.10 10.0.1.11 10.0.1.12 10.0.1.13 10.0.1.14 \
          10.0.1.15 10.0.1.16 10.0.1.17 10.0.1.18 10.0.1.19; do
  scp -i ~/.ssh/id_rsa ./nginx.conf root@$ip:/etc/nginx/nginx.conf
  ssh -i ~/.ssh/id_rsa root@$ip "nginx -t && systemctl reload nginx"
done
# ~200-400ms per server just for connection setup
# Must track IPs, keys, users manually
# No visibility into which succeeded or failed
```

### After vssh

```bash
# Same operation with vssh
for node in web{1..10}; do
  vssh put ./nginx.conf $node:/etc/nginx/nginx.conf
  vssh $node "nginx -t && systemctl reload nginx"
done
# Names auto-discovered from wire mesh
# One shared secret — no per-server keys
# Persistent daemon — connections are instant
```

### Comparison

| | SSH | vssh |
|---|---|---|
| Authentication | Per-server public keys | One shared HMAC secret |
| Key management | Add/remove on every server | Change one secret, done |
| Connection setup | Full handshake every time (~200-400ms) | HMAC token check (~1ms) |
| Server discovery | IP addresses / /etc/hosts / DNS | Wire mesh auto-discovery |
| Fleet health view | None built-in | `vssh status` shows all nodes |
| File transfer | `scp` (new connection each time) | Persistent connection, resumable |
| AI management | ✗ | ✓ MCP integration |
| Dependencies | OpenSSH (system) | None (pure Python) |

---

## Architecture

vssh has two parts: a **daemon** (vsshd) running on each server, and a **client** (`vssh`) you run from your machine.

```
┌─────────────────────────────────────────────┐
│               Your Server Fleet             │
│                                             │
│  ┌──────┐    ┌──────┐    ┌──────┐           │
│  │ web1 │    │ web2 │    │ db1  │           │
│  │vsshd │    │vsshd │    │vsshd │  ...      │
│  │:48291│    │:48291│    │:48291│           │
│  └──┬───┘    └──┬───┘    └──┬───┘           │
│     └───────────┴───────────┘               │
│              WireGuard mesh (wire)           │
└──────────────────┬──────────────────────────┘
                   │  TCP :48291
            ┌──────┴──────┐
            │  vssh client │  ← your machine
            │  (Mac/Linux) │
            └─────────────┘
```

All communication happens on **TCP port 48291** using a simple line-delimited protocol with HMAC-SHA256 authentication.

vssh is **Layer 2** of the MeshPOP stack:

```
Layer 3  mpop    Fleet orchestration — monitor, manage, automate
Layer 2  vssh    Authenticated transport — remote exec, file transfer  ← this
Layer 1  wire    Encrypted mesh VPN — connects all nodes
```

Each layer is independently installable. vssh works without wire (use a static config file) and without mpop.

---

## Installation

### Install vssh

```bash
pip install vssh
```

This installs:
- `vssh` — CLI client
- `vsshd` / `vssh server` — daemon
- `vssh-mcp` — MCP server for AI integration

Pure Python standard library — no external packages required.

### Start the daemon on each server

Set the same shared secret on every server (environment variable or file):

```bash
# Option 1: environment variable
export VSSH_SECRET=your-shared-secret-here
vssh server

# Option 2: secret file
echo "your-shared-secret-here" > ~/.vssh/secret
chmod 600 ~/.vssh/secret
vssh server
```

The secret can be any string. Use a long random value:
```bash
python3 -c "import secrets; print(secrets.token_hex(32))"
```

### Install as a system service (recommended)

```bash
# Set the secret first, then:
vssh install   # writes /etc/systemd/system/vssh.service and enables it
```

The service starts automatically on boot.

### Configure the client (your machine)

If you use **wire** mesh VPN, vssh discovers all nodes automatically — no config needed.

For standalone use without wire, create `~/.vssh/config`:

```ini
# Server names and IPs
web1=192.168.1.10
web2=192.168.1.11
db1=192.168.1.20

# Shared secret (same as on servers)
SECRET=your-shared-secret-here
```

---

## Authentication

vssh uses **HMAC-SHA256** — a time-based token derived from the shared secret:

```
token = hmac_{timestamp}_{sha256(secret + timestamp)[:32]}
```

Every command includes this token. The server verifies it against its own secret. Tokens are valid for ±60 seconds (clock skew window).

**One secret to manage all servers.** To revoke access, change the secret on all servers. No key files to track, no per-user certificates.

Secret is read from (priority order):
1. `VSSH_SECRET` environment variable
2. `~/.vssh/secret` file
3. `SECRET=` entry in `~/.vssh/config`

---

## CLI Reference

### Fleet status

```bash
vssh status              # Connection status to all mesh nodes
vssh status --full       # Status + disk / memory / load per node
```

```
vssh v3.7.4 - Cluster Status (full)
======================================================================
  web1    10.99.1.10   ● online  (8ms)
    disk: 45% used (120GB / 250GB)  mem: 4.2GB / 16GB  load: 0.42
  web2    10.99.1.11   ● online  (9ms)
    disk: 31% used  mem: 2.1GB / 8GB  load: 0.15
  db1     10.99.1.20   ✗ offline

Total: 2/3 online
```

---

### Remote execution

```bash
vssh <host>                  # Interactive terminal (PTY — like SSH)
vssh <host> "command"        # Run a single command
vssh session <host>          # Persistent PTY session
```

```bash
# Examples
vssh web1                          # Open interactive shell on web1
vssh web1 "uptime"                 # Check uptime
vssh web1 "df -h && free -h"       # Chain commands
vssh db1 "systemctl status postgresql"
```

---

### File transfer

```bash
# Upload
vssh put  <local>  <host>:<remote>
vssh put  -z <local> <host>:<remote>          # Force compression
vssh put  --resume <local> <host>:<remote>    # Resume interrupted upload

# Download
vssh get  <host>:<remote>  <local>
vssh get  --retry=3 <host>:<remote> <local>   # Retry on failure

# Directory sync
vssh sync <local_dir>  <host>:<remote_dir>    # 8 parallel streams

# Delta sync (only changed blocks — like rsync)
vssh rsync <local>  <host>:<remote>

# Multiple files in one connection
vssh mput <host>:<base_path>  file1 file2 file3
```

```bash
# Examples
vssh put  ./app.tar.gz  web1:/opt/app.tar.gz
vssh get  web1:/var/log/app.log ./app.log
vssh sync ./config/     web1:/etc/app/
vssh rsync ./src/       web1:/opt/src/
```

vssh auto-compresses text files (`.py`, `.js`, `.json`, `.log`, etc.) and skips upload if remote MD5 matches.

---

### Speed test

```bash
vssh speed-test web1          # 10MB test
vssh speed-test web1 --size=50
```

---

### Pipe operations

```bash
# Upload stdin to remote file
cat data.csv | vssh pipe-up web1:/data/input.csv

# Download remote command output to stdout
vssh pipe-down web1:"journalctl -u nginx -n 100" | grep ERROR
```

---

### Daemon management

```bash
vssh server          # Start daemon (foreground)
vssh install         # Install as systemd (Linux) or launchd (macOS) service
vssh up              # Start via systemd
vssh down            # Stop daemon
vssh restart         # Restart daemon
```

---

### Node info

```bash
vssh info <host>     # OS, IPs, vsshd version, load
```

---

### History and stats

```bash
vssh history           # Last 20 commands
vssh history 50        # Last 50
vssh history put       # Filter by operation (SSH, PUT, GET, RPC, SYNC)
vssh stats             # Transfer stats (last 7 days)
vssh stats 30          # Last 30 days
```

---

## RPC — Structured Remote Calls

RPC is vssh's structured interface for getting typed JSON data from remote servers — no screen scraping, no parsing shell output.

```bash
vssh rpc <host> <method>                     # No arguments
vssh rpc <host> <method> '{"key":"value"}'   # With JSON payload
vssh rpc-list <host>                         # List available methods
```

### Available RPC methods

| Method | Description |
|---|---|
| `get_disk` | Disk usage (path optional) |
| `get_memory` | Memory usage in bytes |
| `get_load` | Load average (1m / 5m / 15m) |
| `get_processes` | Top processes by CPU or memory |
| `get_gpu` | GPU VRAM, utilization, temperature |
| `get_logs` | Read log file or journalctl output |
| `get_network_info` | Local and public IPs |
| `list_services` | Running systemd services |
| `service_status` | Check if a service is active |
| `restart_service` | Restart nginx / docker / ollama / postgresql / redis |
| `docker_containers` | List Docker containers and status |
| `file_read` | Read a file (with security restrictions) |
| `file_write` | Write a file (system paths blocked) |

```bash
# Examples
vssh rpc web1 get_disk
vssh rpc web1 get_disk '{"path": "/data"}'
vssh rpc web1 get_memory
vssh rpc web1 get_processes '{"n": 5, "sort": "mem"}'
vssh rpc web1 get_gpu
vssh rpc web1 get_logs '{"service": "nginx", "lines": 50}'
vssh rpc web1 restart_service '{"service": "nginx"}'
```

---

## Tailscale Failover

If a node's primary VPN IP (wire) is unreachable, vssh automatically retries via Tailscale:

1. Try wire VPN IP (2s timeout)
2. Retry once after 300ms (WireGuard handshake may be completing)
3. Fall back to Tailscale IP, cache for 60s
4. After 60s: retry wire — if recovered, clear failover cache

The failover map (`~/.vssh/tailscale_map`) is built automatically by cross-referencing `tailscale status` with the wire peer list. No manual configuration needed.

---

## AI Management via MCP

vssh ships with an MCP server that lets AI agents (Claude, etc.) manage your fleet through natural language — from installation through daily operations.

### Setup

```bash
pip install vssh   # installs vssh-mcp automatically
```

Add to Claude config (`~/.claude/settings.json`):

```json
{
  "mcpServers": {
    "vssh": { "command": "vssh-mcp" }
  }
}
```

### What the AI can do

Once connected, you can ask Claude in plain language:

> "Check which servers are online and show me their disk usage"
> "Deploy the new config to all web servers and reload nginx"
> "Show me the last 50 nginx error log lines from web1"
> "What's using the most memory on db1?"
> "Sync the /etc/app/ config directory from web1 to web2"
> "Run a speed test to web1"

The AI handles the vssh commands, interprets the results, and tells you what it found.

### MCP Tools Reference

| Tool | Description |
|---|---|
| `vssh_status` | Fleet status — all nodes, online/offline, latency |
| `vssh_exec` | Run a shell command on a remote server |
| `vssh_put` | Upload a file to a remote server |
| `vssh_get` | Download a file from a remote server |
| `vssh_sync` | Sync a directory between two servers |
| `vssh_speed_test` | Measure upload/download speed to a server |
| `vssh_tunnel` | Create a port-forwarding tunnel |
| `vssh_keys` | Show configured secrets and available servers |

### Example AI workflow — deploy update

```
You: "Deploy app-v2.tar.gz to all web servers and restart the app service"

AI:
1. vssh_status → check which web servers are online
2. vssh_put(web1, /tmp/app-v2.tar.gz, /opt/app.tar.gz)
3. vssh_exec(web1, "cd /opt && tar xf app.tar.gz && systemctl restart app")
4. vssh_exec(web1, "systemctl is-active app")
5. Repeats for web2, web3...
6. Reports: "Deployed to 3/3 servers. All app services active."
```

---

## Links

- Wire VPN: [github.com/meshpop/wire](https://github.com/meshpop/wire)
- Fleet orchestration: [github.com/meshpop/mpop](https://github.com/meshpop/mpop)
- PyPI: [pypi.org/project/vssh](https://pypi.org/project/vssh/)

## License

MIT — [MeshPOP](https://github.com/meshpop)
