dsh-zen-remote 2.3.2 → 2.3.3
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/README.md +178 -282
- package/README.zh-CN.md +179 -283
- package/docs/interface.md +1 -1
- package/lib/adapters/dsh-better-sidebar/index.js +9 -2
- package/lib/adapters/dsh-better-sidebar/index.js.map +1 -1
- package/lib/types/adapters/dsh-better-sidebar/index.d.ts +1 -1
- package/lib/types/adapters/dsh-better-sidebar/index.d.ts.map +1 -1
- package/package.json +13 -13
- package/src/adapters/dsh-better-sidebar/index.ts +9 -2
package/README.md
CHANGED
|
@@ -28,387 +28,283 @@
|
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
-
##
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
## Contents
|
|
32
|
+
|
|
33
|
+
1. [Install](#1-install)
|
|
34
|
+
2. [Quick Start (Unified Setup & Pairing Wizard)](#2-quick-start-unified-setup--pairing-wizard)
|
|
35
|
+
- [Scenario A: Set up as Host (Phone & Desktop Access)](#scenario-a-set-up-as-host-phone--desktop-access)
|
|
36
|
+
- [Scenario B: Set up another computer as Desktop Client](#scenario-b-set-up-another-computer-as-desktop-client)
|
|
37
|
+
3. [Daily Usage: Session Sharing & Cross-Device Workflow](#3-daily-usage-session-sharing--cross-device-workflow)
|
|
38
|
+
4. [Lock-Screen Push: When Notifications Fire](#4-lock-screen-push-when-notifications-fire)
|
|
39
|
+
5. [Features](#5-features)
|
|
40
|
+
6. [Advanced Settings & Field Reference](#6-advanced-settings--field-reference)
|
|
41
|
+
7. [Security Model, Version Tolerance & Known Notes](#7-security-model-version-tolerance--known-notes)
|
|
42
42
|
|
|
43
43
|
---
|
|
44
44
|
|
|
45
|
-
##
|
|
46
|
-
|
|
47
|
-
**What it is for**: one always-on computer runs the sessions (the host), and the DSH desktop app on another computer (the client) watches and carries on with them remotely — history, live progress, messages, approvals, the terminal all work, and everything executes on the host. Phones and tablets take a different road: a browser reaching the host through the gateway (a web app device), see [Setting up public access](#setting-up-public-access-host).
|
|
48
|
-
|
|
49
|
-
**How it works**, in four steps:
|
|
50
|
-
|
|
51
|
-
1. **Pair**: on the host's settings page, generate a 桌面应用端 (desktop app device) pairing code; on the client's settings page, enter the server address and the code. Step by step in the [settings page walkthrough](#settings-page-walkthrough) below.
|
|
52
|
-
2. **Turn remote access on**: remote access is per session. On the host, pick 开启远程 (turn remote on) from the session's "…" menu (or use the globe icon in the title row, or the list in the settings page — see [Session sharing](#session-sharing)).
|
|
53
|
-
|
|
54
|
-

|
|
55
|
-
|
|
56
|
-
3. **Use it on the client**: after the local workspaces, the sidebar shows a "host device name · workspace name" group; sessions in it open like local ones, and the globe icon in the title row shows the connection state (online / offline / version differs). A host workspace with no remote-enabled session never shows up.
|
|
57
|
-
|
|
58
|
-

|
|
45
|
+
## 1. Install
|
|
59
46
|
|
|
60
|
-
|
|
47
|
+
### Install from the UI (Recommended, Desktop App & Web UI)
|
|
61
48
|
|
|
62
|
-
|
|
49
|
+
Open the **Plugins** page in the DSH sidebar → click **Add plugin** → type `dsh-zen-remote` → click **Enable now** when the install finishes, then restart the DSH desktop app (or `dsh web`).
|
|
63
50
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
<p align="center"><img src="docs/images/mobile-info-remote.png" alt="The remote access switch in the phone's session info card" width="320"></p>
|
|
67
|
-
|
|
68
|
-
What it can and cannot do: see [Features](#features) and [Known limitations](#known-limitations). A paired desktop client is treated as a trusted device — the trust boundary is in [Security model](#security-model).
|
|
69
|
-
|
|
70
|
-
> In these screenshots the red frames mark what this plugin adds to the DSH interface; session titles are demo names and conversation content is blurred. The UI is shown in Chinese.
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Install
|
|
75
|
-
|
|
76
|
-
**From the UI** (desktop app and web UI alike; the desktop app is how a 2.0 host actually runs): open the **Plugins** page in the sidebar → **Add plugin** → type `dsh-zen-remote` → click **Enable now** when the install finishes, then restart the desktop app (or `dsh web`).
|
|
77
|
-
|
|
78
|
-
**Web service from the command line** (host on a headless `dsh web` box, also where phone access still points):
|
|
51
|
+
### Install from the CLI (Headless `dsh web` server)
|
|
79
52
|
|
|
80
53
|
```sh
|
|
81
54
|
dsh plugin --profile web add dsh-zen-remote
|
|
82
55
|
# then restart dsh web
|
|
83
56
|
```
|
|
84
57
|
|
|
85
|
-
>
|
|
58
|
+
> `dsh plugin` requires `--profile` to name the target profile. The desktop app's `desktop` profile is managed exclusively by the app (`--profile desktop` is refused on the CLI), so use the UI install above on the desktop app.
|
|
86
59
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
`dsh plugin` requires `--profile` to name the profile to install into and errors out without it. The desktop app's `desktop` profile is managed exclusively by the app — the CLI refuses `--profile desktop` — so on the desktop app use the UI above.
|
|
60
|
+
<details>
|
|
61
|
+
<summary>Manual Profile Edit / Local Development (<code>link:</code>), Upgrading & Uninstall</summary>
|
|
90
62
|
|
|
91
|
-
**Editing the profile by hand** (local development, `link:` installs): add the dependency to the profile's `package.json` (`~/.dsh/profiles/desktop` for the desktop app, `~/.dsh/profiles/web` for the web service)
|
|
63
|
+
**Editing the profile by hand** (local development, `link:` installs): add the dependency to the profile's `package.json` (`~/.dsh/profiles/desktop` for the desktop app, `~/.dsh/profiles/web` for the web service) and append `"dsh-zen-remote"` to the `dsh.profile.bundles` array:
|
|
92
64
|
|
|
93
65
|
```jsonc
|
|
94
66
|
// ~/.dsh/profiles/desktop/package.json
|
|
95
67
|
{
|
|
96
68
|
"dependencies": {
|
|
97
|
-
"dsh-zen-remote": "^2.3.
|
|
69
|
+
"dsh-zen-remote": "^2.3.3" // for local development: "link:/path/to/dsh-zen-remote"
|
|
98
70
|
}
|
|
99
71
|
}
|
|
100
72
|
```
|
|
101
73
|
|
|
102
74
|
```sh
|
|
103
75
|
cd ~/.dsh/profiles/desktop && pnpm install
|
|
104
|
-
#
|
|
76
|
+
# For a link: checkout, also run `npm install --omit=dev` inside the repository so the gateway subprocess can resolve `web-push`.
|
|
105
77
|
```
|
|
106
78
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
<summary>Static mount / local development</summary>
|
|
113
|
-
|
|
114
|
-
Copy [`cordis.patch.yml.example`](cordis.patch.yml.example) into your profile's `cordis.patch.yml` with an absolute path — it is the same single row, with an optional `config:` block showing the row fields. A normal install from the UI or with `dsh plugin` never needs it.
|
|
79
|
+
**Upgrading & migration notes**:
|
|
80
|
+
- **Upgrading with HMR on**: remove `dsh-zen-remote` first, then install the new version (`dsh plugin --profile <name> remove dsh-zen-remote` → `add dsh-zen-remote`, or Uninstall → Add in the Plugins page).
|
|
81
|
+
- **DSH 0.2.1-alpha.3+ profile reset**: `dsh --profile <name> --from-default-profile <template>` replaces an existing custom profile with a fresh copy; back up `~/.dsh/profiles/<name>` first.
|
|
82
|
+
- **Upgrading from 1.x**: since 2.0.0 the composition layer is a single row (`dsh-zen-remote`). Remove any legacy `dsh-zen-remote-gateway`, `dsh-zen-remote-push`, or manual `dsh-push.mjs` lines from `cordis.patch.yml`. Existing `~/.dsh/lan-gate.config.json` settings and paired devices carry over automatically.
|
|
83
|
+
- **Uninstalling**: click **Uninstall** on the `dsh-zen-remote` plugin page and restart. To also wipe pairing and share state, delete `~/.dsh/lan-gate-state.json`, `~/.dsh/lan-gate.config.json`, `~/.dsh/zen-remote-shares.json`, and `~/.dsh/zen-remote-server.json`.
|
|
115
84
|
|
|
116
85
|
</details>
|
|
117
86
|
|
|
118
|
-
To uninstall: open dsh-zen-remote in the **Plugins** page and click **Uninstall**, then confirm; on the web service you can also run `dsh plugin --profile web remove dsh-zen-remote`; for a hand-edited install, delete it from the profile's `dependencies` and `dsh.profile.bundles`. Then restart. To also wipe pairing and share state, delete `~/.dsh/lan-gate-state.json`, `~/.dsh/lan-gate.config.json`, `~/.dsh/zen-remote-shares.json` and `~/.dsh/zen-remote-server.json` (the server id — once it is gone, paired clients see a brand-new server).
|
|
119
|
-
|
|
120
87
|
---
|
|
121
88
|
|
|
122
|
-
##
|
|
89
|
+
## 2. Quick Start (Unified Setup & Pairing Wizard)
|
|
123
90
|
|
|
124
|
-
|
|
91
|
+
The same plugin supports two roles:
|
|
92
|
+
- **Server (`role: host`, default)**: runs your sessions, spawns the local security gateway and Web Push service, and lets your phone (PWA) or another computer connect.
|
|
93
|
+
- **Desktop Client (`role: client`)**: runs inside another computer's DSH desktop app, connects to the host, and mounts the host's shared sessions directly into its own sidebar (without spawning a local gateway or fighting for port 3088).
|
|
125
94
|
|
|
126
|
-
|
|
127
|
-
- **If you hand-inserted a `dsh-zen-remote/dsh-push.mjs` line into a profile patch back in 1.0, remove it** — the main entry loads the push half by role now, and a leftover line loads push twice.
|
|
128
|
-
- **`~/.dsh/lan-gate.config.json` still works.** Nothing to migrate; its values show up in the settings page. Priority per field: environment variable > plugin row settings > `lan-gate.config.json` > built-in default. The file is never written or deleted by the plugin.
|
|
129
|
-
- **Paired devices become Web 应用端 automatically** and keep working; no re-pairing.
|
|
130
|
-
- **The admin page moved.** `/lan-gate/admin` is now just a notice pointing at the plugin settings page (it still 403s anything that is not a direct local connection). Pairing codes, device management, push test and sharing all live in the DSH Plugins page → dsh-zen-remote settings block.
|
|
131
|
-
- With no configuration touched at all, the plugin comes up as a host and the gateway, push and phone UI behave exactly as 1.1.x did.
|
|
95
|
+
Open **Plugins → `dsh-zen-remote`** in DSH. The **Remote Access & Device Pairing** card sits right at the top of the page:
|
|
132
96
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
## Setting up public access (host)
|
|
136
|
-
|
|
137
|
-
Once installed, the mobile UI already works at `127.0.0.1:3080` on the machine itself. To reach it from outside: put your own TLS-terminating reverse proxy in front of the gateway, verify it with a 403 self-check, then pair devices. The full walkthroughs — nginx / Caddy / Cloudflare Tunnel / Lucky — live in [docs/remote-access.en.md](docs/remote-access.en.md). **Prefer a guided setup?** The plugin detail page on the host has a three-step wizard (“Connect your phone”) that automates every path below — a managed Caddy with DNS-01 certificates, snippet generation + verification for your existing proxy, and a fully automatic Cloudflare Tunnel. The short form:
|
|
138
|
-
|
|
139
|
-
1. The gateway listens on `127.0.0.1:3088` only; a reverse proxy (nginx, Caddy, Cloudflare Tunnel, Lucky) terminates HTTPS and forwards to it. **Self-check**: from **mobile data** (not home Wi-Fi), open `https://your-domain/lan-gate/admin` — the correct result is a **403**. If you can see the notice page, your proxy is not forwarding `X-Forwarded-*` headers and public requests are being treated as local ones — fix that before continuing.
|
|
140
|
-
2. Open the DSH Plugins page **on the host machine**, open the dsh-zen-remote settings block, pick the device role for the code you are about to mint, and generate an 8-character pairing code (valid 10 minutes, single use).
|
|
141
|
-
3. On the phone, open your HTTPS domain and enter the code on the pairing page. Once paired you land in DSH; identity lives in a long-lived cookie. Use "Add to Home Screen" to install it as an app, grant notification permission, and the agent reaches your lock screen when it needs you.
|
|
97
|
+

|
|
142
98
|
|
|
143
99
|
---
|
|
144
100
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
A pairing code is minted **for one device role**, and the role is checked at redemption — a code offered to the wrong channel is refused with a clear message, is not consumed, and does not count toward the wrong-code lockout.
|
|
101
|
+
### Scenario A: Set up as Host (Phone & Desktop Access)
|
|
148
102
|
|
|
149
|
-
|
|
150
|
-
| --- | --- | --- |
|
|
151
|
-
| Shape | Phones, tablets, browsers — anything through the gateway | A computer running the DSH desktop app with this plugin in its `client` role |
|
|
152
|
-
| Sees | **All** server sessions | **Only** the sessions the host has enabled remote access on (enforced server-side) |
|
|
153
|
-
| How to pair | Open the gateway URL in a browser, enter the code on the pairing page | Fill the server URL + code into the client's plugin settings page |
|
|
154
|
-
|
|
155
|
-
**Desktop-client pairing**, step by step:
|
|
156
|
-
|
|
157
|
-
1. On the host's plugin settings page, choose 桌面应用端 and generate a code.
|
|
158
|
-
2. On the other computer's DSH desktop app, open the plugin settings page, set the role to **client**, enter the server address and the code.
|
|
159
|
-
3. The address must be `https://` unless it is a private-network host — LAN ranges (`192.168.x`, `10.x`, `172.16–31.x`), Tailscale/CGNAT (`100.64.x`), loopback, `localhost` / `*.local`, or IPv6 `::1` / `fc00::/7` / `fe80::/10`. A public address over plain http is refused, so the device token never travels in the clear.
|
|
160
|
-
4. On success the token is stored in the row (a secret field) and the client connects automatically. The settings block shows the connection state — connected / offline / version mismatch / token revoked — and can unpair.
|
|
161
|
-
|
|
162
|
-
The server-side settings page can rename devices, change a device's role (`set-role`), and revoke one or all. Revocation is immediate: open connections and push subscriptions die with it. Desktop-client devices can never reach any admin route — the gateway only ever forwards them into the relay prefix.
|
|
163
|
-
|
|
164
|
-
**Device name**: the 设备名称 (device name) field in the settings page's role card — the `serverName` config — is shared by both roles. On a host it is the name that titles the group in other devices' sidebars (default: the computer's name); on a client it is the name registered with the server when pairing. The name syncs both ways by itself: the server renaming itself reaches the clients' group titles in about 15 seconds; a client saving a new name updates the server's device list; the server renaming a client in its device list reaches that client in about 30 seconds. A client pushes its name only when you explicitly save the settings — everything else defers to the server's record, and a rename made while offline is dropped if the process exits before it can be pushed. Names are 1–40 characters.
|
|
103
|
+
Click **Start Setup Wizard** (or **Reconfigure Wizard** if already configured) to complete setup in three guided steps:
|
|
165
104
|
|
|
166
|
-
|
|
105
|
+
#### Step 1: Choose "Act as Server (Host)" & an Access Method
|
|
167
106
|
|
|
168
|
-
|
|
107
|
+

|
|
169
108
|
|
|
170
|
-
|
|
109
|
+
Pick one of the four access methods based on your network environment (switching methods automatically stops any managed process from the previous mode and updates the gateway bind address):
|
|
171
110
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
111
|
+
| Access method | Best for | What the wizard automates |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| **Managed Caddy (Recommended)** | Public IP (or router port forwarding) + domain on Cloudflare | Automatically downloads a prebuilt `Caddy + caddy-dns/cloudflare` binary (auto-accelerated via mainland China mirrors when needed) and issues/renews TLS certificates via DNS-01; gateway listens only on `127.0.0.1:3088` |
|
|
114
|
+
| **Existing Reverse Proxy** | Already running Nginx, Caddy, or Lucky | Generates ready-to-paste proxy snippets with WebSocket and `X-Forwarded-*` headers, plus one-click `trustedProxies` registration (full manual guide: [docs/remote-access.en.md](docs/remote-access.en.md)) |
|
|
115
|
+
| **Cloudflare Tunnel** | No public IP / no router port forwarding + domain on Cloudflare | Automatically downloads `cloudflared` (with mainland China mirror failover), creates or reuses a tunnel via the Cloudflare API, writes the DNS CNAME record, and starts the tunnel; gateway listens only on `127.0.0.1:3088` |
|
|
116
|
+
| **LAN / Tailscale Direct** | Same Wi-Fi / LAN or shared Tailscale virtual network | No domain or TLS certificate needed; auto-detects the machine's LAN / Tailscale IPv4 address and binds `0.0.0.0:3088`, while the gateway's built-in **private-IP wall** rejects any unproxied direct connection from a public internet IP (`403 public-direct-forbidden`) |
|
|
176
117
|
|
|
177
|
-
|
|
118
|
+
#### Step 2: Fill in Connection Parameters
|
|
119
|
+
- **Managed Caddy / Cloudflare Tunnel**: enter your public domain (e.g. `dsh.example.com`) and paste your Cloudflare API Token (click *"How do I get an API token?"* for step-by-step instructions and required permissions, or *"Domain not on Cloudflare yet?"* for onboarding steps).
|
|
120
|
+
- **Existing Reverse Proxy**: enter the external `https://` URL of your proxy and copy the generated Nginx / Caddy / Lucky configuration snippet.
|
|
121
|
+
- **LAN / Tailscale Direct**: the wizard automatically detects and pre-fills `http://<your-LAN-or-Tailscale-IP>:3088`.
|
|
178
122
|
|
|
179
|
-
|
|
123
|
+
#### Step 3: Verify Connectivity & Pair Devices
|
|
180
124
|
|
|
181
|
-
|
|
125
|
+

|
|
182
126
|
|
|
183
|
-
|
|
127
|
+
1. **Start Ingress Service & Auto-Probe**:
|
|
128
|
+
- For Managed Caddy or Cloudflare Tunnel, click **Start Caddy** or **Create & Start Tunnel** in the service bar.
|
|
129
|
+
- The wizard automatically probes **Local gateway ready** and **Access URL reachable** (verifying that `/` serves the `401` pairing page and `/lan-gate/admin` returns `403`).
|
|
130
|
+
2. **Pair Phone / Browser (Web App)**:
|
|
131
|
+
- On the **Pair Phone / Browser (QR)** tab, click **Generate Pairing QR**.
|
|
132
|
+
- Scan the QR code with your phone camera or browser, enter the 8-character pairing code, and use **"Add to Home Screen"** to install DSH as a standalone PWA with lock-screen push notifications.
|
|
133
|
+
3. **Pair Another Computer (Desktop Client)**:
|
|
134
|
+
- Switch to the **Pair Desktop Client** tab, click **Generate Desktop Pairing Code**, and click **Copy Server URL & Code** to send to your other computer.
|
|
184
135
|
|
|
185
|
-
**
|
|
136
|
+
> **Quick actions after setup**: once configured, you can directly click **Pair Phone (QR)**, **Pair Desktop Client**, or **Manage Devices** (rename devices, switch device roles, revoke one or all devices, or send a test push) right from the overview card footer without re-running the wizard.
|
|
186
137
|
|
|
187
138
|
---
|
|
188
139
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
### Settings page walkthrough
|
|
140
|
+
### Scenario B: Set up another computer as Desktop Client
|
|
192
141
|
|
|
193
|
-
|
|
142
|
+
On the second computer running the DSH desktop app:
|
|
194
143
|
|
|
195
|
-
|
|
144
|
+
1. Open **Plugins → `dsh-zen-remote`** and click **Start Setup Wizard**.
|
|
145
|
+
2. **Step 1**: select **Act as Desktop Client** and click **Next**.
|
|
146
|
+
3. **Step 2**: enter a name for this computer (e.g. `Living-Room-Mac`), the host's gateway URL (`https://` for public URLs, or `http://192.168.x.x:3088` / `http://100.x.x.x:3088` for LAN / Tailscale), and the 8-character desktop pairing code from the host, then click **Pair & Connect**.
|
|
147
|
+
4. **Step 3**: once all three status lights (**Pairing token**, **Server connection**, **Session & plugin sync**) turn green, the host's shared workspaces and sessions appear in your left sidebar immediately.
|
|
196
148
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
1. Set 运行角色 (role) to 主服务端 (host) and give it a recognizable 设备名称 (device name), e.g. "书房" (study) — it titles the group in clients' sidebars and shows on the phone. Save.
|
|
149
|
+
---
|
|
200
150
|
|
|
201
|
-
|
|
151
|
+
## 3. Daily Usage: Session Sharing & Cross-Device Workflow
|
|
202
152
|
|
|
203
|
-
|
|
204
|
-
|
|
153
|
+
| | **Web App Device (Phone / Tablet / Browser)** | **Desktop Client (Another DSH Desktop App)** |
|
|
154
|
+
| --- | --- | --- |
|
|
155
|
+
| Visible sessions | **All** sessions on the host | **Only** sessions with remote access enabled on the host (enforced server-side) |
|
|
156
|
+
| Credential | Long-lived HttpOnly cookie | Secret device token stored in the plugin config (`Bearer` token) |
|
|
205
157
|
|
|
206
|
-
|
|
158
|
+
### Enabling or Disabling Remote Access on a Session
|
|
207
159
|
|
|
208
|
-
|
|
160
|
+
Remote access is controlled per session (or automatically enabled for new sessions if you turn on *Auto-share new sessions* in Step 2 of the wizard). You can toggle it from four places:
|
|
209
161
|
|
|
210
|
-
|
|
162
|
+
1. **Host sidebar**: open a session's `"…"` menu and click **开启远程 / 关闭远程** (Enable / Disable remote);
|
|
211
163
|
|
|
212
|
-
|
|
164
|
+

|
|
213
165
|
|
|
214
|
-
**
|
|
166
|
+
2. **Host session header**: click the globe icon in the session title row (hover to see remaining idle time; a dot indicates a desktop client is currently viewing it);
|
|
167
|
+
3. **Phone (Web App) session info card**: open the info card at the top right of a session and toggle **远程访问** (Remote access);
|
|
215
168
|
|
|
216
|
-
|
|
217
|
-
2. Give this computer a 设备名称 (device name), e.g. "客厅" (living room); it is registered in the host's device list when pairing. Save.
|
|
218
|
-
3. 连接服务端 (connect to server): enter the host's gateway address (on a LAN e.g. `http://192.168.1.10:3088`; public addresses must be `https://`) and a 桌面应用端 pairing code from the host, then click 配对 (pair).
|
|
219
|
-
4. Done when 连接状态 (connection state) reads 已连接到「host name」 (connected to "host name"); the host's groups appear in the sidebar right away. The 诊断 (diagnostics) area lists the request interceptor state and recent remote-call failures — the first place to look when something is off.
|
|
169
|
+
<p align="center"><img src="docs/images/mobile-info-remote.png" alt="Remote access toggle inside the phone session info card" width="320"></p>
|
|
220
170
|
|
|
221
|
-
|
|
171
|
+
4. **Host settings page**: view and close shared sessions individually or all at once under the shared sessions list.
|
|
222
172
|
|
|
223
|
-
|
|
173
|
+
### Working in Remote Sessions on a Desktop Client
|
|
224
174
|
|
|
225
|
-
|
|
175
|
+
- **Sidebar groups**: once a host workspace has at least one remote-enabled session, a **"Server Name · Workspace Name"** group appears in the client's sidebar. Click any session to view full history, stream responses, approve tools, use the server terminal, browse files and diffs, download deliverables, or drag-and-drop files to upload.
|
|
226
176
|
|
|
227
|
-
|
|
177
|
+

|
|
228
178
|
|
|
229
|
-
|
|
179
|
+
- **Closing & idle sleep**: a shared session automatically closes remote access after **48 hours** (`idleHours`) of inactivity (mid-turn sessions or sessions waiting on approvals/questions never sleep). When remote access is closed—or when the client unpairs or its token is revoked—remote sessions are automatically archived/hidden from the sidebar; an actively open session displays a "remote closed" banner and recovers automatically if re-shared.
|
|
230
180
|
|
|
231
|
-
|
|
232
|
-
| --- | --- | --- |
|
|
233
|
-
| `role` | `host` | `host` runs gateway + push + sharing; `client` connects to a server. Anything not exactly `client` degrades to `host` |
|
|
234
|
-
| `port` | `3088` | Gateway port; if taken it retries upward (up to +20) |
|
|
235
|
-
| `host` | `127.0.0.1` | Gateway listen address; only open this up when the proxy is on another machine |
|
|
236
|
-
| `targetPort` | *(auto)* | Local DSH Web UI port. Leave unset: the gateway discovers the host's real listening port (desktop builds use a configurable one) |
|
|
237
|
-
| `rateLimit` | `120` | Per-minute cap on unpaired requests, per real client IP |
|
|
238
|
-
| `trustedProxies` | empty | Comma-separated IPs; required when the proxy is on another machine |
|
|
239
|
-
| `vapidSubject` | `mailto:admin@localhost` | Push contact. **On iOS this must be a real email or https URL**, or Apple refuses to deliver |
|
|
240
|
-
| `lang` | `auto` | Language of the gateway's pages, the push opt-in card and the notification copy. `auto` follows the browser's `Accept-Language` (falling back to Chinese); `zh`/`en` pin it |
|
|
241
|
-
| `pushTurnEnd` | off | Also push when a turn ends. Off by default — a finished turn doesn't mean you're needed. Approval/question pushes are unaffected and always fire |
|
|
242
|
-
| `pushEvents` | `agent/turn-stopping` | Which events count as "turn ended", comma-separated; only meaningful with `pushTurnEnd` |
|
|
243
|
-
| `pushDebounceMs` | `15000` | Minimum gap between two automatic pushes; approval/question notifications are never suppressed by it |
|
|
244
|
-
| `pushSummary` | off | Include the turn's final reply (prose only, never the reasoning; clipped to 120 chars) and the question text in the body |
|
|
245
|
-
| `pushTool` | on | Set `false` to remove the model-callable `push_notify` tool |
|
|
246
|
-
| `serverName` | computer name | **Device name** (≤ 40 chars), shared by both roles: on a host it is the display name in clients' group titles; on a client it is the name registered when pairing. Syncs both ways with the server automatically |
|
|
247
|
-
| `idleHours` | `48` | Idle-sleep window in hours for remote-enabled sessions; range (0, 8760] |
|
|
248
|
-
| `autoShareNewSessions` | off | Auto-enable remote on every session the host creates |
|
|
249
|
-
| `pluginBridgeConsent` | `{}` | The plugin bridge's whole-machine-data consent map: plugin name → boolean. Managed by the settings page's Plugin bridge section, rarely hand-edited; leftover keys for unregistered plugins are never pruned |
|
|
250
|
-
|
|
251
|
-
| `serverUrl` *(client)* | empty | The server's gateway address, normalized per the rules above |
|
|
252
|
-
| `deviceToken` *(client)* | empty | The paired device token; stored as a secret field, redacted everywhere |
|
|
253
|
-
| `turnFoldDesktop` | off | Turn-process folding at every width, not just phone widths |
|
|
254
|
-
| `keyboardLiftRatio` / `keyboardLiftMaxPx` / `keyboardSafetyPadPx` | `0.42` / `400` / `15` | Soft-keyboard lift calibration for browsers that never report the keyboard (see the known-issues note below) |
|
|
255
|
-
| `maxUploadBytes` | 20 MB | Attachment upload cap |
|
|
256
|
-
|
|
257
|
-
The legacy environment variables keep working and outrank the row: `LAN_GATE_PORT`, `LAN_GATE_HOST`, `LAN_GATE_TARGET_PORT`, `LAN_GATE_RATE_LIMIT`, `LAN_GATE_TRUSTED_PROXIES`, `LAN_GATE_VAPID_SUBJECT`, `LAN_GATE_LANG`, `DSH_PUSH_TURN_END`, `DSH_PUSH_EVENTS`, `DSH_PUSH_DEBOUNCE_MS`, `DSH_PUSH_SUMMARY`, `DSH_PUSH_TOOL`, `DSH_PUSH_LANG` (notification-copy override; without it `lang` decides), and `DSH_PUSH_APPROVAL_GRACE_MS` (how long to wait before pushing "approval pending" so a faster auto-approver's verdict wins; default 5000; it and the file-only `pushApprovalGraceMs` key are not part of the settings surface). `LAN_GATE_RELAY_SECRET` is **not** settable: the plugin mints it per load and hands it to the gateway itself.
|
|
258
|
-
|
|
259
|
-
The upload-size, keyboard-calibration and turn-fold knobs behave exactly as in 1.x — just moved from "plugin row YAML" to "settings page" (a hand-edited row works too).
|
|
181
|
+

|
|
260
182
|
|
|
261
183
|
---
|
|
262
184
|
|
|
263
|
-
## When
|
|
264
|
-
|
|
265
|
-
By default only when you are **actually needed**, along two independent lines.
|
|
266
|
-
|
|
267
|
-
**1. Decided by the system (always on, never suppressed by the debounce)**
|
|
268
|
-
|
|
269
|
-
| Situation | Notification |
|
|
270
|
-
| --- | --- |
|
|
271
|
-
| A tool is waiting for your authorization | "DSH needs your approval", with the tool name |
|
|
272
|
-
| The model called `ask_user_question` and is waiting | "DSH is waiting for your answer" |
|
|
273
|
-
|
|
274
|
-
Neither looks at session depth — a subagent stuck on an approval still calls out, because it's still you it's waiting for. Neither is suppressed by the debounce either: "something needs your nod" is the one notification that must never be swallowed. Since 2.0.0 this includes sessions running inside the desktop app: the host-side hook fires wherever the session lives.
|
|
275
|
-
|
|
276
|
-
**Timing when a machine answers**: approval events arrive as "record asked → consult the answerer → record decided", so the push waits `DSH_PUSH_APPROVAL_GRACE_MS` (5 seconds by default) and skips anything answered within it. Raise it if you run a slower model-backed approver (measured: 2.4s average). Approvals a policy waves through don't disturb you.
|
|
277
|
-
|
|
278
|
-
**2. Decided by the model**
|
|
185
|
+
## 4. Lock-Screen Push: When Notifications Fire
|
|
279
186
|
|
|
280
|
-
|
|
187
|
+
Notifications fire only when **you are actually needed**, along two independent paths (all payloads are end-to-end encrypted with `aes128gcm`):
|
|
281
188
|
|
|
282
|
-
**
|
|
283
|
-
|
|
284
|
-
|
|
189
|
+
1. **System-triggered (always on, never suppressed by debounce)**:
|
|
190
|
+
- **Tool waiting for approval**: pushes *"DSH needs your approval"* with the tool name. Waits `DSH_PUSH_APPROVAL_GRACE_MS` (default 5s) before sending so fast auto-approver plugins can answer first without buzzing your phone.
|
|
191
|
+
- **`ask_user_question` waiting for your answer**: pushes *"DSH is waiting for your answer"*.
|
|
192
|
+
- Works for both top-level sessions and subagent sessions across Web and Desktop hosts.
|
|
193
|
+
2. **Model-triggered (`push_notify` tool)**:
|
|
194
|
+
- Called by the agent when you asked to be notified upon completion, or when it hits a blocker that requires your decision.
|
|
195
|
+
- Ordinary turn completions do not push by default (enable `pushTurnEnd` in advanced settings if you want a push after every turn; enable `pushSummary` to include up to 120 characters of the final reply in the notification body).
|
|
285
196
|
|
|
286
197
|
---
|
|
287
198
|
|
|
288
|
-
## Features
|
|
289
|
-
|
|
290
|
-
**
|
|
291
|
-
|
|
292
|
-
-
|
|
293
|
-
-
|
|
294
|
-
-
|
|
295
|
-
-
|
|
296
|
-
-
|
|
297
|
-
-
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
**Desktop client (2.0 additions)**
|
|
307
|
-
|
|
308
|
-
- Remote groups in the sidebar ("server name · workspace name"), remote sessions open like local ones: full history, live progress, messages, cancels, queue edits, approvals and question answers, model selection, file tree, changes list plus changes summary/diff, goals, slash commands, agent presets, subagent prompts/interrupts, attachments and @ references, terminals running server-side
|
|
309
|
-
- Artifacts a remote session produces can be **downloaded to this computer**: the deliverable card and the document preview header each carry a "download to this computer" action that reads the host file through the relay in byte windows (any file size, never held whole in memory) and hands it to the browser; Word/Excel/PPT previews are now rendered to PDF by the host's LibreOffice (`officeToPdf/*` is on the relay allowlist) instead of failing with "not supported in remote sessions"
|
|
310
|
-
- Remote sessions can attach **non-image files**: the upload request is caught by the sub-client's own backend and streamed through the relay to the server session; the 500 MiB cap is the relay's own (local uploads have no such limit), an over-limit pick is refused locally on the client with a readable message, and slow links are bounded by the client's 15-minute upload budget (the host HTTP server's 5-minute request deadline is the practical ceiling: 500 MiB needs ~14 Mbps sustained).
|
|
311
|
-
- The model picker in a remote session lists the **server's** model catalog (groups named "server name · group name"), local sessions list only local models; the last-fetched server groups survive while the relay is offline; server groups never appear in local settings such as the vision router. Known boundary: a session created on the server that has never sent a message shows the local default model in the picker
|
|
312
|
-
- New sessions created inside a remote group run on the server's workspace and are shared automatically; remote sessions can be renamed, archived, pinned, forked, and their remote access closed (with confirmation) from the client
|
|
313
|
-
- Title-row connection icon per remote session (online / offline / version differs); a "remote closed" banner with the reason when a session is slept or closed server-side; offline groups go grey with input disabled and reconnect automatically with backoff
|
|
314
|
-
- First-come-first-served approvals/questions: if the server's own UI answers first, the client syncs to "already handled"
|
|
315
|
-
|
|
316
|
-
**Both ends (2.1 additions)**
|
|
317
|
-
|
|
318
|
-
- The **plugin bridge**: another plugin registers one bridge declaration and its panel data in remote sessions is served by the HOST — the sub-client reroutes the declared GET requests to the server, which executes them over loopback and streams the answer back; local data never leaks into a remote session. Bridged requires the plugin name, exact version and declaration to agree on BOTH ends; whole-machine data additionally needs the server owner's per-plugin consent in the settings page. Author guide: [the plugin bridge](docs/plugin-bridge.md)
|
|
319
|
-
|
|
320
|
-
**Desktop client (2.2 additions)**
|
|
321
|
-
|
|
322
|
-
- Dragging files into a remote session attaches them by **upload**: the desktop app's same-machine shortcut (a dragged file keeps its disk path and would become an `@absolute path` mention naming a file on YOUR machine, unreadable on the server) is intercepted in the browser and the drop is re-shaped into an ordinary upload — the same relay path as the attach button, so the server session gets the bytes. Dragging a **folder** into a remote session is not supported: the drop is stopped with a short on-screen notice. Local sessions are untouched — the shortcut keeps working there — and the guard's status is visible in the settings page diagnostics.
|
|
323
|
-
|
|
324
|
-
In depth: [interface](docs/interface.md) · [public access + relay protocol](docs/remote-access.en.md)
|
|
199
|
+
## 5. Features
|
|
200
|
+
|
|
201
|
+
- **Mobile PWA UI (Host)**:
|
|
202
|
+
- Two-level page stack (session list home + standalone session page with left-edge swipe back, Android back-gesture support, first-frame boot skeleton, and immutable asset caching)
|
|
203
|
+
- Home-screen plugin entry chips (auto-discovered per installed plugin, individually toggleable)
|
|
204
|
+
- Compact mobile composer with bottom sheets for permissions/models and direct phone attachment uploads
|
|
205
|
+
- Session info card (6-tile stats, log export, rename, fork, archive, one-tap full-conversation PNG share image, and remote access switch)
|
|
206
|
+
- **Unified Setup & Pairing Wizard (2.3)**:
|
|
207
|
+
- Top-of-page 2×2 live overview card and 3-step modal wizard covering all four host ingress modes (Managed Caddy, Existing Reverse Proxy, Cloudflare Tunnel, LAN / Tailscale Direct) and Desktop Client pairing
|
|
208
|
+
- Automatic download of prebuilt `Caddy + caddy-dns/cloudflare` and `cloudflared` binaries with mainland China network auto-detection, mirror acceleration (`gh-proxy.com` / `ghfast.top`), connect/stall failover watchdogs, and daily upstream release tracking via GitHub Actions
|
|
209
|
+
- Automatic LAN / Tailscale IPv4 discovery and local network probing, backed by the gateway's private-IP wall (`403 public-direct-forbidden`) against accidental public exposure
|
|
210
|
+
- Dedicated **Manage Devices** modal and automatic sidebar cleanup on client unpair/revocation
|
|
211
|
+
- **Cross-Desktop Remote Sessions (Client)**:
|
|
212
|
+
- Full-featured remote sessions in the sidebar: history, streaming, approvals/questions, server model catalog, file tree, changes summary/diff, server-side terminal, windowed artifact downloads, server-side LibreOffice PDF preview, up to 500 MiB file uploads, and drag-and-drop file upload interception
|
|
213
|
+
- **Plugin Bridge**:
|
|
214
|
+
- Cooperative plugins and built-in adapters (such as `dsh-better-sidebar`) route their panel requests in remote sessions to the host machine (with explicit owner consent for whole-machine data). See [Plugin Bridge Guide](docs/plugin-bridge.md).
|
|
215
|
+
|
|
216
|
+
In depth: [Interface Design](docs/interface.md) · [Public Access & Relay Protocol](docs/remote-access.en.md)
|
|
325
217
|
|
|
326
218
|
---
|
|
327
219
|
|
|
328
|
-
##
|
|
329
|
-
|
|
330
|
-
Three layers, strictest first:
|
|
331
|
-
|
|
332
|
-
1. **Relay protocol version** must match — in practice, **both ends need the same zen-remote version**. A 1.x server's gateway doesn't know the client's bearer token and answers its requests with the generic pairing wall, which the client reports as "token invalid (or the server's zen-remote is older than 2.0.0)". DSH versions may differ.
|
|
333
|
-
2. **DSH interface fingerprints** decide the rest: both ends compute normalized fingerprints of the remote-interface groups the relay uses and compare them at handshake. Identical is fully compatible even when version strings differ; a difference connects anyway, annotates the remote group "(版本有差异)" (version differs), lists the differing groups in the settings diagnostics, and degrades only the affected panels.
|
|
334
|
-
3. **Runtime degradation**: a forwarded call that fails parameter/result validation shows "incompatible with the server version" in that one panel and is recorded in the diagnostics — everything else keeps working.
|
|
335
|
-
|
|
336
|
-
---
|
|
220
|
+
## 6. Advanced Settings & Field Reference
|
|
337
221
|
|
|
338
|
-
|
|
222
|
+
Day-to-day setup and device management are handled entirely by the **Remote Access & Device Pairing Wizard** at the top of the `dsh-zen-remote` page. To fine-tune individual low-level fields, click `dsh-zen-remote` a second time under **Included components** on the plugin detail page.
|
|
339
223
|
|
|
340
|
-
|
|
224
|
+
<details>
|
|
225
|
+
<summary>View underlying component settings screenshots & full field reference table</summary>
|
|
341
226
|
|
|
342
|
-
|
|
343
|
-
- **"Only remote-enabled sessions are visible" constrains session data**: an unshared session's list, history, live progress and approval/question events are refused or filtered by the relay; @-mention candidates list **only shared sessions**; and a `dsh-session:` reference embedded in a message, a queue edit or a slash command refuses the whole call when it names a session without remote access. It is not a sandbox around the machine.
|
|
344
|
-
- **Admin actions stay local**: generating pairing codes, changing roles and revoking devices only work from the settings page on the server machine itself — requests that arrived through the gateway can view status but mutations are refused — the one exception is toggling a session's remote access, which a web app device (a phone) may do, and that is what the phone info card's remote access switch uses; desktop clients cannot reach the admin routes at all.
|
|
227
|
+
Each field resolves in order: **environment variable > plugin row settings > `~/.dsh/lan-gate.config.json` > default**. Saving a field that requires a gateway/push restart reloads the plugin row automatically without restarting the app.
|
|
345
228
|
|
|
346
|
-
|
|
229
|
+
| Host Role & Pairing Cards | Shared Sessions List | Client Settings & Diagnostics |
|
|
230
|
+
| --- | --- | --- |
|
|
231
|
+
|   |  |  |
|
|
347
232
|
|
|
348
|
-
|
|
233
|
+
| Field | Default | What it does |
|
|
234
|
+
| --- | --- | --- |
|
|
235
|
+
| `role` | `host` | `host` runs gateway + push + sharing; `client` connects to a remote host |
|
|
236
|
+
| `serverName` | computer name | **Device name** (≤ 40 chars): sidebar group title on a host, registered device name on a client; syncs both ways automatically |
|
|
237
|
+
| `port` | `3088` | Gateway port; retries upward (up to +20) if occupied |
|
|
238
|
+
| `host` | `127.0.0.1` | Gateway bind address (`127.0.0.1` for local proxy/tunnel, `0.0.0.0` for LAN/Tailscale direct) |
|
|
239
|
+
| `targetPort` | *(auto)* | Local DSH Web UI port; leave empty to auto-discover the host's listening port |
|
|
240
|
+
| `rateLimit` | `120` | Per-minute cap on unpaired requests per client IP |
|
|
241
|
+
| `trustedProxies` | empty | Comma-separated trusted proxy IPs when the reverse proxy runs on another machine |
|
|
242
|
+
| `vapidSubject` | `mailto:admin@localhost` | Web Push contact (**must be a real `mailto:` or `https://` URL for iOS delivery**) |
|
|
243
|
+
| `lang` | `auto` | Gateway & notification language: `auto` (browser `Accept-Language`), `zh`, or `en` |
|
|
244
|
+
| `pushTurnEnd` | off | Also send a push when an ordinary turn finishes (approvals/questions always push) |
|
|
245
|
+
| `pushEvents` | `agent/turn-stopping` | Comma-separated events that count as turn end when `pushTurnEnd` is on |
|
|
246
|
+
| `pushDebounceMs` | `15000` | Minimum interval (ms) between automatic pushes |
|
|
247
|
+
| `pushSummary` | off | Include up to 120 chars of the final reply in notification bodies |
|
|
248
|
+
| `pushTool` | on | Register the model-callable `push_notify` tool |
|
|
249
|
+
| `idleHours` | `48` | Idle-sleep window in hours for shared sessions; range `(0, 8760]` |
|
|
250
|
+
| `autoShareNewSessions` | off | Automatically enable remote access on new sessions created on the host |
|
|
251
|
+
| `pluginBridgeConsent` | `{}` | Whole-machine data consent map (`pluginName -> boolean`) for the plugin bridge |
|
|
252
|
+
| `serverUrl` *(client)* | empty | Remote server gateway URL (`https://` required for public hosts; `http://` allowed on private LAN/Tailscale ranges) |
|
|
253
|
+
| `deviceToken` *(client)* | empty | Paired client token (secret field, redacted everywhere) |
|
|
254
|
+
| `turnFoldDesktop` | off | Enable turn-process folding on desktop widths as well as mobile |
|
|
255
|
+
| `keyboardLiftRatio` / `keyboardLiftMaxPx` / `keyboardSafetyPadPx` | `0.42` / `400` / `15` | Soft-keyboard lift calibration fallback for environments where the OS hides keyboard height |
|
|
256
|
+
| `maxUploadBytes` | 20 MB | Mobile attachment upload cap |
|
|
257
|
+
|
|
258
|
+
Legacy environment variables (outrank row settings): `LAN_GATE_PORT`, `LAN_GATE_HOST`, `LAN_GATE_TARGET_PORT`, `LAN_GATE_RATE_LIMIT`, `LAN_GATE_TRUSTED_PROXIES`, `LAN_GATE_VAPID_SUBJECT`, `LAN_GATE_LANG`, `DSH_PUSH_TURN_END`, `DSH_PUSH_EVENTS`, `DSH_PUSH_DEBOUNCE_MS`, `DSH_PUSH_SUMMARY`, `DSH_PUSH_TOOL`, `DSH_PUSH_LANG`, `DSH_PUSH_APPROVAL_GRACE_MS`.
|
|
349
259
|
|
|
350
|
-
|
|
351
|
-
- Switching to a different server and back to the original one — or a server deleting a workspace and recreating one under the same id — leaves the affected remote groups invisible until the client page is **reloaded** (DSH's sidebar never re-accepts a removed workspace id within one page lifetime).
|
|
352
|
-
- "Export session" is disabled in a remote session (the menu item is hidden, and the backend refuses virtual session ids); the changes panel's **summary and diff do work** (relayed), while the entries that would pop a dialog on the server machine — "open", "reveal in Finder", "open in app", on the changes panel and deliverable cards alike — are hidden there.
|
|
353
|
-
- The host truncates @-mention candidates to the **first 50 rows before** share-filtering: on a server with many sessions, a shared one may be missing from the candidate list (the reference check itself is unaffected — references you type out are still verified one by one).
|
|
354
|
-
- A third-party plugin's own endpoints work in remote sessions only through the [plugin bridge](docs/plugin-bridge.md): cooperative plugins register themselves, third-party plugins are covered by built-in adapters. A plugin with neither has its panels degraded or hidden in remote sessions (its **tools** still run on the server, unaffected).
|
|
355
|
-
- With [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar) installed, remote sessions have TWO treatments: **0.25.0, 0.26.0, 0.26.1, 0.28.0-alpha.0 and 0.28.0** are bridged by zen-remote's built-in adapter — its file / git / task panels read and write the SERVER's data in a remote session (consent required, see the security boundary doc). Any OTHER version keeps the hide-local-surfaces treatment: its surfaces are hidden in remote sessions because they would read and write the **client machine's own** files; and because 0.19+ takes over the host's "Files" tab, an unbridged remote session has no sidebar file browser for now; file links in chat or deliverables open an empty right column in a remote session.
|
|
356
|
-
- Both ends need the **same zen-remote version** (identical relay protocol version). DSH versions may differ; interface differences are annotated "(版本有差异)".
|
|
260
|
+
</details>
|
|
357
261
|
|
|
358
262
|
---
|
|
359
263
|
|
|
360
|
-
## Known
|
|
361
|
-
|
|
362
|
-
**iOS 26.x standalone PWA viewport shrinkage**: after adding to the home screen, the viewport loses a status-bar's height at the bottom; an ordinary Safari tab is fine. This is an iOS defect — the missing region is outside the document and CSS cannot reach it. The plugin ships three layers of mitigation (light manifest background, safe-area compensation, forced reflow) which reduce it without guaranteeing a fix. Only quitting and reopening the whole app restores it fully.
|
|
264
|
+
## 7. Security Model, Version Tolerance & Known Notes
|
|
363
265
|
|
|
364
|
-
|
|
266
|
+
### Security Model
|
|
365
267
|
|
|
366
|
-
**
|
|
268
|
+
- **A paired desktop client is a trusted device**: the remote session terminal runs as the **host's OS user**, file-preview endpoints can read files accessible to the host process, and the client can create sessions in any host workspace.
|
|
269
|
+
- **Unshared sessions are strictly isolated at the relay**: list rows, history, live events, approvals/questions, and embedded `dsh-session:` references for unshared sessions are filtered or refused server-side.
|
|
270
|
+
- **Public direct-connection guard on `0.0.0.0`**: even when the gateway binds `0.0.0.0` for LAN / Tailscale mode, `isPrivateOrLoopbackIp` only admits direct connections from loopback, RFC1918 LAN ranges, link-local, and Tailscale (`100.64.0.0/10` / `fd7a:115c:a1e0::/48`); any unproxied direct connection from a public internet IP is rejected with `403 public-direct-forbidden`.
|
|
271
|
+
- **Admin actions are local-only**: minting pairing codes, changing device roles, running the setup wizard, and revoking devices are only permitted directly on the host machine.
|
|
272
|
+
- **Data & privacy**: the plugin reports nothing to any third party. Pairing state lives in `~/.dsh/lan-gate-state.json`, shared sessions in `~/.dsh/zen-remote-shares.json`, and Cloudflare tokens in `0600` files under `~/.dsh/zen-remote-ingress/`.
|
|
367
273
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
---
|
|
274
|
+
### Version Tolerance
|
|
371
275
|
|
|
372
|
-
|
|
276
|
+
1. **Relay protocol version**: both host and client must run the same `dsh-zen-remote` version.
|
|
277
|
+
2. **DSH host version**: DSH versions may differ between host and client. Normalized interface fingerprints are compared during handshake; any differing group is annotated `"(版本有差异)"` and degrades only the affected panel.
|
|
373
278
|
|
|
374
|
-
|
|
375
|
-
- **Files**: attachment uploads are written only to `.dsh-uploads/` inside the current session's working directory; pairing state lives in `~/.dsh/lan-gate-state.json`, config in `~/.dsh/lan-gate.config.json`, the share table in `~/.dsh/zen-remote-shares.json`, the server id in `~/.dsh/zen-remote-server.json`.
|
|
376
|
-
- **Credentials**: no account or password is ever collected or stored; a device's identity is a random token this plugin issues itself (HttpOnly cookie for web devices, a secret row field for desktop clients), plus a per-load relay shared secret that never leaves the machine pair.
|
|
279
|
+
### Known Limitations & Third-Party Plugin Support
|
|
377
280
|
|
|
378
|
-
|
|
281
|
+
<details>
|
|
282
|
+
<summary>Expand known limitations, iOS PWA notes & adapted third-party plugins</summary>
|
|
379
283
|
|
|
380
|
-
|
|
284
|
+
**Known limitations**
|
|
285
|
+
- With multiple DSH windows open simultaneously on a client, active-session detection may occasionally dismiss a closed session early.
|
|
286
|
+
- Switching between different servers or recreating a workspace with the same ID requires reloading the client page once.
|
|
287
|
+
- "Export session" and server-local OS dialogs ("Reveal in Finder" / "Open in app") are hidden in remote sessions.
|
|
288
|
+
- DSH's own Settings RPCs are loopback-only by upstream design, so plugin settings must be edited on the machine running DSH rather than through a remote browser.
|
|
381
289
|
|
|
382
|
-
|
|
290
|
+
**iOS standalone PWA notes**
|
|
291
|
+
- **iOS 26.x viewport shrinkage**: home-screen PWAs may lose a status-bar height at the bottom (an iOS WebKit bug); three mitigation layers are built in, and restarting the PWA restores full height.
|
|
292
|
+
- **iOS 26+ top progressive blur**: opening from the home screen may show a system `WKScrollPocket` blur across the top ~95px; entering any session and navigating back to the list clears it.
|
|
383
293
|
|
|
384
|
-
|
|
294
|
+
**Third-party plugins with mobile & bridge adaptations**
|
|
385
295
|
|
|
386
|
-
| Plugin |
|
|
296
|
+
| Plugin | Adaptation summary | Version tested |
|
|
387
297
|
| --- | --- | --- |
|
|
388
|
-
| [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar) |
|
|
389
|
-
| [@nanmicoder/dsh-agent-teams](https://github.com/NanmiCoder/dsh-agent-teams) |
|
|
390
|
-
| [@ychris12138/dsh-usage-stats](https://github.com/Ychris12138/dsh-usage-stats) |
|
|
391
|
-
| [@opendsh/dsh-plugin-scheduled-tasks](https://github.com/Ceelog/dsh-plugins) |
|
|
392
|
-
| dsh-at-file |
|
|
393
|
-
| [@ace-zone/dsh-market](https://www.npmjs.com/package/@ace-zone/dsh-market) |
|
|
394
|
-
| [dsh-vision-toolkit](https://www.npmjs.com/package/@anionex/dsh-vision-toolkit) |
|
|
395
|
-
| [dsh-web-ui
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
(Old zen-remote + server BS 0.25 + client BS 0.25 is the **currently released** combination — bridged as usual.)
|
|
400
|
-
|
|
401
|
-
| Two-end zen-remote | Server BS | Client BS | Result |
|
|
402
|
-
| --- | --- | --- | --- |
|
|
403
|
-
| New (has the 0.26 adapter) | 0.26 | 0.26 | Fully bridged; every new feature works |
|
|
404
|
-
| New | 0.25 | 0.25 | Bridged as before; behavior identical to the previous release |
|
|
405
|
-
| New | 0.25 | 0.26 | Bridged; the 0.26-only methods and `/sidebar/write` get 404 from the 0.25 server, `ws/sidechat` is refused so side chat falls back to 15s polling — single-feature degradation: core side chat works, no mis-routing, nothing written locally |
|
|
406
|
-
| New | 0.26 | 0.25 | Bridged; the UI never sends the new requests — equivalent to the 0.25 experience |
|
|
407
|
-
| Old (≤ 2.2.1) | 0.26 | any | Not in the adapter's version table → unbridged (hidden treatment) |
|
|
408
|
-
| Old | 0.25 | 0.26 | Bridged as 0.25; new methods are refused with the adapter's `adapter-method-unknown`, and `/sidebar/write` carrying a virtual id errors against the local plugin (no file written) — single-feature degradation |
|
|
409
|
-
| One new, one old | any | any | Core remote sessions work; the adapter digests differ → the bridge comparison fails → hidden treatment, until both ends run the same version |
|
|
410
|
-
|
|
411
|
-
The technical detail behind each adaptation — anchor selectors, breakpoints, what was traded away — is in the "compatible plugins" section of the [interface doc](docs/interface.md).
|
|
298
|
+
| [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar) | Full-screen mobile right-sidebar drawer with notch safe-area padding and swipe-back; **Plugin Bridge** built-in adapter for `0.25.0` through `0.28.2` (seven versions) (reads/writes host file/Git/task panels in remote sessions with host owner consent) | 0.19.1 / adapter 0.25–0.28.2 |
|
|
299
|
+
| [@nanmicoder/dsh-agent-teams](https://github.com/NanmiCoder/dsh-agent-teams) | Repositions AgentTeams floating pill below session header and preserves parent session back-link | 0.1.9 |
|
|
300
|
+
| [@ychris12138/dsh-usage-stats](https://github.com/Ychris12138/dsh-usage-stats) | Moves usage & balance entry into the mobile home chips bar | 0.2.9 |
|
|
301
|
+
| [@opendsh/dsh-plugin-scheduled-tasks](https://github.com/Ceelog/dsh-plugins) | Moves scheduled tasks entry into the mobile home chips bar | 0.2.3 |
|
|
302
|
+
| dsh-at-file | Deduplicates `.dsh-uploads/` attachment rows on mobile | 0.6.7 |
|
|
303
|
+
| [@ace-zone/dsh-market](https://www.npmjs.com/package/@ace-zone/dsh-market) | Compacts marketplace modal header on mobile so the close button stays reachable | 0.1.66 |
|
|
304
|
+
| [dsh-vision-toolkit](https://www.npmjs.com/package/@anionex/dsh-vision-toolkit) | Works with mobile attachment uploads for image Q&A/OCR | — |
|
|
305
|
+
| [dsh-web-ui bundle](https://www.npmjs.com/package/@linxin666/dsh-web-ui-all) | Mobile width-constrained centered overlays for file tree / preview | — |
|
|
306
|
+
|
|
307
|
+
</details>
|
|
412
308
|
|
|
413
309
|
---
|
|
414
310
|
|