dsh-mobile 0.1.4 → 0.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  Notable changes to DSH Mobile are recorded here. GitHub Releases remain the source for downloadable packages and complete generated commit notes.
4
4
 
5
+ ## 0.2.0 - 2026-08-24
6
+
7
+ - Add independent LAN and remote access flows with separate paired-device stores.
8
+ - Add optional Tailscale Funnel and managed cpolar remote providers.
9
+ - Restore saved Android connections automatically and improve mobile loading over limited links.
10
+ - Page older session history on demand and compress eligible gateway responses.
11
+ - Build the pinned Funnel host from source and publish checksums, an SBOM, and third-party notices.
12
+
5
13
  ## 0.1.4 - 2026-08-23
6
14
 
7
15
  - Keep the plugin compatible with DeepSeek Harness 0.1.1.
package/README.en.md CHANGED
@@ -16,15 +16,15 @@
16
16
 
17
17
  <p align="center"><a href="README.md">简体中文</a> · <a href="CHANGELOG.md">Changelog</a></p>
18
18
 
19
- > The native app currently supports Android only; the iOS client remains a local experiment and is not included in builds or releases. This is a DeepSeek Harness community plugin.
19
+ > DSH Mobile 0.2.0 is a DeepSeek Harness community plugin for the DeepSeek Harness 0.1.1 series (verified with 0.1.1-rc.2); the native app supports Android only.
20
20
  >
21
- > Current release: DSH Mobile 0.1.4 supports DeepSeek Harness 0.1.1 and continues to refine mobile interaction and layout.
21
+ > **0.2.0 update**: adds optional remote access and improves mobile loading performance.
22
22
  >
23
- > The Android app uses a stable release signing key starting with 0.1.4. Users upgrading from 0.1.3 or earlier must uninstall the old app, install the new build, and pair once more; later releases can be installed in place. The plugin can be upgraded independently, and older app builds can continue connecting to the 0.1.4 plugin.
23
+ > Upgrading the app from 0.1.3 or earlier requires one uninstall and re-pair; older app builds can still use the 0.2.0 LAN path.
24
24
 
25
- <p align="center"><a href="https://github.com/saya-ch/dsh-mobile/releases"><strong>Download the Android app</strong></a></p>
25
+ <p align="center"><a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.2.0/dsh-mobile-android-v0.2.0.apk"><strong>Download Android app 0.2.0</strong></a> · <a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.2.0">Release notes and checksums</a></p>
26
26
 
27
- DSH Mobile is a DeepSeek Harness plugin that lets a mobile browser or the Android app connect over a protected LAN and keep using the same sessions, Workspaces, messages, and tools. It is a mobile entry point only; the DeepSeek Harness source is not modified and no public-Internet tunneling is needed.
27
+ DSH Mobile is a DeepSeek Harness plugin that lets a mobile browser or the Android app connect over a protected LAN or an optional Tailscale Funnel or cpolar remote path. Local and remote access keep the same sessions, Workspaces, messages, and tools while using separate switches and paired-device stores without modifying DeepSeek Harness source.
28
28
 
29
29
  Mobile access runs on its own HTTPS origin with pinned certificates; only paired devices pass validation.
30
30
 
@@ -67,18 +67,49 @@ dsh plugin --profile web add dshmarket
67
67
 
68
68
  Restart DSH, then search for **dsh-mobile** under **Settings → Plugin Market** and install it with one click.
69
69
 
70
- After starting DSH, open **Mobile Access** in the lower-left sidebar, then:
70
+ `setup` automatically selects and remembers the current LAN; Wi-Fi, hotspot, and IP changes normally recover without re-pairing. Use `--address 192.168.x.x` only when automatic selection fails. Settings, certificates, devices, and customization files live under `$DSH_HOME/mobile-access/`.
71
+
72
+ After installation, start DSH and use the connection guide below to choose LAN or remote access.
73
+
74
+ ## Connection guide
75
+
76
+ LAN and remote access are independent connections. Prefer LAN while the phone is near the computer for the lowest latency, and enable remote access only when leaving that network. Each path keeps its own switch, paired devices, and sign-in state.
71
77
 
