dsh-remote-control-plugin 0.0.0-stage → 0.3.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.
- package/INSTALL.md +35 -0
- package/README.md +96 -2
- package/client.js +563 -0
- package/cordis.patch.yml +3 -0
- package/host/fabric.js +578 -0
- package/host/relay-core.js +189 -0
- package/host/relay-link.js +335 -0
- package/icon.svg +15 -0
- package/index.js +1010 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +45 -4
- package/web/app.css +420 -0
- package/web/app.js +594 -0
- package/web/icon.svg +15 -0
- package/web/index.html +85 -0
package/INSTALL.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Install on another desktop
|
|
2
|
+
|
|
3
|
+
Published as `dsh-remote-control-plugin`. The package is self-contained — no dependencies, no
|
|
4
|
+
build step, no postinstall script.
|
|
5
|
+
|
|
6
|
+
## 1. Install it
|
|
7
|
+
|
|
8
|
+
Open DSH on the other desktop, start a session, and send:
|
|
9
|
+
|
|
10
|
+
> Install the DSH plugin bundle `dsh-remote-control-plugin@0.3.0` with plugin_manager, then tell me when it is applied.
|
|
11
|
+
|
|
12
|
+
Then restart DSH. A **Devices** icon appears in the sidebar.
|
|
13
|
+
|
|
14
|
+
`dsh plugin add` does not work for this: it forwards to `pnpm`, which is not on `PATH` outside
|
|
15
|
+
the app. The agent route uses the bundled pnpm and also registers the bundle in the profile,
|
|
16
|
+
which a bare `pnpm add` would not do.
|
|
17
|
+
|
|
18
|
+
## 2. Connect the two machines
|
|
19
|
+
|
|
20
|
+
Pick the machine with a reachable address (same LAN, VPN, or a forwarded port):
|
|
21
|
+
|
|
22
|
+
1. On that machine: **Devices → Relay → Create relay here**, then **Copy** the invite.
|
|
23
|
+
2. On the other machine: **Devices → Relay**, paste the invite into the URL field, **Connect**.
|
|
24
|
+
3. Either machine: **Request** next to the peer, then **Allow** on the target.
|
|
25
|
+
|
|
26
|
+
No reachable machine? Run a relay anywhere both can reach (`PORT=8787 node relay/server.js`) and
|
|
27
|
+
paste `http://<host>:8787 <workspace key>` into both panels.
|
|
28
|
+
|
|
29
|
+
## Notes
|
|
30
|
+
|
|
31
|
+
- The account label is cosmetic; trust comes from the one-time **Allow** on the target device.
|
|
32
|
+
- Approvals persist in `$DSH_HOME/remote-control-peers.json`; deleting it revokes every peer.
|
|
33
|
+
- The relay and the control API share one port (default `57330`), so one forwarded port is enough.
|
|
34
|
+
- Device trusts are per install: the published plugin and a locally built copy share the protocol
|
|
35
|
+
but keep separate approval stores.
|
package/README.md
CHANGED
|
@@ -1,3 +1,97 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-remote-control-plugin
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Control other DSH desktops from the one you are using.
|
|
4
|
+
|
|
5
|
+
Two surfaces share one Host plugin:
|
|
6
|
+
|
|
7
|
+
- **Devices panel** (Web GUI) — discovers other DSH desktops on the same network, asks the
|
|
8
|
+
target for access once, then lists that device's sessions and lets you read the live
|
|
9
|
+
transcript, send prompts, and stop a turn.
|
|
10
|
+
- **Browser page** (phone / any browser) — the same control surface as a small standalone
|
|
11
|
+
page, unlocked with a pairing code. Useful when the other end is a phone rather than a DSH
|
|
12
|
+
desktop.
|
|
13
|
+
|
|
14
|
+
Both drive `ctx.sessionController` in-process, so neither depends on the GUI's boot payload.
|
|
15
|
+
|
|
16
|
+
## How device trust works
|
|
17
|
+
|
|
18
|
+
1. Each Host announces itself on UDP `discoveryPort` with its device id, display name, and
|
|
19
|
+
account id (when signed in).
|
|
20
|
+
2. To control a peer, this Host sends `POST /peer/hello`. The **target** shows a pending
|
|
21
|
+
request in its own Devices panel; nothing is granted until a person there presses **Allow**.
|
|
22
|
+
3. On approval the target issues a random per-peer secret. Every later call is signed
|
|
23
|
+
`HMAC-SHA256(secret, deviceId + method + path + timestamp + body)`, valid for 5 minutes of
|
|
24
|
+
clock skew.
|
|
25
|
+
4. Approvals persist in `$DSH_HOME/remote-control-peers.json`. Deleting that file revokes
|
|
26
|
+
every device.
|
|
27
|
+
|
|
28
|
+
There is deliberately no "same account implies trust" shortcut: two devices holding grants for
|
|
29
|
+
the same account share no secret, so account id alone cannot authenticate a peer. The panel
|
|
30
|
+
shows the account id of each candidate and flags a different account, but a person approves.
|
|
31
|
+
|
|
32
|
+
## Across networks (relay)
|
|
33
|
+
|
|
34
|
+
LAN discovery only reaches one subnet. Devices on different networks meet through a relay, and
|
|
35
|
+
there are two ways to get one.
|
|
36
|
+
|
|
37
|
+
**Create it on a device.** In **Devices → Relay**, press **Create relay here**. That Host serves
|
|
38
|
+
the relay on its own listener and shows a ready-to-paste invite — one line holding the URL and
|
|
39
|
+
the workspace key. Paste it into the other device's Relay panel.
|
|
40
|
+
|
|
41
|
+
This only reaches other networks if that device is reachable there: the same LAN, a forwarded
|
|
42
|
+
port, a VPN address, or a configured `publicUrl`. The invite carries the address the panel can
|
|
43
|
+
see, so forward that port (or set `publicUrl`) before sharing it.
|
|
44
|
+
|
|
45
|
+
**Or run a relay on a machine that is always reachable:**
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
PORT=8787 node relay/server.js
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Host it on a VPS, NAS, or any always-on machine, put it behind TLS or a VPN, and paste
|
|
52
|
+
`http://host:8787 <workspace key>` into every device.
|
|
53
|
+
|
|
54
|
+
Either way the workspace key never leaves the devices: the relay only receives its SHA-256 as a
|
|
55
|
+
group id. It forwards opaque `/peer/*` calls that stay signed end to end, so a compromised relay
|
|
56
|
+
can drop or delay traffic but cannot forge a control command. Group membership alone grants
|
|
57
|
+
nothing — the target still requires the one-time approval before any session call is accepted.
|
|
58
|
+
Treat the relay as private: whoever holds the group id can join the group and *request* access.
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
Defaults work on a LAN. Override the row in `$DSH_HOME/profiles/<profile>/cordis.patch.yml`:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
- id: remote-control
|
|
66
|
+
name: 'dsh-remote-control-plugin'
|
|
67
|
+
config:
|
|
68
|
+
host: 0.0.0.0 # any interface (default); use one LAN IP to narrow exposure
|
|
69
|
+
port: 57330 # browser page + peer API
|
|
70
|
+
discovery: true
|
|
71
|
+
discoveryPort: 57331 # UDP discovery
|
|
72
|
+
relayUrl: '' # e.g. https://relay.example.com for cross-network control
|
|
73
|
+
relayKey: '' # shared workspace key; required when relayUrl is set
|
|
74
|
+
deviceName: Desk PC # defaults to the OS hostname
|
|
75
|
+
publicUrl: '' # e.g. https://dsh.example.com for a tunnel/proxy
|
|
76
|
+
trustedHosts: [] # extra accepted authorities for the browser page
|
|
77
|
+
code: '' # fixed pairing code; empty = generated and persisted
|
|
78
|
+
cookieDays: 30
|
|
79
|
+
defaultCwd: ''
|
|
80
|
+
tls: { certFile: '', keyFile: '' }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The browser page and the peer API share the listener. When `host` is a wildcard, any IP literal
|
|
84
|
+
is accepted and domain-name Hosts are refused unless they appear in `publicUrl` or
|
|
85
|
+
`trustedHosts`; this blocks DNS-rebinding. Requests without a same-origin `Sec-Fetch-Site` must
|
|
86
|
+
come from loopback.
|
|
87
|
+
|
|
88
|
+
## Threat model / limits
|
|
89
|
+
|
|
90
|
+
- Approval is manual on purpose. A peer on the LAN can prompt for access; it cannot grant it.
|
|
91
|
+
- Prompts sent from a peer run with this Host's tools and permissions. Approve devices you
|
|
92
|
+
control.
|
|
93
|
+
- Peer transport is plain HTTP. The peer secret crosses the LAN once, at approval time. Use a
|
|
94
|
+
private network or VPN, or set `tls` and front the port with a proxy.
|
|
95
|
+
- The pairing code protects the browser page only; it is printed at activation and stored in
|
|
96
|
+
`$DSH_HOME/remote-control.json`.
|
|
97
|
+
- Approvals that arrive while no Devices panel is open stay pending for up to 10 minutes.
|