@hahahhh/sshx 0.0.5 → 0.0.6-rc.0

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 (2) hide show
  1. package/README.md +46 -24
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  > Transparent SSH enhancement — add remote-to-local commands, auto port forwarding, and local domains to your SSH workflow. Zero side effects when you don't need them.
4
4
 
5
- **sshx** is a drop-in wrapper around OpenSSH. Wrap it as `alias ssh=sshx` and your existing SSH workflow works exactly as before — every flag, config, and connection passes through verbatim. But when you connect to a host (or Docker container) with sshx-aware features enabled, you unlock a persistent, shared remote server that gives you:
5
+ **sshx** is a drop-in wrapper around OpenSSH. Wrap it as `alias ssh=sshx` and your existing SSH workflow works exactly as before — every flag, config, and connection passes through verbatim. But when you connect to a host (or Docker container) with sshx-aware features enabled, you unlock a connection-scoped shared remote server that gives you:
6
6
 
7
7
  - 🔄 **Reverse command bridge** — run `sshx local <cmd>` *on the remote* to execute commands on your local machine, with stdout, stderr, exit code, and stdin all propagated.
8
- - 📁 **Bidirectional home mount** — opt in to mount the command initiator's home while preserving the source path hierarchy and working directory.
8
+ - 📁 **Remote-to-local filesystem** — opt in to let local tools work directly with files from the active remote session.
9
9
  - 🔌 **Automatic port forwarding** — remote local listeners (loopback `127.0.0.1` and wildcard `0.0.0.0`; e.g., a dev server on `0.0.0.0:8080` or `localhost:8080`) are automatically detected and forwarded to your local machine.
10
10
  - 🌐 **Local domain binding** — access forwarded ports as `<host>.<your-user>.sshx:<port>` in your local browser, no manual `-L` flags needed.
11
11
  - 🐳 **Docker container support** — target running containers by name or ID: `sshx my-container`. Command bridge support works inside containers via `docker exec`.
@@ -24,6 +24,14 @@ sshx is designed to be **safe to alias**. Hosts without sshx configuration are u
24
24
 
25
25
  ---
26
26
 
27
+ ## Architecture
28
+
29
+ ![sshx system architecture](docs/architecture.svg)
30
+
31
+ Each user-visible session remains a normal OpenSSH connection (or a `docker exec` session for container targets). One hidden multiplexed sidecar channel carries control and optional RemoteFS traffic to a shared target-side server, while one on-demand local daemon owns DNS records and TCP forwards across active client terminals. Automatic port forwarding uses SSH `direct-tcpip`; Docker targets support shells and the command bridge but do not use automatic port scanning.
32
+
33
+ ---
34
+
27
35
  ## Features
28
36
 
29
37
  ### 📦 Drop-in Compatibility
@@ -60,17 +68,17 @@ sshx local --timeout=30 npm test
60
68
  - Commands have no implicit deadline. Put `--timeout=<duration>` immediately after the target to opt in; bare numbers mean seconds, and values such as `500ms`, `30s`, and `2m` are accepted. Timed-out commands exit with status 124.
61
69
  - Policy: a configurable deny list controls which commands are blocked.
62
70
 
63
- ### 📁 Bidirectional Workspace Mount (opt-in, beta)
71
+ ### 📁 Remote-to-Local Filesystem (opt-in, beta)
64
72
 
65
- Set `features.remoteFs: true` to expose the command initiator's home directory through a read-write FUSE mount. Its absolute hierarchy is preserved below the managed session directory (for example, `/Users/xiaot` becomes `<session>/Users/xiaot`):
73
+ Set `features.remoteFs: true` to let a command launched with `sshx local <cmd>` access the remote working tree through a local FUSE mount:
66
74
 
67
- - `sshx remote <cmd>` starts the remote command at the corresponding path inside the mounted local home.
68
- - An interactive `sshx remote` shell still starts in the remote home. `SSHX_MOUNT_ROOT` points to the mounted source root and `SSHX_WORKSPACE` points to the mapped source working directory.
69
- - From that shell, `sshx local <cmd>` mounts the remote home locally and starts the local command at the corresponding mapped path.
70
- - If the source working directory is outside its home, sshx exports that directory as a safe fallback and still preserves its absolute hierarchy below the session directory.
71
- - Writes and `fsync` are sent to the source immediately. Metadata and directory entries use short TTLs and are revalidated when files are reopened.
75
+ - The remote home is exported lazily on the first local command. A working directory outside the remote home creates a separate lazy export for that root.
76
+ - The local command starts at the corresponding mounted working directory. Absolute command arguments are not rewritten.
77
+ - Mounts are reused for the sidecar session, so detached tools such as `open` and `code` can continue reading files after the bridge request returns.
78
+ - Local files are never exported or mounted on the remote host.
79
+ - With RemoteFS disabled, the command runs from the local home and receives `SSHX_REMOTE_CWD` plus `SSHX_REMOTE_FS=0`.
72
80
 
