Skip to main content

Secure SSH/SCP tool with Tailscale failover, P2P transport, and MCP server

Project description

vssh

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

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:

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

# 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

# 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

# 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

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

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

python3 -c "import secrets; print(secrets.token_hex(32))"

Install as a system service (recommended)

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

# 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

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

vssh <host>                  # Interactive terminal (PTY — like SSH)
vssh <host> "command"        # Run a single command
vssh session <host>          # Persistent PTY session
# 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

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

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

Pipe operations

# 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

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

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

History and stats

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.

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

pip install vssh   # installs vssh-mcp automatically

Add to Claude config (~/.claude/settings.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

License

MIT — MeshPOP

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-3.7.5.tar.gz (48.5 kB view details)

Uploaded Source

Built Distribution

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

vssh-3.7.5-py3-none-any.whl (49.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: vssh-3.7.5.tar.gz
  • Upload date:
  • Size: 48.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for vssh-3.7.5.tar.gz
Algorithm Hash digest
SHA256 0673c0ad3cbec39b00258801f2628a828c1c5507bd879c35ed868564303e1a7f
MD5 26a7dbdcfbb1d7a47ce503ceb4f749fa
BLAKE2b-256 b1e79295671e8b21edee7f690fb0e43a371128c3ac0f8d14e7ef1434b9f8c688

See more details on using hashes here.

File details

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

File metadata

  • Download URL: vssh-3.7.5-py3-none-any.whl
  • Upload date:
  • Size: 49.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.8

File hashes

Hashes for vssh-3.7.5-py3-none-any.whl
Algorithm Hash digest
SHA256 28f0ff1f378c1f0a57eb020f1193e91ac862a499af9657eaad40595a5a5e341c
MD5 1edebc76a21ab787a96865dc1c5b794b
BLAKE2b-256 09b0ccc30d42a519fbb1419670aaea5ecbf8384f494dcfd4e2a3d90262ade125

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