vssh 4.3.0__tar.gz → 4.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. {vssh-4.3.0 → vssh-4.3.1}/CHANGELOG.md +45 -4
  2. vssh-4.3.1/CONTRIBUTING.md +44 -0
  3. vssh-4.3.1/HELP.md +127 -0
  4. vssh-4.3.1/PKG-INFO +322 -0
  5. vssh-4.3.1/README.ko.md +220 -0
  6. vssh-4.3.1/README.md +301 -0
  7. vssh-4.3.1/SECURITY.md +67 -0
  8. {vssh-4.3.0 → vssh-4.3.1}/cmd/vssh/main.go +7 -18
  9. {vssh-4.3.0 → vssh-4.3.1}/docs/DESIGN_RATIONALE_AND_DIRECTION.md +1 -1
  10. {vssh-4.3.0 → vssh-4.3.1}/docs/PUBLISH_HANDOFF.md +8 -2
  11. vssh-4.3.1/docs/PYTHON_SDK.md +93 -0
  12. {vssh-4.3.0 → vssh-4.3.1}/docs/SECURITY_TRANSPORT_MIGRATION.md +1 -1
  13. {vssh-4.3.0 → vssh-4.3.1}/internal/server/auth.go +0 -35
  14. {vssh-4.3.0 → vssh-4.3.1}/internal/server/server.go +7 -16
  15. vssh-4.3.1/internal/server/server_auth_test.go +37 -0
  16. vssh-4.3.1/internal/server/sync.go +113 -0
  17. vssh-4.3.1/internal/server/transfer_advanced.go +460 -0
  18. {vssh-4.3.0 → vssh-4.3.1}/scripts/authorize_fleet.sh +1 -1
  19. {vssh-4.3.0 → vssh-4.3.1}/scripts/build_node_registry.sh +1 -1
  20. {vssh-4.3.0 → vssh-4.3.1}/scripts/deploy_fleet.sh +1 -1
  21. {vssh-4.3.0 → vssh-4.3.1}/scripts/enable_require_tls.sh +1 -1
  22. {vssh-4.3.0 → vssh-4.3.1}/scripts/enroll.sh +5 -7
  23. {vssh-4.3.0 → vssh-4.3.1}/src/vssh/_version.py +2 -2
  24. vssh-4.3.0/CONTRIBUTING.md +0 -6
  25. vssh-4.3.0/HELP.md +0 -115
  26. vssh-4.3.0/PKG-INFO +0 -359
  27. vssh-4.3.0/README.ko.md +0 -326
  28. vssh-4.3.0/README.md +0 -338
  29. vssh-4.3.0/SECURITY.md +0 -16
  30. vssh-4.3.0/docs/DEV_STATUS_2026-06-12.md +0 -281
  31. vssh-4.3.0/docs/NEXT_SESSION_PROMPT.md +0 -60
  32. vssh-4.3.0/internal/server/auth_test.go +0 -57
  33. vssh-4.3.0/internal/server/sync.go +0 -228
  34. vssh-4.3.0/internal/server/transfer_advanced.go +0 -983
  35. {vssh-4.3.0 → vssh-4.3.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  36. {vssh-4.3.0 → vssh-4.3.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  37. {vssh-4.3.0 → vssh-4.3.1}/.github/workflows/ci.yml +0 -0
  38. {vssh-4.3.0 → vssh-4.3.1}/.github/workflows/release.yml +0 -0
  39. {vssh-4.3.0 → vssh-4.3.1}/.gitignore +0 -0
  40. {vssh-4.3.0 → vssh-4.3.1}/Makefile +0 -0
  41. {vssh-4.3.0 → vssh-4.3.1}/cmd/vssh/doctor.go +0 -0
  42. {vssh-4.3.0 → vssh-4.3.1}/cmd/vssh/fanout_test.go +0 -0
  43. {vssh-4.3.0 → vssh-4.3.1}/cmd/vssh/mcp.go +0 -0
  44. {vssh-4.3.0 → vssh-4.3.1}/cmd/vssh/mcp_test.go +0 -0
  45. {vssh-4.3.0 → vssh-4.3.1}/cmd/vssh/setup.go +0 -0
  46. {vssh-4.3.0 → vssh-4.3.1}/docs/AGENT_READINESS_ROADMAP.md +0 -0
  47. {vssh-4.3.0 → vssh-4.3.1}/docs/AI_NATIVE_CAPABILITIES.ko.md +0 -0
  48. {vssh-4.3.0 → vssh-4.3.1}/docs/CODEX_ORCHESTRATION.ko.md +0 -0
  49. {vssh-4.3.0 → vssh-4.3.1}/docs/CODEX_ORCHESTRATION.md +0 -0
  50. {vssh-4.3.0 → vssh-4.3.1}/docs/DIRECTION.md +0 -0
  51. {vssh-4.3.0 → vssh-4.3.1}/docs/DISTRIBUTION.ko.md +0 -0
  52. {vssh-4.3.0 → vssh-4.3.1}/docs/GAP_ANALYSIS.md +0 -0
  53. {vssh-4.3.0 → vssh-4.3.1}/docs/NETWORK_TRAVERSAL_AUDIT.ko.md +0 -0
  54. {vssh-4.3.0 → vssh-4.3.1}/docs/PERFORMANCE.ko.md +0 -0
  55. {vssh-4.3.0 → vssh-4.3.1}/docs/PUBLISHING_AUDIT.ko.md +0 -0
  56. {vssh-4.3.0 → vssh-4.3.1}/docs/PUBLISHING_AUDIT.md +0 -0
  57. {vssh-4.3.0 → vssh-4.3.1}/docs/PYTHON_SDK.ko.md +0 -0
  58. {vssh-4.3.0 → vssh-4.3.1}/docs/ROADMAP.md +0 -0
  59. {vssh-4.3.0 → vssh-4.3.1}/docs/SECURITY_AUDIT_PACKAGE.md +0 -0
  60. {vssh-4.3.0 → vssh-4.3.1}/docs/SSH_REPLACEMENT_ROADMAP.md +0 -0
  61. {vssh-4.3.0 → vssh-4.3.1}/docs/WHY_VSSH.ko.md +0 -0
  62. {vssh-4.3.0 → vssh-4.3.1}/docs/WHY_VSSH.md +0 -0
  63. {vssh-4.3.0 → vssh-4.3.1}/go.mod +0 -0
  64. {vssh-4.3.0 → vssh-4.3.1}/go.sum +0 -0
  65. {vssh-4.3.0 → vssh-4.3.1}/install.sh +0 -0
  66. {vssh-4.3.0 → vssh-4.3.1}/internal/adapter/vssh.go +0 -0
  67. {vssh-4.3.0 → vssh-4.3.1}/internal/agent/agent.go +0 -0
  68. {vssh-4.3.0 → vssh-4.3.1}/internal/agent/api.go +0 -0
  69. {vssh-4.3.0 → vssh-4.3.1}/internal/config/config.go +0 -0
  70. {vssh-4.3.0 → vssh-4.3.1}/internal/event/event.go +0 -0
  71. {vssh-4.3.0 → vssh-4.3.1}/internal/server/artifact_test.go +0 -0
  72. {vssh-4.3.0 → vssh-4.3.1}/internal/server/client.go +0 -0
  73. {vssh-4.3.0 → vssh-4.3.1}/internal/server/exec_test.go +0 -0
  74. {vssh-4.3.0 → vssh-4.3.1}/internal/server/fmux.go +0 -0
  75. {vssh-4.3.0 → vssh-4.3.1}/internal/server/forward.go +0 -0
  76. {vssh-4.3.0 → vssh-4.3.1}/internal/server/identity.go +0 -0
  77. {vssh-4.3.0 → vssh-4.3.1}/internal/server/jobs.go +0 -0
  78. {vssh-4.3.0 → vssh-4.3.1}/internal/server/jobs_test.go +0 -0
  79. {vssh-4.3.0 → vssh-4.3.1}/internal/server/policy.go +0 -0
  80. {vssh-4.3.0 → vssh-4.3.1}/internal/server/policy_e2e_test.go +0 -0
  81. {vssh-4.3.0 → vssh-4.3.1}/internal/server/policy_templates_test.go +0 -0
  82. {vssh-4.3.0 → vssh-4.3.1}/internal/server/policy_test.go +0 -0
  83. {vssh-4.3.0 → vssh-4.3.1}/internal/server/pty_darwin.go +0 -0
  84. {vssh-4.3.0 → vssh-4.3.1}/internal/server/pty_linux.go +0 -0
  85. {vssh-4.3.0 → vssh-4.3.1}/internal/server/relay.go +0 -0
  86. {vssh-4.3.0 → vssh-4.3.1}/internal/server/rpc.go +0 -0
  87. {vssh-4.3.0 → vssh-4.3.1}/internal/server/tlsident.go +0 -0
  88. {vssh-4.3.0 → vssh-4.3.1}/internal/server/transfer.go +0 -0
  89. {vssh-4.3.0 → vssh-4.3.1}/internal/server/transfer_test.go +0 -0
  90. {vssh-4.3.0 → vssh-4.3.1}/internal/ssh/ssh.go +0 -0
  91. {vssh-4.3.0 → vssh-4.3.1}/internal/ssh/ssh_test.go +0 -0
  92. {vssh-4.3.0 → vssh-4.3.1}/policies/README.md +0 -0
  93. {vssh-4.3.0 → vssh-4.3.1}/policies/backup.json +0 -0
  94. {vssh-4.3.0 → vssh-4.3.1}/policies/ci.json +0 -0
  95. {vssh-4.3.0 → vssh-4.3.1}/policies/deploy.json +0 -0
  96. {vssh-4.3.0 → vssh-4.3.1}/policies/readonly.json +0 -0
  97. {vssh-4.3.0 → vssh-4.3.1}/pyproject.toml +0 -0
  98. {vssh-4.3.0 → vssh-4.3.1}/scripts/audit_transport_scan.sh +0 -0
  99. {vssh-4.3.0 → vssh-4.3.1}/scripts/cross_authorize_fleet.sh +0 -0
  100. {vssh-4.3.0 → vssh-4.3.1}/scripts/pin_fleet_keys.sh +0 -0
  101. {vssh-4.3.0 → vssh-4.3.1}/src/vssh/__init__.py +0 -0
  102. {vssh-4.3.0 → vssh-4.3.1}/src/vssh/__main__.py +0 -0
  103. {vssh-4.3.0 → vssh-4.3.1}/src/vssh/_bootstrap.py +0 -0
  104. {vssh-4.3.0 → vssh-4.3.1}/src/vssh/_cli.py +0 -0
  105. {vssh-4.3.0 → vssh-4.3.1}/src/vssh/client.py +0 -0
  106. {vssh-4.3.0 → vssh-4.3.1}/test/agent_suite.sh +0 -0
  107. {vssh-4.3.0 → vssh-4.3.1}/tests/test_python_sdk.py +0 -0
@@ -2,6 +2,43 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ - Docs (README/HELP/SECURITY + ko) updated to the key-only model (no shared
6
+ secret in quickstart, auth via `~/.vssh/authorized_keys`). pip `vssh` 4.3.1
7
+ fetches the 0.7.38 binary.
8
+
9
+ ### Security/cleanup (2026-06-14) — P4 finish: secret-free daemon, dead HMAC code removed (v0.7.38)
10
+
11
+ - `vssh server` no longer requires (or reads) a shared secret to start — auth is
12
+ purely per-node Ed25519 keys. Removed the secret-required startup gate and the
13
+ `VSSH_INSECURE_ALLOW_EMPTY_SECRET` escape hatch from `cmd/vssh`.
14
+ - Deleted the now-dead HMAC code: `server.ValidateAuthToken`,
15
+ `server.GenerateAuthToken`, the legacy HMAC-authenticated client transfer
16
+ functions (`SendFileCompressed/SendFileResume/ParallelDownload/PipeUp/PipeDown/
17
+ MultiPut/SyncFile` + helpers), and the HMAC token unit tests. The modern
18
+ `dialAuth`/`Handle*` (VAUTH1) paths are unaffected.
19
+ - `scripts/enroll.sh` is now key-only: it installs the daemon unit WITHOUT
20
+ `VSSH_SECRET` and verifies via VAUTH1 (no shared secret anywhere in onboarding).
21
+ - `go vet`/`go test` green (incl. `TestServerRejectsLegacyHMACAuth`).
22
+
23
+ ### Security (2026-06-14) — P4: key-only auth (legacy shared-secret HMAC removed)
24
+
25
+ - The daemon now accepts **only per-node Ed25519 VAUTH1**. The legacy shared-secret
26
+ (HMAC) authentication path was removed from `internal/server/server.go`: any
27
+ non-VAUTH1 auth line — including a *valid* HMAC token for the configured secret —
28
+ is rejected (`AUTH_FAILED`). This removes the entire class of risk where one
29
+ fleet-wide shared secret, if leaked, grants access, and eliminates the secret
30
+ that could be hardcoded or re-leaked.
31
+ - `dialAuth` (the canonical client path) has been VAUTH1-only since 0.7.25; the
32
+ remaining `GenerateAuthToken` callers (`transfer_advanced.go` / `sync.go`) are
33
+ legacy client functions not wired to any CLI command. The HMAC helpers remain
34
+ for now but are unreachable by the daemon.
35
+ - New regression test `TestServerRejectsLegacyHMACAuth`; `go vet`/`go test` green.
36
+ - Operational: the live fleet already enforces VAUTH at runtime
37
+ (`VSSH_REQUIRE_VAUTH=1`, incl. the m1 controller as of 2026-06-14); this makes
38
+ key-only the built-in default. Rollout (build → d1 canary → deploy_fleet → drop
39
+ `VSSH_SECRET` from units + retire legacy HMAC helpers) is deferred until all
40
+ nodes are reachable. **Not yet deployed.**
41
+
5
42
  ### Distribution (2026-06-14) — P1 one-line install pipeline (v0.7.36)
6
43
 
7
44
  - **Makefile version single-sourced** from `cmd/vssh/main.go` (was hardcoded
@@ -15,14 +52,18 @@
15
52
  console-script that downloads the matching Go release binary to `~/.vssh/bin`
16
53
  on first use (stdlib-only, checksum-verified, `VSSH_BIN`/`VSSH_VERSION`/`VSSH_HOME`
17
54
  overrides, atomic cache write) AND keeps `from vssh import VSSH` importable.
18
- Package version single-sourced from `src/vssh/_version.py` (== binary version);
55
+ Package version (`src/vssh/_version.py:__version__`) stays on PyPI's existing
56
+ `vssh` 4.x line (4.3.0) so `pip install vssh` resolves to it (highest wins); the
57
+ launcher downloads the decoupled `BINARY_VERSION` (0.7.36) GitHub release asset.
19
58
  `python -m vssh` works.
20
59
  - Verified on m1: `go vet`/`go test` green, Python SDK 8/8, `make release`
21
60
  4-arch + checksums, wheel/sdist build, fresh-venv install (SDK import +
22
61
  console-script exec), and an adversarial download test (happy path +
23
62
  tampered-checksum rejection fail-closed + `VSSH_BIN` override).
24
- - Publishing (GitHub release assets + PyPI upload) prepared but deferred to a
25
- credentialed hand-off step; see NEXT_SESSION_PROMPT.
63
+ - PUBLISHED 2026-06-14: pushed `main` + tag `v0.7.36`; GitHub release (CI-built
64
+ 4 binaries + `checksums.txt`, marked Latest); PyPI `vssh` **4.3.0**. Verified
65
+ live on m1: one-line `install.sh` (checksum-verified -> `vssh 0.7.36`) and
66
+ `pip install vssh` -> import SDK + `vssh --version` downloads 0.7.36 binary.
26
67
 
27
68
  ### Tooling (2026-06-13) — P1a one-command node onboarding
28
69
 
@@ -314,7 +355,7 @@ new-TLS); fleet `agent_suite` 38/38 GREEN over the new TLS client across
314
355
  `d1 g1 c1 v1 macmini` (linux + darwin); `handshake-test --tls` AUTH_OK against
315
356
  both a 0.7.26 (d1) and a 0.7.25 (c1) daemon; rolled out to 14 online nodes
316
357
  (`deploy_fleet.sh`), `n1 s1 s2` offline/skipped. m1 coordinator binary was on 0.7.25 during rollout, then upgraded to 0.7.26 the
317
- same day via the safe bootout/bootstrap procedure (see NEXT_SESSION_PROMPT).
358
+ same day via the safe bootout/bootstrap procedure (internal release note).
318
359
 
319
360
  ## [v0.7.25] - 2026-06-13
320
361
 
@@ -0,0 +1,44 @@
1
+ # Contributing
2
+
3
+ Thanks for helping improve `vssh`. This is a Go binary with a small Python SDK.
4
+
5
+ ## Development setup
6
+
7
+ ```bash
8
+ git clone https://github.com/meshpop/vssh && cd vssh
9
+ make build # build ./vssh
10
+ ```
11
+
12
+ Requires Go 1.25+ (and Python 3.9+ for the SDK tests).
13
+
14
+ ## Before opening a PR
15
+
16
+ ```bash
17
+ go vet ./...
18
+ go build ./...
19
+ go test ./... # Go tests
20
+ make test # Go tests + Python SDK tests
21
+ bash -n install.sh # when editing the installer
22
+ ```
23
+
24
+ CI runs the same checks. Please make sure they pass locally.
25
+
26
+ ## Pull requests
27
+
28
+ - Target `main`.
29
+ - Keep changes focused and describe behavior changes clearly — **especially
30
+ anything touching authentication, transport, policy, or `vssh server`.**
31
+ - Update `CHANGELOG.md` and bump the version in `cmd/vssh/main.go` for
32
+ user-visible changes.
33
+ - Add or update tests for new behavior.
34
+
35
+ ## Releasing
36
+
37
+ Maintainers: see [docs/PUBLISH_HANDOFF.md](docs/PUBLISH_HANDOFF.md) for the
38
+ release process (tag → CI builds the GitHub release → PyPI upload).
39
+
40
+ ## Security
41
+
42
+ Report vulnerabilities privately via
43
+ [GitHub Security Advisories](https://github.com/meshpop/vssh/security/advisories/new),
44
+ not public issues or PRs.
vssh-4.3.1/HELP.md ADDED
@@ -0,0 +1,127 @@
1
+ # vssh — help & usage
2
+
3
+ `vssh` is an sshd-free, AI-native remote execution daemon for private networks.
4
+ It runs commands, transfers files, and manages jobs on remote nodes over TLS 1.3
5
+ with Ed25519 key authentication, returning structured execution evidence.
6
+
7
+ - No `sshd` required on the target
8
+ - Built-in PTY, typed RPC, file transfer, and long-running jobs
9
+ - TLS 1.3 + Ed25519 key auth (legacy shared-secret path for bootstrap)
10
+ - Node-name routing over Tailscale, Wire/WireGuard, LAN, or configured addresses
11
+ - A built-in MCP server (`vssh mcp`) for AI agents
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ curl -fsSL https://raw.githubusercontent.com/meshpop/vssh/main/install.sh | bash
17
+ # or
18
+ pip install vssh
19
+ ```
20
+
21
+ ## Quick start
22
+
23
+ ```bash
24
+ # On the target node (no shared secret — key-authenticated)
25
+ vssh server # listens on :48291
26
+
27
+ # On the client: print your key, add it to the server's ~/.vssh/authorized_keys
28
+ vssh pubkey
29
+ vssh run web1 "uptime" # structured result
30
+ vssh web1 # interactive shell
31
+ vssh doctor --json # diagnose before AI/MCP use
32
+ ```
33
+
34
+ ## Command reference
35
+
36
+ ### Execution
37
+ ```bash
38
+ vssh <node> # interactive PTY shell
39
+ vssh shell <node> # interactive PTY shell
40
+ vssh run <node> <command> # run a command (structured result)
41
+ vssh exec <node> <command> # alias for run
42
+ vssh run-many <n1,n2> <cmd> # run across comma-separated nodes
43
+ vssh run-batch <node> ... # run several commands on one session
44
+ ```
45
+
46
+ ### Typed APIs
47
+ ```bash
48
+ vssh rpc <node> <method> [json] # call a typed daemon RPC
49
+ vssh rpc-many <nodes> <method> [json] # RPC across nodes
50
+ vssh facts <node> # typed host facts
51
+ vssh facts-many <nodes> # facts across nodes
52
+ ```
53
+
54
+ ### Jobs (long-running)
55
+ ```bash
56
+ vssh job-start <node> <command> # start a background job
57
+ vssh job-status <node> <id> # job status
58
+ vssh job-logs <node> <id> # job logs
59
+ vssh job-cancel <node> <id> # cancel a job
60
+ vssh artifact-collect <node> # collect output artifacts
61
+ ```
62
+
63
+ ### Files
64
+ ```bash
65
+ vssh put <local> <node:path> # upload
66
+ vssh get <node:path> <local> # download
67
+ ```
68
+
69
+ ### Fleet & ops
70
+ ```bash
71
+ vssh # dashboard (default)
72
+ vssh status # dashboard
73
+ vssh list # list known nodes
74
+ vssh doctor [--json] # diagnose binary, secret, config, peers, MCP readiness
75
+ vssh deploy <node> # atomic binary install + restart + verify
76
+ vssh server # run the daemon
77
+ vssh mcp # run the MCP server (for AI agents)
78
+ vssh setup # first-run self-configuration
79
+ vssh version # show version
80
+ vssh help # full help
81
+ ```
82
+
83
+ ## Discovery
84
+
85
+ `vssh` finds nodes from, in order: a Wire coordinator, Tailscale, the config
86
+ file (`~/.vssh/servers.json`), and a local cache. You can always address a node
87
+ by an explicit IP as well.
88
+
89
+ ## Configuration
90
+
91
+ ### Node inventory — `~/.vssh/servers.json`
92
+ ```json
93
+ {
94
+ "web1": { "ip": "192.0.2.10", "user": "deploy" },
95
+ "db1": { "ip": "192.0.2.20", "user": "postgres" }
96
+ }
97
+ ```
98
+
99
+ ### Per-host users — `~/.wire/users.json` (root: `/etc/wire/users.json`)
100
+ ```json
101
+ { "web1": "deploy", "db1": "postgres" }
102
+ ```
103
+
104
+ > Keep real inventory, hostnames, VPN IPs, and secrets **out of source control**.
105
+
106
+ ## Environment variables
107
+
108
+ | Variable | Description |
109
+ |----------|-------------|
110
+ | `VSSH_PORT` | Daemon listen port (default **48291**). |
111
+ | `VSSH_REQUIRE_TLS` | `1` = refuse non-TLS connections. |
112
+ | `VSSH_NO_HOSTKEY_VERIFY` | `1` = opt out of host-identity verification (not recommended). |
113
+ | `VSSH_BIN` / `VSSH_VERSION` / `VSSH_HOME` | pip-wrapper/installer overrides (see README). |
114
+
115
+ ## Security
116
+
117
+ `vssh server` authenticates peers with **TLS 1.3 + Ed25519 keys (VAUTH1)** only —
118
+ no shared secret. Authorize clients via `~/.vssh/authorized_keys`. Commands can be
119
+ gated with optional per-key policy. The VPN encrypts the tunnel but does **not**
120
+ replace vssh authentication. Full details and
121
+ a hardening checklist: [SECURITY.md](SECURITY.md).
122
+
123
+ ## More
124
+
125
+ - MCP / agent orchestration: [docs/CODEX_ORCHESTRATION.md](docs/CODEX_ORCHESTRATION.md)
126
+ - Python SDK: [docs/PYTHON_SDK.md](docs/PYTHON_SDK.md)
127
+ - Why vssh: [docs/WHY_VSSH.md](docs/WHY_VSSH.md)
vssh-4.3.1/PKG-INFO ADDED
@@ -0,0 +1,322 @@
1
+ Metadata-Version: 2.4
2
+ Name: vssh
3
+ Version: 4.3.1
4
+ Summary: vssh — AI-native, drop-in ssh replacement (CLI binary + Python SDK)
5
+ Project-URL: Homepage, https://github.com/meshpop/vssh
6
+ Project-URL: Repository, https://github.com/meshpop/vssh
7
+ Author: MeshPop
8
+ License: MIT
9
+ Keywords: ai-agents,mcp,remote-execution,ssh,tailscale,vssh
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: System :: Systems Administration
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+
22
+ # vssh
23
+
24
+ **An sshd-free, AI-native remote execution daemon for private networks.**
25
+
26
+ `vssh` runs commands, transfers files, and manages long-running jobs on remote
27
+ nodes over your private network (Tailscale, WireGuard/Wire, or LAN) — without
28
+ running `sshd` on the target. It speaks a typed protocol over TLS 1.3 with
29
+ Ed25519 key authentication, and every result comes back as structured evidence
30
+ (stdout, stderr, exit code, duration, transport), which makes it a natural
31
+ execution layer for AI agents and automation as well as for humans.
32
+
33
+ ```bash
34
+ # Install (Linux/macOS, amd64/arm64)
35
+ curl -fsSL https://raw.githubusercontent.com/meshpop/vssh/main/install.sh | bash
36
+ # or
37
+ pip install vssh
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Why vssh
43
+
44
+ Use `vssh` when the operator is often an **AI agent or automation runtime**, not
45
+ a person typing into a terminal, and when you want execution that is:
46
+
47
+ - **sshd-free** — the target runs `vssh server`, not OpenSSH.
48
+ - **typed & auditable** — structured `stdout/stderr/exit/duration` evidence, not a raw text stream.
49
+ - **policy-gated** — classify and block dangerous commands before they run.
50
+ - **durable** — start/poll/cancel long-running jobs and collect artifacts.
51
+ - **name-routed** — address nodes by name and capability, not raw IPs.
52
+
53
+ If all you need is an interactive human shell on a box that already runs `sshd`,
54
+ use `ssh` directly — `vssh` deliberately does not wrap OpenSSH. See
55
+ [docs/WHY_VSSH.md](docs/WHY_VSSH.md).
56
+
57
+ **Out of scope:** operating the VPN mesh and fleet dashboards. Use
58
+ [Wire](https://github.com/meshpop/wire) for the network layer and
59
+ [mpop](https://github.com/meshpop/mpop) for monitoring.
60
+
61
+ ---
62
+
63
+ ## Install
64
+
65
+ ### One-line installer (recommended)
66
+
67
+ ```bash
68
+ curl -fsSL https://raw.githubusercontent.com/meshpop/vssh/main/install.sh | bash
69
+ ```
70
+
71
+ The installer detects your OS/arch, downloads the matching binary from the
72
+ [latest GitHub release](https://github.com/meshpop/vssh/releases/latest),
73
+ **verifies its SHA-256 against the published `checksums.txt`**, and installs to
74
+ `~/bin`. Options:
75
+
76
+ ```bash
77
+ # Install a specific version
78
+ curl -fsSL .../install.sh | VSSH_VERSION=0.7.36 bash
79
+ # Install to a custom directory
80
+ curl -fsSL .../install.sh | INSTALL_DIR=/usr/local/bin bash
81
+ ```
82
+
83
+ ### pip (CLI + Python SDK)
84
+
85
+ ```bash
86
+ pip install vssh
87
+ ```
88
+
89
+ This installs both the `vssh` CLI (the Go binary is fetched and checksum-verified
90
+ for your platform on first run, cached under `~/.vssh/bin`) **and** the Python
91
+ SDK (`from vssh import VSSH`). See [Python SDK](#python-sdk).
92
+
93
+ ### From source
94
+
95
+ ```bash
96
+ git clone https://github.com/meshpop/vssh && cd vssh
97
+ make build # builds ./vssh
98
+ make install # installs to /usr/local/bin (sudo)
99
+ ```
100
+
101
+ Requires Go 1.25+.
102
+
103
+ ---
104
+
105
+ ## Quick start
106
+
107
+ On the **target** node, start the daemon (no shared secret — auth is key-based):
108
+
109
+ ```bash
110
+ vssh server # listens on :48291
111
+ ```
112
+
113
+ Authorize a client by adding its public key to the server's
114
+ `~/.vssh/authorized_keys` (run `vssh pubkey` on the client to print it). For a
115
+ fleet, `scripts/enroll.sh <node>` does this from a controller automatically.
116
+ Then, from the client:
117
+
118
+ ```bash
119
+ vssh run web1 "df -h" # run a command, get structured output
120
+ vssh web1 # open an interactive shell
121
+ vssh put ./app web1:/tmp/ # upload a file
122
+ vssh get web1:/var/log/x . # download a file
123
+ vssh # fleet dashboard
124
+ vssh doctor --json # diagnose binary, secret, config, peers, MCP
125
+ ```
126
+
127
+ `vssh run` returns an evidence envelope — exit code, durations, transport, and
128
+ the endpoints it tried — not just raw text.
129
+
130
+ > For a multi-node fleet, `scripts/enroll.sh <node>` installs the daemon and
131
+ > cross-authorizes keys from a controller. See [Security](#security).
132
+
133
+ ---
134
+
135
+ ## How it works
136
+
137
+ ```text
138
+ vssh client ──TLS 1.3──▶ vssh server ──▶ typed exec / file / job / RPC APIs ──▶ structured evidence
139
+ (Ed25519 pinned) (:48291)
140
+ ```
141
+
142
+ 1. **Transport** — TLS 1.3 with the daemon's **Ed25519 public key pinned**
143
+ (raw-key, not a CA). The client is TLS-first; set `VSSH_REQUIRE_TLS=1` to
144
+ refuse plaintext entirely.
145
+ 2. **Host identity** — the client verifies it reached the *intended* host by
146
+ checking the daemon key against a trusted registry (on by default since
147
+ 0.7.33), so a name can't be silently misrouted to the wrong machine.
148
+ 3. **Authentication** — per-node **Ed25519 challenge–response (VAUTH1)** only.
149
+ There is no shared secret; a client is authorized by listing its public key in
150
+ the server's `~/.vssh/authorized_keys` (or `/etc/vssh/authorized_keys`).
151
+ 4. **Policy** — commands can be classified and gated before execution; per-key
152
+ allow/deny lists, path scoping, and rate limits are available opt-in.
153
+
154
+ ---
155
+
156
+ ## CLI reference
157
+
158
+ ```
159
+ Execution
160
+ vssh <node> Interactive PTY shell
161
+ vssh shell <node> Interactive PTY shell
162
+ vssh run <node> <cmd> Run a command (structured result)
163
+ vssh exec <node> <cmd> Alias for run
164
+ vssh run-many <n1,n2> <cmd> Run across comma-separated nodes
165
+ vssh run-batch <node> ... Run multiple commands on one session
166
+
167
+ Typed APIs
168
+ vssh rpc <node> <method> [json] Call a typed daemon RPC
169
+ vssh rpc-many <nodes> <method> [json] RPC across nodes
170
+ vssh facts <node> Typed host facts
171
+ vssh facts-many <nodes> Facts across nodes
172
+
173
+ Jobs (long-running)
174
+ vssh job-start <node> <cmd> Start a background job
175
+ vssh job-status <node> <id> Job status
176
+ vssh job-logs <node> <id> Job logs
177
+ vssh job-cancel <node> <id> Cancel a job
178
+ vssh artifact-collect <node> Collect output artifacts
179
+
180
+ Files
181
+ vssh put <local> <node:path> Upload
182
+ vssh get <node:path> <local> Download
183
+
184
+ Fleet & ops
185
+ vssh Dashboard (default)
186
+ vssh status Dashboard
187
+ vssh list List known nodes
188
+ vssh doctor [--json] Diagnose local setup
189
+ vssh deploy <node> Atomic binary install + restart + verify
190
+ vssh server Run the daemon
191
+ vssh mcp Run the MCP server (for AI agents)
192
+ vssh setup First-run self-configuration
193
+ vssh version Show version
194
+ vssh help Full help
195
+ ```
196
+
197
+ ---
198
+
199
+ ## MCP server (for AI agents)
200
+
201
+ The MCP server is built into the binary — no separate install:
202
+
203
+ ```bash
204
+ vssh mcp
205
+ ```
206
+
207
+ It exposes typed tools that return execution evidence, so an agent can route,
208
+ gate, and run work with an audit trail:
209
+
210
+ | Tool | Purpose |
211
+ |------|---------|
212
+ | `vssh_doctor` | Diagnose binary, secret source, config, peers, MCP readiness |
213
+ | `vssh_hosts_list` | List known hosts (addresses, tags, capabilities, health) for routing |
214
+ | `vssh_route_select` | Pick the best host by capability, tag, and health |
215
+ | `vssh_exec_safe` | Run read-only/diagnostic commands (dangerous ones blocked) |
216
+ | `vssh_exec` | Run with policy checks; `allow_dangerous` after explicit approval |
217
+ | `vssh_exec_routed` | Route first, then execute with policy + evidence |
218
+ | `vssh_policy_check` | Classify a command before running it |
219
+ | `vssh_status`, `vssh_list` | Status and peer inventory |
220
+
221
+ Commands matching destructive patterns (`rm -rf`, `shutdown`, `reboot`,
222
+ `docker rm`, `kubectl delete`, `systemctl restart`, …) are **blocked** unless the
223
+ caller sets `allow_dangerous: true` after explicit human approval. Every response
224
+ is an evidence envelope with timestamps, the policy decision, target, command,
225
+ and the structured result. See [docs/CODEX_ORCHESTRATION.md](docs/CODEX_ORCHESTRATION.md).
226
+
227
+ ---
228
+
229
+ ## Python SDK
230
+
231
+ ```python
232
+ from vssh import VSSH
233
+
234
+ client = VSSH(secret="a-long-random-value")
235
+
236
+ client.exec("web1", "uptime") # -> ExecResult(stdout, exit_code, ...)
237
+ client.exec_many(["web1", "db1"], "uptime")
238
+ client.facts("web1") # typed host facts
239
+ job = client.job_start("web1", "long-task")
240
+ client.job_status("web1", job["job_id"])
241
+ client.doctor()
242
+ ```
243
+
244
+ The SDK is a thin client over the installed `vssh` binary (it does not
245
+ reimplement the protocol), so it inherits the same transport, auth, and policy.
246
+ Full method list: `exec`, `exec_many`, `rpc`, `rpc_many`, `facts`, `facts_many`,
247
+ `job_start`, `job_status`, `job_logs`, `job_cancel`, `artifact_collect`,
248
+ `doctor`. See [docs/PYTHON_SDK.md](docs/PYTHON_SDK.md).
249
+
250
+ ---
251
+
252
+ ## Configuration
253
+
254
+ ### Node inventory — `~/.vssh/servers.json`
255
+
256
+ ```json
257
+ {
258
+ "web1": { "ip": "192.0.2.10", "user": "deploy", "tags": ["linux", "web"], "capabilities": ["docker"] },
259
+ "gpu1": { "ip": "192.0.2.20", "user": "ubuntu", "tags": ["gpu"], "capabilities": ["cuda", "ollama"], "monitor_port": 8721 }
260
+ }
261
+ ```
262
+
263
+ Nodes are also discovered automatically from a Wire coordinator, Tailscale, and a
264
+ local cache. **Do not commit a real `servers.json`, hostnames, VPN IPs, or
265
+ secrets** — keep inventory outside the repo and use example values in docs.
266
+
267
+ ### Per-host users — `~/.wire/users.json`
268
+
269
+ ```json
270
+ { "web1": "deploy", "db1": "postgres" }
271
+ ```
272
+
273
+ ### Environment variables
274
+
275
+ | Variable | Purpose |
276
+ |----------|---------|
277
+ | `VSSH_PORT` | Daemon listen port (default **48291**). |
278
+ | `VSSH_REQUIRE_TLS` | `1` = refuse non-TLS connections. |
279
+ | `VSSH_NO_HOSTKEY_VERIFY` | `1` = opt out of host-identity verification (not recommended). |
280
+ | `VSSH_BIN` | (pip wrapper) use this binary instead of downloading. |
281
+ | `VSSH_VERSION` | (pip wrapper / installer) pin the binary release to fetch. |
282
+ | `VSSH_HOME` | Override the `~/.vssh` directory. |
283
+
284
+ ---
285
+
286
+ ## Security
287
+
288
+ - The native daemon grants authorized peers command execution and file transfer
289
+ **as the configured user** — treat access as root-equivalent.
290
+ - Authentication is **per-node Ed25519 keys (VAUTH1)** only; authorize clients via
291
+ `~/.vssh/authorized_keys`. Enforce encryption with `VSSH_REQUIRE_TLS=1`.
292
+ - The VPN (WireGuard/Tailscale) encrypts the tunnel but is **not** a substitute
293
+ for vssh authentication — always set a strong secret or enroll keys, and
294
+ firewall the listen port.
295
+ - Report vulnerabilities privately via
296
+ [GitHub Security Advisories](https://github.com/meshpop/vssh/security/advisories/new).
297
+
298
+ Full model and hardening steps: [SECURITY.md](SECURITY.md).
299
+
300
+ ---
301
+
302
+ ## Documentation
303
+
304
+ - [Why vssh](docs/WHY_VSSH.md) — positioning and scope
305
+ - [Python SDK](docs/PYTHON_SDK.md) — SDK reference
306
+ - [Codex / agent orchestration](docs/CODEX_ORCHESTRATION.md) — MCP usage for agents
307
+ - [SECURITY.md](SECURITY.md) · [CONTRIBUTING.md](CONTRIBUTING.md) · [CHANGELOG.md](CHANGELOG.md)
308
+ - [한국어 README](README.ko.md)
309
+
310
+ ## Building & contributing
311
+
312
+ ```bash
313
+ make build # build ./vssh
314
+ make test # go test ./... + Python SDK tests
315
+ make release # cross-compile linux/darwin × amd64/arm64 into dist/
316
+ ```
317
+
318
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
319
+
320
+ ## License
321
+
322
+ MIT