dsh-ssh-forward
Verifieddsh-ssh-forward · v0.2.0 · MIT · Web UI
Keep an SSH local port forward (for example searxng on 127.0.0.1:8080) alive for DeepSeek Harness — configured entirely from a built-in Settings page, with lease-based ownership so several profiles can run at once.
Install
dsh plugin add dsh-ssh-forward Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Published to npm without a public repository. Inspect the package contents before installing.
Tags
Creators
Readme
dsh-ssh-forward
Keep a local SSH port forward alive for DeepSeek Harness — typically
127.0.0.1:8080 → <host>:127.0.0.1:8080 so the searxng behind a private host is reachable from
dsh-thin-search.
Everything is configured from the DSH UI. The plugin ships its own Settings page (Plugins → this plugin → Configure); saving writes the current profile and restarts the tunnel. No YAML, no JSON, no hand editing. It is a standard DSH plugin: a cordis entry plus a bundle patch, no native code, no build step, nothing written into the DSH installation directory.
What it does
- Owner / adopter. Exactly one host owns the tunnel. The owner writes a lease
(
$DSH_HOME/ssh-tunnel/lease.json, pid + heartbeat), spawnsssh, and removes the lease plus the child on exit. Every other host adopts an already-listening port and never kills it. keepAlive—desktop(default: only the desktop profile creates it),any(first host wins),none(reuse only). The default keeps the tunnel's lifetime attached to DSH desktop.- Stale leases are reclaimed by rename, so two hosts can never both reap the same record.
- Failure is never fatal — a missing ssh, a bad host or a timeout only logs a warning; the watch pass retries later.
Settings page
| Section | Contents |
|---|---|
| Connection source | system ~/.ssh/config · a custom config file · DSH-only (generated) |
| Host & forward | Host alias (dropdown parsed from that file); forward mode: the config's LocalForward or an explicit -L |
| DSH-only fields | shown only for the DSH source: host name, user, port, identity file, ProxyJump |
| Behaviour | who may own the tunnel, auto-repair, tunnel_status tool, connect timeout |
| Advanced | ssh executable, extra args, readiness timeout, watch interval |
The status card on top shows state, listen address, lease owner, last error, config problems, and a Restart tunnel button.
Connection sources:
- system — reads
~/.ssh/configas-is; ssh finds it itself; the host dropdown is parsed from it. - custom — an absolute path passed with
-F. - DSH-only — the plugin generates
$DSH_HOME/ssh-forward/ssh_configfrom the form fields, for people who do not want to touch the system config or want separate credentials for DSH.
Install
dsh plugin --profile desktop add dsh-ssh-forward
dsh plugin --profile web add dsh-ssh-forward
Restart DSH, then fill in the connection fields on the Settings page.
Development
pnpm install
pnpm test # unit: lease, probe, state machine, ssh config parsing, argv
pnpm integration # real child process + real port + real lease (owner path)
pnpm check # syntax + unit + integration
pnpm e2e # boots a real DSH host through the full lifecycle (needs a normal shell)
Layout:
lib/index.js cordis entry: volatile Config, manager wiring, settings bridge, restart
lib/client.js browser half: the Settings page (plugins.row.config slot)
lib/manager.js the state machine (dependency-injected, unit tested)
lib/lease.js lease: liveness, heartbeat, stale reclaim
lib/config.js config snapshot, normalisation, validation
lib/sshconfig.js ssh config parsing / DSH-only generation / argv
lib/settings-bridge.js /api/dsh-ssh-forward-settings/* routes
lib/probe.js TCP readiness probe
lib/spawner.js ctx.subprocess or child_process
Publish
The registry on this machine is an install mirror, so publishing must be explicit:
pnpm login --registry=https://registry.npmjs.org
pnpm publish --registry=https://registry.npmjs.org --access public --no-git-checks
License
MIT. See LICENSE.