- lib/edge-auth.mjs: basic / bearer / oauth2 client_credentials (Authentik M2M) / custom headers (Cloudflare Access), proxy session cookie jar, retry with fresh token, clear edge-refusal vs DSH-refusal errors - link + enroll go through the edge; master.edgeAuth stored at pair time, secrets as DSH credential refs only; config edgeAuth override - /dsh-remote/edge-auth: show/change/probe; Settings UI section + pair form - pair.mjs / install-mac.sh: --edge … flags, secrets prompted via /dev/tty - tests: fake Authentik-like proxy for all modes + WebSocket through the edge
17 KiB
dsh-remote
Remote control for DeepSeek Harness, in the style of Claude Desktop: one master and any number of slaves.
- Every session on every device. Sessions run on the machine that created them. From any device you can browse all of them, open them, and chat in them; the work still executes on the owning machine.
- The full remote UI at
/r/<device>/. The whole DSH UI of another device is proxied over the link: HTTP, the Remote WebSocket, SSE, plugin bundles and terminals. Slave→slave goes through the master's relay. - Plugin and config sync, master → slaves. Slaves get the master's npm/registry bundles, its local
link:plugins (packed and shipped over the link) and itscordis.patch.yml. The config lands in a managed bundle layer, so each slave's owncordis.patch.ymlstill wins and slave-only plugins stay untouched. - Opt-in secrets. A credential (
OMNI_API_KEY, …) reaches a slave only if three places agree: the master offers it, the master enables it for that device, and the slave accepts it. - Devices UI. A sidebar panel lists every device and its sessions (live running state, filter, "New session on "). A "Remote devices" settings page handles pairing, revocation and per-device sync policy. You get a toast when a session on any device finishes.
No dependencies: the WebSocket, the multiplexer and the proxy are plain Node.
slave (laptop) ──outbound WebSocket /dsh-remote/link──► master ◄── slave (ai-pc)
own sessions + own local plugins plugins, config, relay hub
Install
On every machine, register the plugin in the DSH web profile and restart DSH:
# master (this repo as a link: plugin, e.g. in Beast-Workspace)
# profile package.json → "@local/dsh-remote": "link:/path/to/dsh-remote" + bundle entry
# or use the helper (also creates the node_modules link and a config block):
node deploy/install-local.mjs --role master --name "Pelican-Node"
node deploy/install-local.mjs --role auto --name "ai-pc" # on a slave
systemctl --user restart dsh # or restart `dsh web` however you run it
deploy/dsh-web.user.service is a ready-made systemd user unit for a rootless dsh web (127.0.0.1:3081).
macOS: one-line slave install
Create a pairing code on the master (Settings → Remote devices → Create pairing code). Then, in Terminal on the Mac:
curl -fsSL https://git.rizzy.dev/DSH-Plugins/dsh-remote/raw/branch/main/deploy/install-mac.sh \
| bash -s -- --master http://192.168.0.111:3080 --code XXXX-XXXX-XXXX-XXXX --name my-mac
No sudo and no Homebrew. The script:
- installs Node 22 to
~/.local/opt/node(checksum-verified), and DSH and pnpm to~/.local; - puts dsh-remote in
~/.dsh/plugins/dsh-remoteand registers it as a slave that acceptsOMNI_API_KEYandFORGE_CREDENTIALS(--acceptchanges the list); - pairs with the code (
deploy/pair.mjs, no running DSH needed); - installs the LaunchAgent
dev.rizzy.dsh-web:dsh webon 127.0.0.1:3081, started at login and restarted if it dies. Logs go to~/.dsh/logs/.
On first link-up the Mac pulls the master's plugins and config. With sync.restartOnSync: true, set by the script, DSH restarts itself once so new plugins load. It waits until no session is running and restarts at most once per 10 minutes.
Re-running the script updates everything and keeps the pairing. To remove it: launchctl bootout gui/$(id -u)/dev.rizzy.dsh-web, then delete ~/Library/LaunchAgents/dev.rizzy.dsh-web.plist, ~/.dsh and ~/.local/opt/node.
Away from home, point --master at the public URL. If an auth proxy guards it, add the --edge … flags (see Masters behind an auth proxy). Secrets are prompted for on the terminal and never passed on the command line:
curl -fsSL https://git.rizzy.dev/DSH-Plugins/dsh-remote/raw/branch/main/deploy/install-mac.sh \
| bash -s -- --master https://dsh.rizzy.dev --code XXXX-XXXX-XXXX-XXXX --name my-mac \
--edge basic --edge-user dsh-remote-mac
Pair a device
- Master: Settings → Remote devices → Create pairing code (single use, valid 15 min).
- Slave: Settings → Remote devices → enter the master URL (e.g.
http://192.168.0.111:3080orhttps://dsh.rizzy.dev) and the code → Pair. Then restart the slave's DSH once: withrole: autoit switches from master to slave.
The slave gets its own device key; the master stores only its hash. Revoke cuts the device off immediately: open links close with code 4401 and later connections are refused with 401.
Use it
- Sidebar → Devices: every device, its sessions grouped by workspace, a running indicator, search. Click a session to open it. A local session opens in place. A remote one opens
/r/<device>/#/session/<id>, the full UI of that device, with a "Remote: · back to local" pill in the corner. - + New session on a device card creates a session in one of that device's workspaces and opens it.
- Open DSH ↗ opens a device's whole UI.
- Toasts: ": '