72
- 1. Select **Create and copy key** or **Copy pairing link**; the panel shows a pairing QR code.
78
+ ### Local network
79
+
80
+ Use this when the phone and computer share Wi-Fi, Ethernet, or a phone hotspot. It is the default and simplest path.
73
81
 
74
82
  <p align="center">
75
- <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/desktop-qr.png" width="70%" alt="Mobile Access panel with the pairing QR code">
83
+ <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/lan-access.png" width="82%" alt="DSH Mobile LAN access, pairing QR code, and device management">
76
84
  </p>
77
85
 
78
- 2. In the Android app, tap **Scan QR code** and point the camera at the screen or tap **Scan**, select the computer, and paste the key or pairing link.
79
- 3. Pairing establishes persistent device trust; later launches do not ask again.
86
+ 1. Connect the phone and computer to the same local network, then open **Mobile Access Local network** in the lower-left corner of DeepSeek Harness.
87
+ 2. If needed, select **Enable local access**, then select **Create and copy key**. The panel displays a pairing QR code.
88
+ 3. In the Android app, open **Local network**, scan for computers, select the device, then scan the QR code or paste the pairing key.
89
+ 4. Pairing creates persistent device trust. Later app launches discover and connect automatically; Wi-Fi, hotspot, and DHCP address changes normally do not require pairing again.
80
90
 
