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 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
- # Temporary Holding Version
1
+ # dsh-remote-control-plugin
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.