73
- The mounted home permits reads, writes, and creation, but blocks file/directory deletion and rename in both directions. It can still include sensitive files such as shell configuration and SSH credentials, so enable `remoteFs` only for targets you trust. sshx excludes its managed mount tree from reverse exports to avoid recursive mounts.
81
+ The mounted remote tree permits reads, writes, and creation, but blocks file/directory deletion and rename. It can include sensitive remote files such as shell configuration and SSH credentials, so enable `remoteFs` only for targets you trust.
74
82
 
75
83
  Set `FS_READ_ONLY=1` on the client when starting sshx to make the session mounts read-only:
76
84
 
@@ -78,15 +86,15 @@ Set `FS_READ_ONLY=1` on the client when starting sshx to make the session mounts
78
86
  FS_READ_ONLY=1 sshx debian@orb pwd
79
87
  ```
80
88
 
81
- The client value is exported into the remote session. A later `sshx local <cmd>` inherits it, so the reverse mount is read-only as well.
89
+ The value is exported into the remote session, and later `sshx local <cmd>` mounts remain read-only.
82
90
 
83
- FUSE is a hard dependency when this feature is enabled: a mount failure aborts the command even when `strict` is false. Linux needs `/dev/fuse` plus `fusermount`/`fusermount3`; macOS needs a current macFUSE installation and is currently beta. The remote target must be Linux with FUSE available.
91
+ FUSE is required only on the local machine receiving the mounted remote tree. A mount failure fails that `sshx local` invocation instead of running it from the wrong directory. Linux needs `/dev/fuse` plus `fusermount`/`fusermount3`; macOS needs a current macFUSE installation and is currently beta.
84
92
 
85
93
  #### FUSE setup
86
94
 
87
- The machine receiving the mounted view needs a working FUSE runtime. The Linux target therefore always needs FUSE for `sshx remote`; a macOS client also needs macFUSE when a remote shell runs `sshx local` and mounts the remote working directory back on the Mac.
95
+ The local machine receiving the mounted view needs a working FUSE runtime. The remote Linux host exports files through the sshx protocol and does not need FUSE.
88
96
 
89
- **Linux target/client**
97
+ **Linux client**
90
98
 
91
99
  Install the FUSE 3 userspace tools (the kernel normally already includes the FUSE driver):
92
100
 
@@ -153,12 +161,13 @@ The URL port is the remote port. sshx does not bind `127.0.0.1:<port>`; it binds
153
161
 
154
162
  ### 🏗️ Shared Server Architecture
155
163
 
156
- - One **server daemon** per client target alias, installed under `~/.sshx_server/<uuid>` on the remote and shared by that client's concurrent SSH sessions.
157
- - Client connects via a hidden `socket-proxy` SSH channel.
164
+ - Compatible runtime daemons live under `~/.sshx_server/targets/<TargetID>/runtimes/<RuntimeID>` and serve multiple application contexts and sessions.
165
+ - Client connects through one hidden multiplexed sidecar SSH channel.
158
166
  - Server manages port sniffing, forwarding state, and command bridge routing centrally.
159
167
  - Clients renew local and remote leases every 5 seconds. A daemon expires a client after 15 seconds without a heartbeat.
160
- - The local daemon exits when its last client lease closes. The remote server drains briefly and exits after its last bridge lease closes.
161
- - Application or protocol version changes drain the existing daemon before the current binary starts a replacement.
168
+ - The on-demand local daemon and remote server keep a 10-second handoff window after their last lease, then exit.
169
+ - AppVersion is diagnostic only. Compatible clients coexist; incompatible RuntimeIDs use separate directories and daemons.
170
+ - `ClientInstallID` is stable across upgrades; `TargetID` identifies normalized OpenSSH destinations, `ContextID` identifies stable application contexts, and every live sidecar gets a fresh `SessionID`.
162
171
 
163
172
  ---
164
173
 
@@ -181,6 +190,19 @@ npm install -g @hahahhh/sshx
181
190
 
182
191
  The npm wrapper auto-downloads the correct native binary for your platform from GitHub Releases.
183
192
 
193
+ ### VS Code / Cursor Remote SSH
194
+
195
+ Install the CLI normally, then run the single integration command for each application you use:
196
+
197
+ ```sh
198
+ sshx integrate install vscode
199
+ sshx integrate install cursor
200
+ ```
201
+
202
+ The command locates OpenSSH, installs paired `ssh`/`scp` shims backed by the same sshx binary, patches JSONC `remote.SSH.path`, and verifies the complete invocation chain. It is idempotent and rolls back settings and shims on failure; run it again to repair the integration after moving the sshx executable. No restart, integration-specific configuration, or extra binary is required.
203
+
204
+ Remote-SSH remains the user-visible OpenSSH connection. sshx creates a temporary ControlMaster so bootstrap, sidecar, forwarding, and scp reuse the same authentication. Only a stable `SSHX_CONTEXT_ID` and context-launcher path are injected into application terminals. Probe, unknown, and ambiguous calls are exact passthrough; an incompatible or failed sidecar receives the original bootstrap bytes and preserves normal OpenSSH behavior regardless of `strict`.
205
+
184
206
  ### Download Binary
185
207
 
186
208
  Download the prebuilt binary directly from [GitHub Releases](https://github.com/xiaot623/sshx/releases):
@@ -292,8 +314,8 @@ features:
292
314
  # <host>.<user>.sshx:<remote-port>.
293
315
  autoForward: true
294
316
 
295
- # Read-write workspace mounts in both command directions. Default: false.
296
- # Requires FUSE on the local machine and remote target.
317
+ # Lazily mount remote files for local commands. Default: false.
318
+ # Requires FUSE only on the local machine.
297
319
  remoteFs: false
298
320
 
299
321
  commands:
@@ -319,8 +341,8 @@ commands:
319
341
  └─────────────────────────────────┘ └─────────────────────────────────┘
320
342
  ```