81
- `setup` automatically selects and remembers the current LAN; Wi-Fi, hotspot, and IP changes normally recover without re-pairing. Use `--address 192.168.x.x` only when automatic selection fails. Settings, certificates, devices, and customization files live under `$DSH_HOME/mobile-access/`.
91
+ The app is optional: select **Copy pairing link** and open it in a mobile browser. The browser must manually trust the plugin certificate on the first visit.
92
+
93
+ ### Remote access
94
+
95
+ Use this after the phone leaves the computer's network. Remote access is disabled by default, and the phone needs neither the Tailscale nor cpolar app.
96
+
97
+ Remote providers may impose bandwidth and connection limits: the [cpolar Free plan](https://svip.cpolar.com/pricing) currently lists 1 Mbps, while [Tailscale Funnel](https://tailscale.com/docs/features/tailscale-funnel#requirements-and-limitations) has non-configurable bandwidth limits. DSH Mobile reduces transfer and waiting with 10-message pages, load-on-scroll history, gzip, and a persistent WebSocket, but it cannot raise provider quotas.
98
+
99
+ <p align="center">
100
+ <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/remote-access.png" width="82%" alt="DSH Mobile remote access and provider selection">
101
+ </p>
102
+
103
+ 1. Open **Mobile Access → Remote** in the lower-left corner of DeepSeek Harness and choose a provider:
104
+ - **Tailscale Funnel**: select **Enable remote access**, complete the one-time Tailscale sign-in on the official page, follow the panel prompt to allow Funnel, then return to DSH and wait until the connection is ready.
105
+ - **cpolar**: select **Install official component**, sign in to the cpolar dashboard and obtain an Authtoken, paste it, then select **Save and connect**. The component is downloaded into the plugin's private directory only after confirmation.
106
+ 2. When the panel reports that remote access is ready, select **Create remote pairing QR code**.
107
+ 3. In the Android app, open **Remote access** and scan the QR code to create its separate pairing.
108
+ 4. The app keeps device trust and reconnects automatically. Disable remote access when it is not needed; LAN access remains unchanged.
109
+
110
+ Tailscale Funnel has broad reach but may be unreliable from mainland China. cpolar is better suited to mainland networks. The plugin validates the pinned component download, stores its configuration and program entirely under `$DSH_HOME/mobile-access/`, and can remove them completely from the panel.
111
+
112
+ The public remote origin still requires DSH device pairing. Managed remote components currently support Windows x64.
82
113
 
83
114
  ## Extend and customize
84
115
 
@@ -96,7 +127,7 @@ It can also drive computer capabilities the phone can use, like reading the mach
96
127
 
97
128
  Two kinds of changes are supported: the phone UI itself (theme, layout, buttons), and computer capabilities the phone can use (browsing computer files, running programs on the computer). `/mobile` hands the request to the DSH agent, which edits files under the local DSH configuration directory (`$DSH_HOME/mobile-access/`); the phone client applies them automatically. UI changes live in `mobile.css`/`mobile.js`. Computer capabilities come from extensions under `extensions/`, whose `host.mjs` runs with the local user's privileges on the computer. DeepSeek Harness source is not modified.
98
129
 
99
- > When using computer-side capabilities, use only content you trust.
130
+ > `host.mjs` has the same privileges as a local program. Create and run only computer-side extensions that you understand and trust.
100
131
 
101
132
  The examples above, applied:
102
133
 
@@ -111,7 +142,7 @@ The examples above, applied:
111
142
 
112
143
  | Client | Best for | Notes |
113
144
  | --- | --- | --- |
114
- | Android app | Everyday use | Auto-discovery; private certificate pinning inside the app, no manual browser trust step |
145
+ | Android app | Everyday use | Separate Local and Remote entries; LAN discovery and a system-trusted remote HTTPS path |
115
146
  | Mobile browser | Temporary or cross-platform | Open the HTTPS origin shown by Mobile Access; trust the certificate manually on first visit |
116
147
 
117
148
  The Android app is a thin Kotlin WebView shell and contains no frontend copy; mobile browsers load the same page. For compatibility diagnosis, append `?frontend=stock` to the browser URL to temporarily use the previous desktop-page adaptation.
@@ -120,7 +151,10 @@ The Android app is a thin Kotlin WebView shell and contains no frontend copy; mo
120
151
 
121
152
  ```mermaid
122
153
  flowchart LR
123
- Phone["Android / mobile browser"] -->|"HTTPS + device session"| Gateway["DSH Mobile gateway"]
154
+ Phone["Android / mobile browser"] -->|"LAN HTTPS"| Lan["LAN gateway"]
155
+ Phone -->|"remote HTTPS"| Remote["separate remote gateway"]
156
+ Lan --> Gateway["DSH Mobile Gateway Core"]
157
+ Remote --> Gateway
124
158
  Gateway -->|"loopback proxy"| DSH["Stock DSH Web and Host"]
125
159
  ```
126
160
 
@@ -128,7 +162,9 @@ Three layers: the Host face for discovery, pairing, HTTPS, loopback proxying, an
128
162
 
129
163
  ## Security
130
164
 
131
- - Use the plugin only on a trusted LAN or trusted VPN; never expose it to the public Internet.
165
+ - Use the LAN listener only on a trusted home, office, or hotspot network; do not add your own port forwarding.
166
+ - A remote origin is publicly reachable, but unpaired requests cannot enter DSH; turn the remote switch off when it is not needed.
167
+ - cpolar downloads a pinned official build only after confirmation and verifies its size and SHA-256. It installs no system service, PATH entry, or startup task, and plugin cleanup removes its managed files.
132
168
  - A paired device is a fully trusted DeepSeek Harness operator and can run tools on the computer; revoke lost devices from the computer.
133
169
  - The LAN gateway listens only while Mobile Access is enabled; with it off, DSH keeps running normally on the computer.
134
170
 
@@ -138,7 +174,8 @@ See [SECURITY.md](SECURITY.md).
138
174
 
139
175
  | DSH Mobile | Verified DeepSeek Harness releases |
140
176
  | --- | --- |
141
- | `0.1.0–0.1.4` | `0.1.0-rc.5`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.1-rc.2` |
177
+ | `0.2.0` | `0.1.0-rc.5`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.1-rc.2` |
178
+ | `0.1.4` | `0.1.0-rc.5`, `0.1.0-rc.6`, `0.1.0-rc.7`, `0.1.1-rc.2` |
142
179
 
143
180
  At startup, the plugin verifies the DSH Host version and the frontend dependencies required by the mobile layout; an unverified release fails with a clear error instead of serving a broken page. CI also tracks the DSH main branch layout contract. If a DSH upgrade reports an incompatibility, update DSH Mobile first.
144
181
 
package/README.md CHANGED
@@ -17,20 +17,21 @@
17
17
  <p align="center">
18
18
  <a href="#能做什么">能做什么</a> ·
19
19
  <a href="#快速开始">快速开始</a> ·
20
+ <a href="#连接教程">连接教程</a> ·
20
21
  <a href="#扩展与自定义">扩展与自定义</a> ·
21
22
  <a href="CHANGELOG.md">更新记录</a> ·
22
23
  <a href="README.en.md">English</a>
23
24
  </p>
24
25
 
25
- > 当前原生 App 仅支持 Android;iOS 客户端尚处于本地实验阶段,暂不纳入构建与发布。本项目是 DeepSeek Harness 社区插件。
26
+ > DSH Mobile 0.2.0 DeepSeek Harness 社区插件,适配 DeepSeek Harness 0.1.1 系列(已验证 0.1.1-rc.2);原生 App 仅支持 Android。
26
27
  >
27
- > 当前版本:DSH Mobile 0.1.4,适配 DeepSeek Harness 0.1.1,并持续优化移动端交互与布局体验。
28
+ > **0.2.0 更新**:新增可选远程访问,并优化移动端加载体验。
28
29
  >
29
- > Android App 自 0.1.4 起使用稳定发布签名。已安装 0.1.3 或更早版本的用户需要卸载旧 App、安装新版并重新配对一次;此后版本可直接覆盖升级。插件可独立更新,旧版 App 仍可继续连接 0.1.4 插件。
30
+ > 0.1.3 或更早 App 升级时需卸载旧版并重新配对一次;旧版 App 仍可使用 0.2.0 的局域网功能。
30
31
 
31
- <p align="center"><a href="https://github.com/saya-ch/dsh-mobile/releases"><strong>下载 Android App</strong></a></p>
32
+ <p align="center"><a href="https://github.com/saya-ch/dsh-mobile/releases/download/v0.2.0/dsh-mobile-android-v0.2.0.apk"><strong>下载 Android App 0.2.0</strong></a> · <a href="https://github.com/saya-ch/dsh-mobile/releases/tag/v0.2.0">版本说明与校验文件</a></p>
32
33
 
33
- DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App 在受保护的局域网内连接电脑,继续使用同一份会话、工作区、消息和工具。它只作为移动入口接入,不修改 DeepSeek Harness 源码。
34
+ DSH Mobile 是一个 DeepSeek Harness 插件,让手机浏览器或 Android App 通过局域网,或可选的 Tailscale Funnel、cpolar 远程通道连接电脑,继续使用同一份会话、工作区、消息和工具。局域网与远程访问分别启停、分别管理设备,且都不修改 DeepSeek Harness 源码。
34
35
 
35
36
  移动访问使用独立的 HTTPS 与证书固定,只有配对过的设备能通过校验接入。
36
37
 
@@ -73,18 +74,49 @@ dsh plugin --profile web add dshmarket
73
74
 
74
75
  重启 DSH 后,在 **设置 → 插件市场** 里搜索 dsh-mobile,一键安装即可。
75
76
 
76
- 启动后,在 DeepSeek Harness 左下角打开“移动访问”,然后:
77
+ `setup` 会自动选择并记住当前局域网,切换 Wi-Fi、热点或 IP 后通常自动恢复;仅在自动选择失败时使用 `--address 192.168.x.x`。设置、证书、设备和自定义文件保存在 `$DSH_HOME/mobile-access/`。
78
+
79
+ 安装并启动 DSH 后,按照下一节选择局域网或远程连接。
80
+
81
+ ## 连接教程
82
+
83
+ 局域网和远程访问是两套相互独立的连接:在电脑附近优先使用局域网,延迟最低;离开当前网络时再启用远程访问。两边分别管理开关、设备和登录状态,互不影响。
77
84
 
78
- 1. 点击“生成并复制密钥”或“复制配对链接”,面板会显示配对二维码。
85
+ ### 局域网访问
86
+
87
+ 适合同一 Wi-Fi、以太网或手机热点,是默认且最简单的连接方式。
79
88
 
80
89
  <p align="center">
81
- <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/desktop-qr.png" width="70%" alt="桌面端移动访问面板与配对二维码">
90
+ <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/lan-access.png" width="82%" alt="DSH Mobile 局域网访问、配对二维码与设备管理">
82
91
  </p>
83
92
 
84
- 2. Android App 点击“扫码配对”,把手机对准电脑屏幕上的二维码即可;也可以点击“扫描”选择电脑后粘贴密钥或配对链接。
85
- 3. 配对完成后会建立持久设备信任,以后打开 App 无需重复输入。
93
+ 1. 让手机和电脑连接同一个局域网,在 DeepSeek Harness 左下角打开 **移动访问 → 局域网**。
94
+ 2. 如果尚未开启,点击 **开启局域网访问**;随后点击 **生成并复制密钥**,面板会显示配对二维码。
95
+ 3. 在 Android App 中进入 **局域网访问**,扫描发现电脑并点击设备,再扫描二维码或粘贴配对密钥。
96
+ 4. 配对完成后会建立持久设备信任。以后打开 App 会自动发现并连接,切换 Wi-Fi、热点或 DHCP 地址通常不需要重新配对。
86
97
 
87
- `setup` 会自动选择并记住当前局域网,切换 Wi-Fi、热点或 IP 后通常自动恢复;仅在自动选择失败时使用 `--address 192.168.x.x`。设置、证书、设备和自定义文件保存在 `$DSH_HOME/mobile-access/`。
98
+ 不安装 App 也可以访问:点击 **复制配对链接**,在手机浏览器中打开;首次访问需要按浏览器提示手动信任插件证书。
99
+
100
+ ### 远程访问
101
+
102
+ 适合手机离开电脑所在网络后使用。远程访问默认关闭,手机不需要另外安装 Tailscale 或 cpolar。
103
+
104
+ 远程服务可能受带宽和连接限额影响:[cpolar 免费方案](https://svip.cpolar.com/pricing) 当前为 1 Mbps,[Tailscale Funnel](https://tailscale.com/docs/features/tailscale-funnel#requirements-and-limitations) 也存在不可配置的带宽限制。DSH Mobile 通过 10 条分页、顶部按需加载、gzip 和 WebSocket 长连接减少流量与等待,但无法突破服务商限额。
105
+
106
+ <p align="center">
107
+ <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/screenshots/remote-access.png" width="82%" alt="DSH Mobile 远程访问与通道选择">
108
+ </p>
109
+
110
+ 1. 在 DeepSeek Harness 左下角打开 **移动访问 → 远程**,选择一种连接方式:
111
+ - **Tailscale Funnel**:点击 **启用远程访问**,在打开的官方页面完成一次 Tailscale 登录;按面板提示继续允许 Funnel,然后返回 DSH 等待连接就绪。
112
+ - **cpolar**:点击 **安装官方组件**,登录 cpolar 控制台取得 Authtoken,粘贴后点击 **保存并连接**。组件只会在确认后下载到插件私有目录。
113
+ 2. 状态变为“远程访问已就绪”后,点击 **生成远程配对二维码**。
114
+ 3. 在 Android App 中进入 **远程访问**,扫描二维码完成独立配对。
115
+ 4. 此后 App 会保存设备信任并自动重连;不使用时可以关闭远程访问,局域网连接不会受影响。
116
+
117
+ Tailscale Funnel 覆盖范围广,但在中国大陆网络下可能不稳定。cpolar 更适合国内网络;插件会校验下载组件的固定版本,配置与程序均保存在 `$DSH_HOME/mobile-access/`,可随时在面板中彻底清除。
118
+
119
+ 远程公开地址仍受 DSH 设备配对保护。当前托管的远程组件支持 Windows x64。
88
120
 
89
121
  ## 扩展与自定义
90
122
 
@@ -102,6 +134,8 @@ dsh plugin --profile web add dshmarket
102
134
 
103
135
  `/mobile` 把需求交给 DSH 对话中的 agent,由它直接修改本机 `$DSH_HOME/mobile-access/` 下的文件,保存后手机端自动生效。改动分两类:界面和交互在 `mobile.css`/`mobile.js`;需要电脑能力时用 `extensions/` 下的扩展,其 `host.mjs` 以本机用户权限在电脑上运行。不修改 DeepSeek Harness 源码。
104
136
 
137
+ > `host.mjs` 与本机程序拥有相同权限;仅创建和运行你理解并信任的电脑端扩展。
138
+
105
139
  示例的实际效果:
106
140
 
107
141
  <p align="center">
@@ -114,10 +148,10 @@ dsh plugin --profile web add dshmarket
114
148
  ## App 与手机浏览器
115
149
 
116
150
 
117
- | 方式 | 适合场景 | 说明 |
118
- | ------------- | ------------------ | ------------------------------------------------------------------------- |
119
- | Android App | 日常使用 | 自动发现;App 内保存私有证书信任,无需在浏览器手动信任证书 |
120
- | 手机浏览器 | 临时或跨平台访问 | 打开“移动访问”卡片显示的 HTTPS 地址;首次连接需在浏览器手动信任该证书 |
151
+ | 方式 | 适合场景 | 说明 |
152
+ | ------------- | ------------------ | ----------------------------------------------------------------------------- |
153
+ | Android App | 日常使用 | 首屏分开显示局域网与远程入口;局域网自动发现,远程使用系统信任的 HTTPS 通道 |
154
+ | 手机浏览器 | 临时或跨平台访问 | 打开“移动访问”卡片显示的 HTTPS 地址;首次连接需在浏览器手动信任该证书 |
121
155
 
122
156
  Android App 只是 Kotlin WebView 薄壳,不内置另一份网页;手机浏览器访问的是同一页面。需要排查兼容性时,可在浏览器地址后追加 `?frontend=stock`,临时回到旧的桌面页面适配模式。
123
157
 
@@ -125,7 +159,10 @@ Android App 只是 Kotlin WebView 薄壳,不内置另一份网页;手机浏
125
159
 
126
160
  ```mermaid
127
161
  flowchart LR
128
- Phone["Android App / 手机浏览器"] -->|"HTTPS + 设备会话"| Gateway["DSH Mobile 网关"]
162
+ Phone["Android App / 手机浏览器"] -->|"局域网 HTTPS"| Lan["局域网网关"]
163
+ Phone -->|"远程 HTTPS"| Remote["独立远程网关"]
164
+ Lan --> Gateway["DSH Mobile Gateway Core"]
165
+ Remote --> Gateway
129
166
  Gateway -->|"回环代理"| DSH["原生 DSH Web 与 Host"]
130
167
  DSH -->|"同一工作区、会话和事件流"| Phone
131
168
  ```
@@ -134,7 +171,9 @@ flowchart LR
134
171
 
135
172
  ## 安全
136
173
 
137
- - 仅在可信家庭、办公局域网或可信 VPN 中使用,不要转发到公网。
174
+ - 局域网监听只用于可信家庭、办公网络或可信热点;不要自行做端口转发。
175
+ - 远程地址可从公网到达,但未配对请求无法进入 DSH;不使用时应关闭远程开关。
176
+ - cpolar 仅在用户确认后下载固定官方版本并校验大小和 SHA-256;不会安装系统服务、写入 PATH 或设置开机启动,插件清理会删除其托管文件。
138
177
  - 配对设备拥有控制电脑端 DeepSeek Harness 的能力,应视为完全可信设备;丢失手机后应在电脑端撤销设备。
139
178
  - 移动网关开启时才监听局域网;关闭后 DeepSeek Harness 仍正常在电脑本机运行。
140
179
 
@@ -143,9 +182,10 @@ flowchart LR
143
182
  ## 兼容性
144
183
 
145
184
 
146
- | DSH Mobile | 已验证的 DeepSeek Harness |
147
- | ------------------ | ------------------------------------------ |
148
- | `0.1.0–0.1.4` | `0.1.0-rc.5`、`0.1.0-rc.6`、`0.1.0-rc.7`、`0.1.1-rc.2` |
185
+ | DSH Mobile | 已验证的 DeepSeek Harness |
186
+ | ------------ | -------------------------------------------------------- |
187
+ | `0.2.0` | `0.1.0-rc.5`、`0.1.0-rc.6`、`0.1.0-rc.7`、`0.1.1-rc.2` |
188
+ | `0.1.4` | `0.1.0-rc.5`、`0.1.0-rc.6`、`0.1.0-rc.7`、`0.1.1-rc.2` |
149
189
 
150
190
  插件启动时会检查 DSH Host 版本和移动布局所需的前端依赖,遇到未经验证的版本会直接报错而不是带病启动;CI 也会持续跟踪 DSH 主分支的布局契约。升级 DSH 后如遇兼容提示,请先升级 DSH Mobile。
151
191
 
package/SECURITY.md CHANGED
@@ -21,7 +21,10 @@ The maintainer will acknowledge a complete report within seven days. Publication
21
21
  - Browser clients require a certificate trusted by that browser platform. Android uses the pairing-key-bound app-private CA. Its WebView exception is restricted to `SSL_UNTRUSTED` for an exact-origin, currently valid leaf signed by that CA; hostname, validity, signature, and every other TLS error remain fail-closed.
22
22
  - Keep pairing closed except during a short local onboarding action.
23
23
  - Revoke a lost device immediately and rotate the device registry if credential theft is suspected.
24
- - Do not expose the gateway directly to the public Internet.
24
+ - Do not expose the LAN gateway through router port forwarding. Optional remote access uses a separate loopback gateway behind the selected Tailscale Funnel or cpolar service. The provider terminates public TLS, while DSH pairing, device authentication, CSRF checks, and session revocation remain enforced by the plugin gateway.
25
+ - The Funnel node stores its Tailscale login state under `$DSH_HOME/mobile-access/remote/tailscale/`. The plugin does not request or store a Tailscale password, Auth Key, or OAuth secret.
26
+ - cpolar is downloaded only after confirmation from a pinned official artifact whose size and SHA-256 are verified. Its Authtoken is stored in a private, self-update-disabled configuration under `$DSH_HOME/mobile-access/`, never returned by the admin API or written to logs. Cleanup removes the managed executable, configuration, logs, and independent remote device registry.
27
+ - Disabling remote access stops the selected provider process without affecting LAN access. Resetting remote access also removes provider state and the independent remote device registry.
25
28
  - Treat every paired device as a fully trusted operator. Stock DSH methods reached through the authenticated loopback proxy may read configuration or run tools with the desktop user's authority.
26
29
  - Treat `mobile.js` as application code with the paired page's same-origin authority. Restrict write access to trusted host-side DSH sessions and review generated API calls or browser-permission use.
27
30
  - Treat every extension `host.mjs` as a local program with the desktop user's Node.js privileges. It is never sandboxed and is not editable through the mobile gateway; only place code there that you trust.
@@ -0,0 +1,5 @@
1
+ # Third-party notices
2
+
3
+ DSH Mobile includes a small Windows helper built from the source in `native/funnel-host`. The helper uses [Tailscale tsnet](https://pkg.go.dev/tailscale.com/tsnet) `v1.102.3`, distributed under the BSD 3-Clause license. Its exact transitive Go module versions are recorded in `native/funnel-host/go.mod` and `native/funnel-host/go.sum`; each dependency remains subject to its own license.
4
+
5
+ The optional cpolar component is not included in the npm package or Android App. When a user explicitly chooses cpolar installation, DSH Mobile downloads the pinned official archive shown in the UI, verifies its size and SHA-256 digest, and stores it only under the user's DSH Mobile data directory. cpolar remains subject to its [terms of service](https://www.cpolar.com/tos).
package/lib/cli.js CHANGED
@@ -8,6 +8,47 @@ import { X509Certificate, createPrivateKey, createPublicKey } from "node:crypto"
8
8
  import { generate } from "selfsigned";
9
9
  import "@deepseek-ai/cordis";
10
10
  import "@deepseek-ai/schemastery";
11
+ //#region src/private-file.ts
12
+ const execFile$3 = promisify(execFile);
13
+ let userSidTask;
14
+ async function currentWindowsUserSid() {
15
+ userSidTask ??= execFile$3("whoami.exe", [
16
+ "/user",
17
+ "/fo",
18
+ "csv",
19
+ "/nh"
20
+ ], {
21
+ encoding: "utf8",
22
+ windowsHide: true
23
+ }).then(({ stdout }) => {
24
+ const match = /,"(S-\d(?:-\d+)+)"\s*$/u.exec(stdout.trim());
25
+ if (match?.[1] === void 0) throw new Error("unable to resolve the current Windows user SID");
26
+ return match[1];
27
+ });
28
+ return userSidTask;
29
+ }
30
+ /** Restrict a sensitive regular file to the current user and Windows administrators. */
31
+ async function restrictPrivateFile(file, mode = 384) {
32
+ await chmod(file, mode);
33
+ if (process.platform !== "win32") return;
34
+ const userSid = await currentWindowsUserSid();
35
+ await execFile$3("icacls.exe", [
36
+ file,
37
+ "/inheritance:r",
38
+ "/grant:r",
39
+ `*${userSid}:(F)`,
40
+ "*S-1-5-18:(F)",
41
+ "*S-1-5-32-544:(F)",
42
+ "/remove:g",
43
+ "*S-1-1-0",
44
+ "*S-1-5-11",
45
+ "*S-1-5-32-545"
46
+ ], {
47
+ encoding: "utf8",
48
+ windowsHide: true
49
+ });
50
+ }
51
+ //#endregion
11
52
  //#region src/managed-setup.ts
12
53
  const execFile$2 = promisify(execFile);
13
54
  const VIRTUAL_INTERFACE_MARKERS = [
@@ -156,8 +197,8 @@ async function atomicWrite(file, contents) {
156
197
  });
157
198
  const temporary = join(directory, `.${basename(file)}.${process.pid}.tmp`);
158
199
  await writeFile(temporary, contents, { mode: 384 });
159
- await chmod(temporary, 384);
160
200
  await rename(temporary, file);
201
+ await restrictPrivateFile(file);
161
202
  }
162
203
  /** Create a long-lived CA or migrate the legacy self-signed server certificate as that CA. */
163
204
  async function ensureManagedCa(setup, legacy) {
@@ -207,10 +248,12 @@ async function ensureManagedCa(setup, legacy) {
207
248
  await Promise.all([atomicWrite(setup.caCertFile, certPem), atomicWrite(setup.caKeyFile, keyPem)]);
208
249
  }
209
250
  if (certPem === void 0 || keyPem === void 0) throw new Error("managed TLS CA creation did not produce key material");
251
+ await Promise.all([restrictPrivateFile(setup.caCertFile), restrictPrivateFile(setup.caKeyFile)]);
210
252
  return assertMatchingCa(certPem, keyPem);
211
253
  }
212
254
  /** Sign and atomically install a server leaf for the interface's current address. */
213
255
  async function refreshManagedServerCertificate(setup, address) {
256
+ await Promise.all([restrictPrivateFile(setup.tls.caCertFile), restrictPrivateFile(setup.tls.caKeyFile)]);
214
257
  const [caCert, caKey] = await Promise.all([readFile(setup.tls.caCertFile, "utf8"), readFile(setup.tls.caKeyFile, "utf8")]);
215
258
  assertMatchingCa(caCert, caKey);
216
259
  const now = /* @__PURE__ */ new Date();
@@ -395,11 +438,11 @@ async function setup(args) {
395
438
  await refreshManagedServerCertificate(managedSetup, network.address);
396
439
  await writeFile(androidCertificate, ca.raw, { mode: 384 });
397
440
  await Promise.all([
398
- chmod(caCertFile, 384),
399
- chmod(caKeyFile, 384),
400
- chmod(certFile, 384),
401
- chmod(keyFile, 384),
402
- chmod(androidCertificate, 384)
441
+ restrictPrivateFile(caCertFile),
442
+ restrictPrivateFile(caKeyFile),
443
+ restrictPrivateFile(certFile),
444
+ restrictPrivateFile(keyFile),
445
+ restrictPrivateFile(androidCertificate)
403
446
  ]);
404
447
  if (options.configureFirewall) await configureWindowsFirewall(options.port);
405
448
  const customCss = join(directory, "mobile.css");
@@ -442,6 +485,7 @@ async function setup(args) {
442
485
  ...managedSetup,
443
486
  tls: Object.fromEntries(Object.entries(managedSetup.tls).map(([key, value]) => [key, typeof value === "string" ? value.replaceAll("\\", "/") : value]))
444
487
  }, null, 2)}\n`, { mode: 384 }), writeFile(join(directory, "control.json"), "{\"version\":1,\"enabled\":true}\n", { mode: 384 })]);
488
+ await Promise.all([restrictPrivateFile(join(directory, "setup.json")), restrictPrivateFile(join(directory, "control.json"))]);
445
489
  console.log(`DSH Mobile follows ${network.name} and is currently configured for ${origin}`);
446
490
  console.log(`Install this CA certificate on Android once: ${androidCertificate}`);
447
491
  console.log(`Ask DSH to customize the mobile Web UI and features in: ${customCss} and ${customScript}`);