321
343
 
322
- 1. **Connection**: `sshx remote` opens a normal SSH session and starts (or connects to) the client-target `sshx server` under `~/.sshx_server/<uuid>`.
323
- 2. **Bridge channels**: A hidden control `socket-proxy` channel links the client to the remote server. `remoteFs` adds a separately framed, bounded data channel paired by session ID.
344
+ 1. **Connection**: `sshx remote` opens a normal SSH session and starts a compatible runtime under `~/.sshx_server/targets/<TargetID>/runtimes/<RuntimeID>`.
345
+ 2. **Sidecar channel**: One hidden SSH channel multiplexes command, port, heartbeat, and optional RemoteFS traffic. ContextID routes VS Code/Cursor terminals to a healthy live session.
324
346
  3. **Port sniffing**: The server reads `/proc/net/tcp*` (Linux) to detect loopback (`127.0.0.1` / `::1`) and wildcard (`0.0.0.0` / `::`) listeners.
325
347
  4. **Forwarding**: Detected ports are forwarded through a single shared local daemon using `ssh -W`.
326
348
  5. **Domains**: The local DNS responder maps `<target>.<suffix>` → localhost. The browser's URL port selects the local forwarded port.
@@ -335,7 +357,7 @@ When `sshx` is invoked for a **non-matching host** (no sshx config, or host not
335
357
  - `SSHX_DISABLE=1 sshx ...` — same as `--no-wrap`, useful in scripts.
336
358
  - `sshx local ...` on a **client** (not inside a remote session) — errors immediately with a clear message. `local` is globally reserved.
337
359
  - `remoteFs` never silently falls back to an unmounted command. A failed FUSE mount fails the invocation.
338
- - Workspace exports are anchored with Go's `os.Root`; path traversal and symlink escapes are rejected.
360
+ - Remote exports are anchored with Go's `os.Root`; path traversal and symlink escapes are rejected.
339
361
  - Docker containers that aren't running or can't be reached are pure passthrough — sshx falls back to raw `ssh` with no side effects.
340
362
  - Unmatched hosts are pure passthrough — no files created, no processes started.
341
363
 
@@ -384,7 +406,7 @@ sshx/
384
406
 
385
407
  - [x] **v1** — Command bridge (non-interactive), auto port forwarding, domain binding, shared server
386
408
  - [ ] **v2** — Streaming stdin for command bridge, GitHub binary releases, Windows client support
387
- - [x] **remoteFs beta** — Bidirectional read-write workspace mounting on Linux/macOS clients and Linux targets
409
+ - [x] **remoteFs beta** — Lazy remote-to-local workspace mounting on Linux/macOS clients
388
410
 
389
411
  ---
390
412
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hahahhh/sshx",
3
- "version": "0.0.5",
3
+ "version": "0.0.6-rc.0",
4
4
  "type": "module",
5
5
  "description": "Transparent SSH enhancement wrapper for OpenSSH",
6
6
  "repository": {