dsh-mobile 0.6.18 → 0.7.1
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 +33 -1
- package/CONTRIBUTORS.md +4 -1
- package/README.en.md +34 -20
- package/README.md +34 -20
- package/SECURITY.md +7 -0
- package/THIRD_PARTY_NOTICES.md +33 -0
- package/apps/mobile/README.md +28 -3
- package/apps/mobile/README.zh-CN.md +28 -3
- package/apps/mobile/release.json +3 -3
- package/docs/CADDY_MANAGED.en.md +1 -1
- package/docs/CADDY_MANAGED.md +1 -1
- package/docs/CLIENT_MODULES.en.md +2 -0
- package/docs/CLIENT_MODULES.md +2 -0
- package/docs/CLOUDFLARE_TUNNEL.en.md +7 -5
- package/docs/CLOUDFLARE_TUNNEL.md +7 -5
- package/docs/CPOLAR_SUBDOMAIN.en.md +2 -2
- package/docs/CPOLAR_SUBDOMAIN.md +2 -2
- package/docs/IROH.en.md +65 -0
- package/docs/IROH.md +65 -0
- package/docs/MOBILE_PLUGIN_ADAPTATION.en.md +8 -0
- package/docs/MOBILE_PLUGIN_ADAPTATION.md +8 -0
- package/docs/README.md +2 -1
- package/docs/SELF_HOSTED_ORIGIN.en.md +26 -1
- package/docs/SELF_HOSTED_ORIGIN.md +27 -2
- package/docs/SKIN_COMPATIBILITY.en.md +2 -0
- package/docs/SKIN_COMPATIBILITY.md +2 -0
- package/docs/SLOW_CONNECTIONS.en.md +8 -0
- package/docs/SLOW_CONNECTIONS.md +8 -0
- package/lib/cli.js.map +1 -1
- package/lib/client.js +1475 -240
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +270 -2
- package/lib/index.mjs +1934 -251
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-layout.js +17 -6
- package/lib/mobile-layout.js.map +1 -1
- package/native/iroh-host/LICENSE.dsh-mobile-iroh +21 -0
- package/native/iroh-host/LICENSE.dsh-tether +21 -0
- package/native/iroh-host/NOTICE.dsh-mobile-iroh +13 -0
- package/native/iroh-host/README.md +66 -0
- package/package.json +14 -5
- package/packages/question-fixes/package.json +1 -1
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
DeepSeek Harness 是这个社区维护的 Android WebView App 的显示名称。它直接打开插件提供的已认证 HTTPS 页面,不打包另一套 DSH 前端;App 与手机浏览器获得相同的 DSH 功能,以及电脑端配置的移动页面定制。
|
|
6
6
|
|
|
7
|
-
当前正式 App 为 **0.
|
|
7
|
+
当前正式 App 为 **0.7.0**;构建号为 85。npm 插件与 Android App 独立编号、独立更新;插件升级不代表需要重新安装 APK。此次改进见 [0.7.0 更新](#070-更新)。
|
|
8
8
|
|
|
9
9
|
支持 Android 10 及以上系统。iOS 本地实验不属于已发布或受支持的客户端。
|
|
10
10
|
|
|
11
11
|
## 安装与配对
|
|
12
12
|
|
|
13
13
|
1. 按[项目快速开始](../../README.md#快速开始)安装并启用插件。局域网配置可通过命令行或电脑端的 **移动访问** 面板完成。
|
|
14
|
-
2. 从 [GitHub Releases](https://github.com/saya-ch/dsh-mobile/releases/tag/android-v0.
|
|
14
|
+
2. 从 [GitHub Releases](https://github.com/saya-ch/dsh-mobile/releases/tag/android-v0.7.0) 安装已签名 APK。原有 HTTPS 通道可选兼容版 `.apk`;arm64-v8a/x86_64 设备使用实验性 iroh 时选完整版 `-iroh.apk`。两包使用相同包名和正式签名,选一个原位升级即可保留配对;不同签名的 Debug 包不能直接覆盖正式版,不要为绕过签名不匹配而卸载或清除数据。
|
|
15
15
|
3. 在电脑 **移动访问 → 局域网访问** 中生成密钥/配对链接,或配置远程通道并生成远程配对二维码。在 App 选择对应方式,扫码或粘贴链接。
|
|
16
16
|
4. 为电脑命名。每条局域网或远程配对单独保存;手机无需安装 cpolar、Cloudflare 或 Tailscale App。
|
|
17
17
|
|
|
@@ -36,6 +36,16 @@ DeepSeek Harness 是这个社区维护的 Android WebView App 的显示名称。
|
|
|
36
36
|
|
|
37
37
|
删除本机记录会移除保存的设备令牌和固定 CA,不等于撤销电脑授权或清除所有 WebView 短期 Cookie。需要立即终止访问时,请在电脑的 **移动访问** 中撤销设备。Android“清除应用数据”会删除全部配对和网页数据,不是普通重连的处理步骤。
|
|
38
38
|
|
|
39
|
+
## 0.7.0 更新
|
|
40
|
+
|
|
41
|
+
App 0.7.0/构建 85 在完整版中增加可选的实验性 iroh 配对和原生传输,配合插件 0.7.0 及以上版本使用。兼容版不含原生库,保留现有 HTTPS 通道并继续支持 32 位设备。旧版更新入口默认下载兼容版;使用 iroh 时请主动选择完整版。两包均保留设备列表、HTTPS 配对和原有正式签名。独立实验 App 的记录不会自动迁移。
|
|
42
|
+
|
|
43
|
+
iroh 默认关闭。电脑端在 **移动访问** 的连接列表选择 **iroh(实验性)**,安装经过校验的组件并开启通道后,生成专属配对二维码。直连和中继可用性取决于两端网络,手机浏览器不能直接使用这条传输,详见 [iroh 使用说明](../../docs/IROH.md)。
|
|
44
|
+
|
|
45
|
+
## 0.6.8 更新
|
|
46
|
+
|
|
47
|
+
App 0.6.8/构建 84 让首次页面下载沿用既有连接总期限,不再被较短页面计时器打断后自动重新下载。首次加载确实失败时保留明确重试;已经正常打开的页面仍可恢复连接,TLS 验证和取消能力不变。这项原生修复需要升级 App,不能仅靠更新插件获得。
|
|
48
|
+
|
|
39
49
|
## 0.6.7 更新
|
|
40
50
|
|
|
41
51
|
App 0.6.7/构建 83 配合插件 0.6.16 及以上版本,新增可选的同电脑局域网/远程切换,默认仍为手动。分别配对两个入口后,在设备列表设置中选择切换方式;符合条件的同会话纯文本草稿可随自动切换迁移,但这不是完整页面状态迁移,详见[切换规则](#同电脑自动连接)。
|
|
@@ -147,12 +157,27 @@ Set-Location apps/mobile/android
|
|
|
147
157
|
|
|
148
158
|
Debug APK 位于 `app/build/outputs/apk/debug/app-debug.apk`。正式发布沿用只保存在仓库 Secrets 中的签名密钥,本地未签名 Release 构建或 Debug 安装不能替代正式签名核验。
|
|
149
159
|
|
|
160
|
+
每次构建默认自带实验性 iroh 传输。需要 Rust 1.99.1(含 `aarch64-linux-android` 与 `x86_64-linux-android` 目标)与明确指定的 NDK:
|
|
161
|
+
|
|
162
|
+
```powershell
|
|
163
|
+
Set-Location apps/mobile/android
|
|
164
|
+
./gradlew.bat :app:assembleDebug -PirohNdk=C:/Android/Sdk/ndk/25.2.9519653 -x :app:lintAnalyzeDebugUnitTest -x :app:lintAnalyzeDebugAndroidTest
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
传 `-PirohNative=false` 可走免工具链的快速通道,构建的是原 HTTPS 外壳:没有 iroh 配对入口,`IrohNative` 会报告自身不可用。传输库生成在 `app/build/generated/iroh/jniLibs/` 下,永不提交。启用原生库的构建不改变包名与既定正式签名;实验阶段的独立 alpha 签名在这里不使用。
|
|
168
|
+
|
|
169
|
+
原生构建还会从固定提交的官方 Maven 归档引入与 `native/Cargo.lock` 匹配的 JVM TLS 校验组件。必须保留其 JNI/Proguard 规则,仅打包 `.so` 不足以完成接入。APK 验证会检查 Debug 和压缩后的 Release 是否保留所需 JVM 类和原生入口名称。
|
|
170
|
+
|
|
150
171
|
当前源码的 Debug 构建使用包名 `io.github.sayach.dshmobile.debug` 与 `-debug` 版本后缀,可与正式 App 并存,存储和配对彼此独立。它不会升级或迁移旧版无后缀 Debug 应用;切换包名时应保留旧应用和数据,不要为此卸载或清除。Release 构建继续使用 `io.github.sayach.dshmobile` 与既定正式签名。
|
|
151
172
|
|
|
152
|
-
已核验的正式版本为 [Android 0.
|
|
173
|
+
已核验的正式版本为 [Android 0.7.0](https://github.com/saya-ch/dsh-mobile/releases/tag/android-v0.7.0),构建 85。兼容版与完整版分别附有对应的 `.sha256` 校验文件;两个公开下载均沿用正式发布签名及包名 `io.github.sayach.dshmobile`。旧版本历史集中在 [CHANGELOG](../../CHANGELOG.md)。
|
|
153
174
|
|
|
154
175
|
## 验收与已知限制
|
|
155
176
|
|
|
177
|
+
- App 0.7.0 的两份公开 APK 均通过校验值、Manifest、正式签名、许可文本和对齐检查;完整版另检查两个 ABI 及保留的 JVM TLS/JNI 入口,兼容版确认没有原生库。此前的 arm64 开发构建有不依赖 ADB 转发的原生 iroh 网关/定制资源真机链路证据;最后补齐 JVM TLS 依赖后尚未新增真机公网中继回归,不能仅凭中继连接存在宣称数据已走中继。
|
|
178
|
+
|
|
179
|
+
- App 0.6.8 的公开 APK 已核验身份、校验值和既定正式签名。Debug 包同签名原位升级后可打开 DSH,并保留原设备记录;受控慢链路故障注入在配对前中断,不计为通过。
|
|
180
|
+
|
|
156
181
|
- App 0.6.7 Debug 构建在 PJW110、Android 16、WebView 153 上,通过 USB 映射和固定 CA 的 HTTPS 连接隔离的 DSH 0.2.1-rc.2。真实自动接管保留同会话 Unicode 草稿,目标原生缩放/续期可用且不额外刷新;还通过确认取消、目标加载中按返回取消与迟到响应、原生续期取消、键盘返回和选择器取消。临时配对与映射已清理;这不是实际 Wi-Fi/蜂窝切网,也不是正式签名 APK 的装机验收。
|
|
157
182
|
- 连接控制的开发包使用 Debug 元数据 0.6.4/构建 80,在 PJW110、Android 16、Google WebView 153.0.8010.36 上,通过 USB 和固定 CA 的 HTTPS 连接隔离的 DSH 0.2.0-rc.2/插件 0.6.10。检查了配对/启动/已保存连接取消、系统返回、手动失败收尾,以及停止原生恢复后从失效页面返回;暂停模块响应时也验证了提前露出网页、非按钮区域触摸透传、取消和放行后完成。原配对保留,临时 QA 记录已清理。
|
|
158
183
|
- App 0.6.4 的 Debug 构建在同一 WebView 中覆盖文档替换/刷新、拒绝旧选择、接收受控 PNG 字节并形成已解码草稿预览;还检查了暂停导航及 HTTP 204 后选择器的可用性。更早的检查覆盖模型菜单返回、旋转保留草稿/引用/图片、已知撤销、重新配对、图标选择与列表顺序,均未发送模型请求。
|
package/apps/mobile/release.json
CHANGED
package/docs/CADDY_MANAGED.en.md
CHANGED
|
@@ -24,7 +24,7 @@ Configure the public HTTPS origin, DNS-01 credentials and local HTTPS port separ
|
|
|
24
24
|
|
|
25
25
|
## Installation, startup, and readiness
|
|
26
26
|
|
|
27
|
-
1. On the computer, open **Mobile Access →
|
|
27
|
+
1. On the computer, open **Mobile Access → Own reverse proxy** and choose **Managed Caddy**.
|
|
28
28
|
2. Choose **Install managed Caddy** and confirm the download. The plugin downloads the fixed [DSH Mobile component distribution](https://github.com/saya-ch/dsh-mobile/releases/tag/caddy-component-2.11.7-tencentcloud-0.4.3-r1), checking its exact length, SHA-256, core and DNS module versions. Unavailable or invalid assets are not executed.
|
|
29
29
|
3. Enter the public HTTPS origin, such as `https://dsh.example.com`, select Tencent Cloud DNSPod, supply a `SecretId` and `SecretKey` with the required zone permissions, and set the local HTTPS port, such as `8443`. Point the domain at the public ingress and, where needed, forward public TCP 443 to port 8443 on the computer.
|
|
30
30
|
4. Choose **Save and start Caddy**. Installed is not ready: the panel reports readiness only after a computer-side public HTTPS probe validates the certificate and confirms that the entry belongs to the current DSH instance. That probe still does not prove phone-network reachability.
|
package/docs/CADDY_MANAGED.md
CHANGED
|
@@ -24,7 +24,7 @@ Android App / 浏览器 → 公网 HTTPS 域名(Caddy 终止 TLS)
|
|
|
24
24
|
|
|
25
25
|
## 安装、启动与就绪
|
|
26
26
|
|
|
27
|
-
1. 在电脑端打开 **移动访问 →
|
|
27
|
+
1. 在电脑端打开 **移动访问 → 自有反向代理**,选择 **托管 Caddy**。
|
|
28
28
|
2. 点击 **安装托管 Caddy** 并确认下载。插件从固定的 [DSH Mobile 组件分发](https://github.com/saya-ch/dsh-mobile/releases/tag/caddy-component-2.11.7-tencentcloud-0.4.3-r1) 下载,核对完整大小、SHA-256、Caddy 核心与 DNS 模块版本;来源不可用或校验失败时不会执行文件。
|
|
29
29
|
3. 填写公网 HTTPS 地址,例如 `https://dsh.example.com`,选择腾讯云 DNSPod,填写具有所需区域权限的 `SecretId` 和 `SecretKey`,再设置本机 HTTPS 端口,例如 `8443`。让域名解析到你的公网入口,并按需将公网 TCP 443 映射至电脑的 8443。
|
|
30
30
|
4. 点击 **保存并启动 Caddy**。组件已安装不等于连接就绪:只有电脑端经公网 HTTPS 验证证书,并确认入口属于当前 DSH 实例后,面板才显示已就绪。这个探测仍不能证明手机网络可达。
|
|
@@ -8,6 +8,8 @@ Choose which client components the dedicated mobile page loads. This does not un
|
|
|
8
8
|
|
|
9
9
|
Selection applies to whole module IDs, not individual buttons, cards or sidebars inside a module. A plugin may register several modules or share/depend on a module with other features. A feature not delivered as an optional client module may not be independently removable here.
|
|
10
10
|
|
|
11
|
+
The selection covers client modules only. A plugin may also inject page-level styles from the host into every page — wallpapers and whole-page palettes are the usual case. That content is not a client module, so deselecting one does not remove it: disable the whole plugin, or let the plugin adapt itself to the mobile layout. See [skins and mobile layout](SKIN_COMPATIBILITY.en.md) for skin diagnosis.
|
|
12
|
+
|
|
11
13
|
## Computer defaults and this device
|
|
12
14
|
|
|
13
15
|
- On the **computer: Settings → General → Mobile page modules → Manage**, set defaults for this computer's mobile access. Enable a LAN or remote access entry first so the current DSH module catalog can be read.
|
package/docs/CLIENT_MODULES.md
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
选择作用于完整模块 ID,不能分别隐藏同一模块中的按钮、卡片或侧栏。一个插件可能注册多个模块,也可能与其他功能共用或依赖某个模块;未拆为可选客户端模块的功能不一定能在这里单独关闭。
|
|
10
10
|
|
|
11
|
+
模块选择只作用于客户端模块。插件还可以在服务端把样式直接注入每个页面——典型就是皮肤壁纸和整页配色;这类内容不是客户端模块,取消勾选不会让它消失。要彻底关掉请禁用整个插件,或由插件自己按移动布局适配。皮肤类插件的排查见[皮肤与移动布局](SKIN_COMPATIBILITY.md)。
|
|
12
|
+
|
|
11
13
|
## 电脑默认与本设备选择
|
|
12
14
|
|
|
13
15
|
- **电脑端:设置 → 通用设置 → 移动页面模块 → 管理**,修改此电脑的移动访问默认加载项。先开启一个局域网或远程访问入口,才能读取当前 DSH 的模块目录。
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[中文指南](CLOUDFLARE_TUNNEL.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> Already running cloudflared or a system service? Use the [own-reverse-proxy mode](SELF_HOSTED_ORIGIN.en.md#when-you-already-run-cloudflared-yourself) and route that tunnel to the protected HTTP backend shown in the panel, without another component installation or a tunnel Token stored by the plugin. The instructions below describe the separate plugin-managed option.
|
|
6
|
+
|
|
7
|
+
The built-in cloudflared provider runs in one of two modes, chosen through the Quick/Named tabs under **Mobile access → cloudflared**:
|
|
6
8
|
|
|
7
9
|
| | Quick tunnel (default) | Named tunnel |
|
|
8
10
|
| --- | --- | --- |
|
|
@@ -41,13 +43,13 @@ These labels follow [Cloudflare's current official guide](https://developers.clo
|
|
|
41
43
|
|
|
42
44
|
## Fill in the DSH Mobile panel
|
|
43
45
|
|
|
44
|
-
1. **Mobile access
|
|
45
|
-
2.
|
|
46
|
+
1. Select **cloudflared** in the **Mobile access** connection list.
|
|
47
|
+
2. Select **Named**, then **Show settings** to expand the form. For an existing saved configuration, choose **Edit** first.
|
|
46
48
|
3. Enter:
|
|
47
49
|
- **Public hostname**: `dsh.example.com`
|
|
48
50
|
- **Local forward port**: `3444`, matching the Service port from step 3
|
|
49
51
|
- **Connector token**: the token you copied
|
|
50
|
-
4. Choose **Save and connect
|
|
52
|
+
4. Choose **Save and connect** and read the actual connection status above. Saved settings do not establish public reachability. Switching tunnel type while connected first requires confirming disconnection.
|
|
51
53
|
|
|
52
54
|
Once saved, leaving the token field blank keeps the stored token, and the field never echoes a saved token back. **Remove the saved token** also **stops the cloudflared channel** and returns the provider to a quick tunnel. Nothing reconnects on its own: switch the channel back on afterwards.
|
|
53
55
|
|
|
@@ -55,7 +57,7 @@ Once saved, leaving the token field blank keeps the stored token, and the field
|
|
|
55
57
|
|
|
56
58
|
Moving to a fixed hostname means **an already paired phone must pair again**: the App does not send a saved device token to a changed Origin without new pairing. This client-side exact-address policy does not mean the server token automatically expires by hostname; remote channels share the device registry. Use a trusted entry you control and revoke a device on the computer if credential theft is suspected.
|
|
57
59
|
|
|
58
|
-
1. On the computer, open **
|
|
60
|
+
1. On the computer, open **Mobile access → cloudflared**, wait for readiness, then choose **Generate pairing QR code**. The panel shows the expiry time; generate a new code after expiry. Closing the pairing window does not revoke existing devices.
|
|
59
61
|
2. In the app, **open the Remote entry first** (the remote access setup page in the connection center), then scan the code.
|
|
60
62
|
|
|
61
63
|
> **A named tunnel must be scanned from inside the Remote flow.** The app treats platform suffixes such as `.ts.net`, cpolar and `.trycloudflare.com` as remote without asking. A domain you own is recognised as a remote candidate too, but the Local network flow accepts only non-candidate addresses, so scanning there is rejected as **Invalid QR code** and looks exactly like "cannot connect". Enter the Remote flow first, then scan.
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
[English guide](CLOUDFLARE_TUNNEL.en.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> 已经自己运行 cloudflared 或使用系统服务?可改用[自备反向代理模式](SELF_HOSTED_ORIGIN.md#已经自己运行-cloudflared-时),将现有隧道指向面板展示的受保护 HTTP 后端,无需再安装组件或在插件保存隧道 Token。以下步骤介绍由插件管理 cloudflared 的另一种方式。
|
|
6
|
+
|
|
7
|
+
内置的 cloudflared 通道有两种模式,在 **移动访问 → cloudflared** 的快速/命名隧道标签中切换:
|
|
6
8
|
|
|
7
9
|
| | 快速隧道(默认) | 命名隧道 |
|
|
8
10
|
| --- | --- | --- |
|
|
@@ -41,13 +43,13 @@ Cloudflare 提供公网域名与 TLS,插件在本机运行 `cloudflared`,把
|
|
|
41
43
|
|
|
42
44
|
## 在 DSH Mobile 面板中填写
|
|
43
45
|
|
|
44
|
-
1.
|
|
45
|
-
2.
|
|
46
|
+
1. 在 **移动访问** 的连接列表选择 **cloudflared**。
|
|
47
|
+
2. 选择 **命名隧道**,点击 **展开配置** 打开表单;已保存的配置先点击 **编辑**。
|
|
46
48
|
3. 依次填写:
|
|
47
49
|
- **公网域名**:`dsh.example.com`
|
|
48
50
|
- **本机转发端口**:`3444`(与第 3 步的 Service 端口一致)
|
|
49
51
|
- **连接器令牌**:粘贴上一步复制的令牌
|
|
50
|
-
4. 点击
|
|
52
|
+
4. 点击 **保存并连接**,以上方实际连接状态判断是否就绪;保存成功不等于公网已经可达。切换隧道类型时若有连接,先确认断开当前通道。
|
|
51
53
|
|
|
52
54
|
已保存后令牌输入框留空表示不更改;输入框里不会再回显已保存的令牌。「移除已保存的令牌」会**同时停止 cloudflared 通道**并把配置改回快速隧道。不会自动重连,之后需要手动重新打开开关。
|
|
53
55
|
|
|
@@ -55,7 +57,7 @@ Cloudflare 提供公网域名与 TLS,插件在本机运行 `cloudflared`,把
|
|
|
55
57
|
|
|
56
58
|
切到固定域名后,**已经配对过的手机必须重新配对**:App 不会把已存设备令牌发送给未经新配对验证的 Origin。这是客户端的精确地址发送策略,不代表服务端令牌自动按域名失效;远程通道共用设备注册表。请使用可信且由你控制的入口,怀疑凭据泄漏时在电脑端撤销设备。
|
|
57
59
|
|
|
58
|
-
1.
|
|
60
|
+
1. 电脑端在 **移动访问 → cloudflared** 中等待连接就绪,再点 **生成配对二维码**。面板显示有效截止时间;超时后须重新生成,已有设备授权不受配对窗口关闭影响。
|
|
59
61
|
2. 手机 App → **先进入「远程」入口**(连接中心的远程访问设置页)→ 再扫描二维码。
|
|
60
62
|
|
|
61
63
|
> **命名隧道必须在「远程」入口里扫码。** App 只把 `.ts.net`、cpolar、`.trycloudflare.com` 这类平台后缀当作「无需询问即为远程」;你自己的域名同样会被识别为可远程的候选地址,但「局域网」流程只接受非候选地址,所以在局域网界面扫码会被判为无效并提示**无效二维码 / Invalid QR code**,看起来就像「连不上」。先进入「远程」流程再扫即可。
|
|
@@ -12,7 +12,7 @@ The public entry still reaches the DSH Mobile Gateway with HTTPS, device pairing
|
|
|
12
12
|
|
|
13
13
|
1. Check that your cpolar plan supports reserving a secondary subdomain. The [official tutorial](https://www.cpolar.com/blog/cpolar-quick-start-tutorial-raspberry-pi-series) uses Basic plans or above; your account dashboard is the authority for current eligibility, available regions and naming rules.
|
|
14
14
|
2. Reserve a name under **Reserved → Reserve a secondary subdomain** in the [cpolar dashboard](https://dashboard.cpolar.com), and record the selected region.
|
|
15
|
-
3. Install the component and save the Authtoken under **Mobile access →
|
|
15
|
+
3. Install the component and save the Authtoken under **Mobile access → cpolar** in DSH.
|
|
16
16
|
|
|
17
17
|
The region must match the reservation. The official command example uses `cn_vip`, but do not copy it for a reservation in another region; confirm other identifiers against cpolar's current instructions. This guide does not maintain a fixed region or domain-suffix mapping.
|
|
18
18
|
|
|
@@ -52,7 +52,7 @@ If you already customize paths, listeners, TLS or other settings, **keep your cu
|
|
|
52
52
|
|
|
53
53
|
Leading and trailing whitespace is removed. The plugin only bounds the requested name's syntax: it starts with a letter or digit, contains letters, digits, `.`, `_`, `-`, and is at most 63 characters long. This is not a DNS-label specification or a guarantee of server acceptance. The account, region and cpolar's actual rules determine whether the reservation is valid. The [official subdomain instructions](https://www.cpolar.com/docs#configure-secondary-subdomain) and tutorial describe the `-subdomain` and `-region` arguments.
|
|
54
54
|
|
|
55
|
-
After checking that no tasks are running, quit DSH completely and reopen it, then connect under **Mobile access →
|
|
55
|
+
After checking that no tasks are running, quit DSH completely and reopen it, then connect under **Mobile access → cpolar**. Use the actual HTTPS address shown by the panel; do not construct a domain suffix yourself.
|
|
56
56
|
|
|
57
57
|
## Verification and pairing
|
|
58
58
|
|
package/docs/CPOLAR_SUBDOMAIN.md
CHANGED
|
@@ -12,7 +12,7 @@ cpolar 默认分配临时公网地址。账号预留子域名后,可请求使
|
|
|
12
12
|
|
|
13
13
|
1. 确认 cpolar 套餐支持保留二级子域名。[官方教程](https://www.cpolar.com/blog/cpolar-quick-start-tutorial-raspberry-pi-series)以基础套餐及以上为例;当前资格、可用地区及命名规则以你的账号控制台为准。
|
|
14
14
|
2. 在 [cpolar 控制台](https://dashboard.cpolar.com)的 **预留 → 保留二级子域名** 中保留名字,记录对应地区。
|
|
15
|
-
3. 在 DSH **移动访问 →
|
|
15
|
+
3. 在 DSH **移动访问 → cpolar** 中安装组件并保存 Authtoken。
|
|
16
16
|
|
|
17
17
|
地区必须与预留记录一致。官方命令示例使用 `cn_vip`,但不要将它照抄到其他地区的预留记录;其他地区的配置值请按 cpolar 当前说明确认。本指南不提供固定地区或域名后缀对照表。
|
|
18
18
|
|
|
@@ -52,7 +52,7 @@ DSH 加载器会整体替换条目的 `config`,不是逐键合并。因此不
|
|
|
52
52
|
|
|
53
53
|
配置值会去除首尾空白。插件的名字检查仅限制请求格式:以字母或数字开头,只含字母、数字、`.`、`_`、`-`,长度不超过 63 字符;这不是 DNS 标签规范,也不代表 cpolar 服务端会接受。预留是否有效仍取决于账号、地区和 cpolar 的实际规则。[官方子域名配置说明](https://www.cpolar.com/docs#configure-secondary-subdomain)与教程说明了 `-subdomain`、`-region` 参数。
|
|
54
54
|
|
|
55
|
-
确认没有正在运行的任务后,完整退出并重开 DSH,再在 **移动访问 →
|
|
55
|
+
确认没有正在运行的任务后,完整退出并重开 DSH,再在 **移动访问 → cpolar** 中连接。连接后使用面板实际显示的 HTTPS 地址,不要自行拼接域名后缀。
|
|
56
56
|
|
|
57
57
|
## 验证与配对
|
|
58
58
|
|
package/docs/IROH.en.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Experimental iroh connection
|
|
2
|
+
|
|
3
|
+
iroh replaces public domains and tunnel accounts with an encrypted direct connection (relay fallback when direct fails): the phone finds the computer's cryptographic identity first, then traffic returns to the loopback gateway. You need no domain, port mapping, or separate HTTPS tunnel.
|
|
4
|
+
|
|
5
|
+
> Plugin 0.7.0 and the full App 0.7.0 integrate the computer provider, pinned TLS, pairing UI and Android native transport. The feature is off by default and remains experimental. Direct connectivity, relay use and speed depend on both networks; availability on every campus, corporate or carrier network is not guaranteed.
|
|
6
|
+
> The design lives in the companion's [integration seam](https://github.com/saya-ch/dsh-mobile-iroh/blob/main/docs/INTEGRATION_SEAM.md).
|
|
7
|
+
|
|
8
|
+
## Prerequisites
|
|
9
|
+
|
|
10
|
+
- Windows x64 or Linux x64 (the only published sidecar targets).
|
|
11
|
+
- Android 10+ with the full `-iroh.apk`, supporting arm64-v8a and x86_64. The ordinary `.apk` is the compatibility build without native libraries: it retains existing HTTPS connections and serves older automatic-update links, but cannot use iroh. Mobile browsers cannot connect directly through iroh either. Both APKs have the same application ID and official signer; install only one.
|
|
12
|
+
- Press **Install official component** once to fetch the sidecar: the plugin downloads the pinned `dsh-mobile-iroh 0.1.0-alpha.1` release per platform (about 15 MB on Windows x64, 24 MB on Linux x64), verifies its SHA-256, and stores it in the plugin private directory. An installed companion plugin with a verified copy is reused instead, with no download. The Rust source lives with the main repository under `native/iroh-host` (shared core under `native/iroh-core`) and is built per platform in CI.
|
|
13
|
+
- DSH Mobile stays enabled; iroh is off by default. LAN runs independently, but only one remote provider is active: switching to iroh stops the previous remote channel.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
1. Under **Mobile access**, pick `iroh (Experimental)`.
|
|
18
|
+
2. Press **Install official component** (labelled with the download size): the first run downloads and verifies the sidecar; later runs reuse the verified copy. A hash mismatch or an unsupported platform stops here with a reason instead of a half-started channel. Merely switching the channel on never triggers a download.
|
|
19
|
+
3. Turn remote access on: the plugin resolves the endpoint identity, issues the endpoint gateway certificate (own CA, 90-day leaf, 30-day renewal headroom), starts a dedicated gateway, launches the sidecar, and waits for `ready`. `ready` means the sidecar is connected and the gateway serves its loopback socket.
|
|
20
|
+
4. Press **Generate iroh pairing QR code** and scan it with the full App. The descriptor binds the sidecar's six-digit code, the Gateway's one-time token and pinned CA. Generating another descriptor replaces the previous pairing window; pairing a phone redeems the Gateway token through the normal pair endpoint over iroh.
|
|
21
|
+
5. Switching off or switching providers stops the owned process and Gateway without deleting stored pairings. The independent App has a different package identity; its Keystore credentials are not automatically migrated into this App.
|
|
22
|
+
|
|
23
|
+
Main actions stay hidden before component installation. Afterwards, the toggle, QR generation and Copy pairing link buttons share one row. Copy stays visible but disabled without valid pairing data; copying an existing descriptor does not generate another pairing code.
|
|
24
|
+
|
|
25
|
+
To reclaim component storage, turn off iroh remote access, expand the component details and choose **Remove the downloaded iroh component**. This removes only the pinned program downloaded by this plugin, keeping identities, pairings and external companion packages. Later use can reinstall it or reuse a verified external copy. Removal while enabled is refused by the management API as well as the panel.
|
|
26
|
+
|
|
27
|
+
## Security boundaries
|
|
28
|
+
|
|
29
|
+
- The sidecar proxies only to a `127.0.0.1`/`::1` loopback gateway with end-to-end TLS; commands and events are versioned, bounded, and rejected closed on unknown fields.
|
|
30
|
+
- A rotated fingerprint, replaced binary, or moved file fails verification and parks the channel at `unavailable` instead of silently running a stale one.
|
|
31
|
+
- Transport revocation and device-token revocation are separate authorizations; their unified management is Phase 2 work. Computer-side revocation keeps working through the existing device management.
|
|
32
|
+
|
|
33
|
+
## Troubleshooting
|
|
34
|
+
|
|
35
|
+
Store the iroh identity and certificates in a directory tree writable only by the current user or trusted system accounts. DSH's default state under the Windows user profile is intended for this; custom locations also need private ancestors. Some AppData or shared locations grant other accounts write access and are rejected. Do not loosen ACLs, delete real identities or move state into untrusted storage to bypass the check. Back up and preserve existing identity files.
|
|
36
|
+
|
|
37
|
+
Before a component release, verify first use with the exact catalog-pinned executable, not only a locally compiled host or a directory initialized by hand. The controller invokes `tls-prepare` because `id` only reads existing identity state. On Windows, `DSH_IROH_PINNED_FIRST_USE=<verified executable> npx vitest run tests/iroh-pinned-first-use.test.ts` checks initialization, reuse and corruption refusal in owned private storage. A failure blocks component acceptance; do not loosen directory ACL checks or replace real identities to make it pass.
|
|
38
|
+
|
|
39
|
+
| Symptom | Meaning |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `iroh_companion_missing` | Nothing installed yet: press Install first, or point `DSH_MOBILE_IROH_COMPANION` at a companion absolute path to reuse |
|
|
42
|
+
| `iroh_sidecar_invalid` | An installed file was modified; reinstalling overwrites it in place, or reinstall the companion for a damaged companion copy |
|
|
43
|
+
| `iroh_platform_unsupported` | No sidecar is published for this OS/architecture |
|
|
44
|
+
| `iroh_identity_invalid` / `iroh_identity_failed` | Check private permissions on the state directory and its ancestors and check for damaged identity files, preserving originals. `id` reads an existing identity; first use invokes `tls-prepare`, not copying or deleting an identity |
|
|
45
|
+
| `iroh_start_timeout` | Inspect iroh errors in the DSH log and network connectivity, and verify that the pinned component runs; repeated pairing is not a substitute for process diagnosis |
|
|
46
|
+
| Channel `ready` but no phone connects | Check that the App is the full build, the device supports a packaged ABI and the descriptor is fresh. Computer readiness does not establish phone connectivity; a relay-service connection alone does not establish that data uses the relay |
|
|
47
|
+
|
|
48
|
+
## VPN, proxies and network environments
|
|
49
|
+
|
|
50
|
+
iroh prefers direct connections with relay fallback. Direct connections use QUIC/UDP; discovery and relays also need their network services to be reachable. The following environment factors **can** interfere; they are troubleshooting directions, not confirmed failure mechanisms for every VPN or network:
|
|
51
|
+
|
|
52
|
+
- **Global VPN/TUN** (system-level virtual adapters) may take over UDP and DNS so that neither direct nor relay handshakes complete. One user on Win11 + WSL2 mirrored networking over mobile data reported ~6 s to connect after disabling the global VPN, and recovery after excluding DSH Mobile from the VPN. This is community evidence, not a statement about every VPN/network combination or both ABIs.
|
|
53
|
+
- **DNS policy** (fake-IP, filtering): discovery depends on DNS resolution; fake-IP modes may resolve discovery domains to a proxy virtual IP.
|
|
54
|
+
- **Loopback entry**: the app reaches the local gateway via `*.iroh.localhost`; this loopback address should not be handed to a remote proxy/DNS.
|
|
55
|
+
|
|
56
|
+
Things to try (voluntary, subject to your own security policy):
|
|
57
|
+
|
|
58
|
+
1. Add DSH Mobile (package `io.github.sayach.dshmobile`) to a per-app bypass/exclusion list in your VPN/proxy client; debug builds and the standalone experimental app use different package names. This is the community Android App, not the official DSH Desktop application; configure the one you actually installed.
|
|
59
|
+
2. If your client supports per-domain rules, set discovery/relay domains to direct. Clash Meta example (illustrative; use your client's syntax): `DOMAIN-SUFFIX,iroh.link,DIRECT`; with fake-ip, add `+.iroh.link` to `fake-ip-filter`. Domain rules only cover client-side resolution — they **cannot** cover all direct peer IPs, relay services and loopback paths, and must not be summarized as "no UDP means it can never work".
|
|
60
|
+
3. `*.iroh.localhost` is the app's local entry point and must not be sent to a remote proxy/DNS.
|
|
61
|
+
4. Always-on/lockdown VPN policies may not allow per-app bypass; this plugin will not bypass system security policy, disable certificate verification, or force network switches.
|
|
62
|
+
|
|
63
|
+
A native "detect VPN and warn" is only an optional follow-up; the current version has no such feature, and whether to run a VPN remains the user's decision.
|
|
64
|
+
|
|
65
|
+
Chinese version: [IROH.md](IROH.md).
|
package/docs/IROH.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# iroh 实验性连接
|
|
2
|
+
|
|
3
|
+
iroh 用加密直连(打不通时走中继)代替公网域名和隧道账号:手机先找到电脑的加密身份,再把流量送回本机网关。你不需要准备域名、端口映射或另一套 HTTPS 隧道。
|
|
4
|
+
|
|
5
|
+
> 插件 0.7.0 和 App 0.7.0 完整版整合电脑端通道、固定 TLS、配对界面和 Android 原生传输。功能默认关闭,仍标记为实验性;能否直连、是否经过中继及连接速度取决于两端网络,不保证在所有校园网、企业网或运营商环境下可用。
|
|
6
|
+
> 相关设计见独立仓的[集成接缝](https://github.com/saya-ch/dsh-mobile-iroh/blob/main/docs/INTEGRATION_SEAM.md)。
|
|
7
|
+
|
|
8
|
+
## 前提
|
|
9
|
+
|
|
10
|
+
- Windows x64 或 Linux x64(sidecar 只发布这两个目标)。
|
|
11
|
+
- Android 10+,安装含 iroh 的完整版 `-iroh.apk`,支持 arm64-v8a 和 x86_64。普通 `.apk` 是无原生库的兼容版,保留原有 HTTPS 连接并供旧版自动更新使用;不支持 iroh,也不支持手机浏览器直接接入。两个 APK 使用相同包名和正式签名,只需安装一个。
|
|
12
|
+
- 点 **安装官方组件** 一次下好 sidecar:插件从固定的 `dsh-mobile-iroh 0.1.0-alpha.1` 发行版按平台下载(Windows x64 约 15 MB,Linux x64 约 24 MB),校验 SHA-256 后放进插件私有目录;已装配套插件且副本校验通过时会直接复用,不再下载。Rust 源码随主仓在 `native/iroh-host`(公共 core 在 `native/iroh-core`)维护,CI 按平台构建校验。
|
|
13
|
+
- DSH Mobile 保持启用;iroh 默认关闭。局域网独立运行,远程通道为单选,切换到 iroh 会停止之前的远程通道。
|
|
14
|
+
|
|
15
|
+
## 使用
|
|
16
|
+
|
|
17
|
+
1. 在 **移动访问** 选择 `iroh(实验性)`。
|
|
18
|
+
2. 点 **安装官方组件**(带下载大小):首次下载并校验 sidecar,通过才算就绪;再次点击会复用已校验的副本。哈希不对、平台不支持都会直接给出原因,不会启动半截通道。注意只是点开通道开关不会触发下载。
|
|
19
|
+
3. 打开远程开关:插件依次解析端点身份、签发端点专属网关证书(自有 CA,90 天叶子、30 天提前续)、起专用网关、拉起 sidecar 进程并等待 `ready`。`ready` 表示 sidecar 已连接且网关在回环监听。
|
|
20
|
+
4. 点 **生成 iroh 配对二维码**,用完整版 App 扫描。描述符绑定 sidecar 的 6 位码、网关的一次性 token 和固定 CA;再次生成会替换旧配对窗口,手机通过 iroh 上的普通 pair 接口核销网关 token。
|
|
21
|
+
5. 关闭或切换通道会停止自有进程与网关,不删除已保存的配对。独立实验 App 的包名不同,其 Keystore 凭据不会自动迁移到本 App。
|
|
22
|
+
|
|
23
|
+
未安装组件时不显示主操作按钮;安装后,开关、生成二维码和复制配对链接三个按钮保持同排。复制按钮在没有有效配对内容时置灰,生成后才可用,重复复制不会重新生成配对码。
|
|
24
|
+
|
|
25
|
+
需要回收组件占用空间时,先关闭 iroh 远程访问,再展开组件详情,选择 **移除已下载的 iroh 组件**。它只删除本插件下载的固定版本程序,不删除身份、配对或外部伴随包;再次使用时可重新安装或复用已校验的外部副本。运行中不允许清理,直接调用管理 API 也会被拒绝。
|
|
26
|
+
|
|
27
|
+
## 安全边界
|
|
28
|
+
|
|
29
|
+
- sidecar 只允许代理到 `127.0.0.1`/`::1` 的回环网关,TLS 仍端到端;命令与事件都是版本化、定长、未知字段直接拒绝。
|
|
30
|
+
- 换指纹/换二进制/挪文件都会让校验失败并停在 `unavailable`,不会静默跑旧通道。
|
|
31
|
+
- 传输层撤销与设备 Token 撤销是两套授权,统一管理界面是 Phase 2 内容;电脑端撤销仍按现有设备管理生效。
|
|
32
|
+
|
|
33
|
+
## 排障
|
|
34
|
+
|
|
35
|
+
iroh 身份和证书必须存放在仅当前用户或受信系统账户可写的目录树。Windows 默认的用户 profile 下 DSH 状态目录符合此用途;自定义状态位置时也要检查父目录权限。某些 AppData 或共享目录允许其他账户写入,会被拒绝;不要通过放宽 ACL、删除真实身份或移入不可信目录来绕过检查。已有身份应先备份并保留。
|
|
36
|
+
|
|
37
|
+
组件发布前必须使用目录中固定的二进制验证首次启用,不能只测本地编译的 host 或手工初始化过的目录。控制器调用 `tls-prepare`,因为 `id` 只读取既有身份。Windows 可设置 `DSH_IROH_PINNED_FIRST_USE=<已校验的程序>` 后运行 `npx vitest run tests/iroh-pinned-first-use.test.ts`,检查私有测试目录中的初始化、复用与损坏拒绝。失败应阻止组件验收,不得通过放松目录 ACL 检查或替换真实身份来让测试通过。
|
|
38
|
+
|
|
39
|
+
| 现象 | 含义 |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `iroh_companion_missing` | 还没点安装(或配套不在常规布局旁);先点安装官方组件,或用 `DSH_MOBILE_IROH_COMPANION` 指向配套绝对路径复用 |
|
|
42
|
+
| `iroh_sidecar_invalid` | 已装文件被改动过;重新点安装即可覆盖修复,配套副本损坏则重装配套 |
|
|
43
|
+
| `iroh_platform_unsupported` | 当前系统/架构没有 sidecar 发布 |
|
|
44
|
+
| `iroh_identity_invalid` / `iroh_identity_failed` | 先检查状态目录及父目录是否私有、已有身份是否损坏,保留原文件再排查;`id` 仅检查已有身份,首次创建使用 `tls-prepare`,不是靠复制或删除身份修复 |
|
|
45
|
+
| `iroh_start_timeout` | 查看 DSH 日志中的 iroh 错误及网络连接,确认固定组件可运行;不要反复重新配对来替代进程排查 |
|
|
46
|
+
| 通道 `ready` 但手机连不上 | 检查 App 是否为完整版、设备是否支持打包的 ABI,以及描述符是否有效;电脑就绪不等于手机已连通,连接中继服务也不等于数据一定经过中继 |
|
|
47
|
+
|
|
48
|
+
## VPN/代理与网络环境
|
|
49
|
+
|
|
50
|
+
iroh 直连优先、中继兜底。直连使用 QUIC/UDP,发现和中继还要求相应网络服务可达。以下环境因素**可能**干扰连接,属于排查方向,并非每一种 VPN 或网络都已确认存在的故障机制:
|
|
51
|
+
|
|
52
|
+
- **全局 VPN/TUN**(系统级虚拟网卡)可能接管 UDP 与 DNS,使直连和中继握手都无法完成。有用户在 Win11 + WSL2 镜像网络、手机移动数据下反馈:关闭全局 VPN 后约 6 秒可用,把 DSH Mobile 加入 VPN 分应用排除后同样恢复。这是社区实测,不代表所有 VPN/网络组合或两种 ABI。
|
|
53
|
+
- **DNS 策略**(fake-IP、过滤规则):域名发现依赖 DNS 解析,fake-IP 模式可能把发现域名解析到代理虚拟 IP。
|
|
54
|
+
- **回环入口**:App 通过 `*.iroh.localhost` 访问本机网关,本机回环地址不应交给远程代理/DNS 处理。
|
|
55
|
+
|
|
56
|
+
可尝试的排查(自愿进行,以本机安全策略为准):
|
|
57
|
+
|
|
58
|
+
1. 在 VPN/代理客户端把 DSH Mobile(包名 `io.github.sayach.dshmobile`)加入分应用绕过/排除列表;调试包与独立实验 App 的包名不同。这指社区 Android App,不是官方 DSH Desktop,请按实际安装的包配置。
|
|
59
|
+
2. 若客户端支持按域名规则,可将发现/中继相关域名设为直连。以 Clash Meta 为例(仅示意,语法以你的客户端为准):`DOMAIN-SUFFIX,iroh.link,DIRECT`;使用 fake-ip 时可在 `fake-ip-filter` 中加入 `+.iroh.link`。域名规则只能覆盖客户端解析相关的部分,**不能**保证覆盖全部直连 peer IP、中继服务与回环路径,也不能概括为“必须有 UDP 否则一定不可用”。
|
|
60
|
+
3. `*.iroh.localhost` 是 App 的本机入口,不应送往远程代理/DNS。
|
|
61
|
+
4. Always-on/锁定类 VPN 策略可能不允许分应用绕过;本插件不会绕过系统安全策略、关闭证书校验或强制切换网络。
|
|
62
|
+
|
|
63
|
+
原生“检测 VPN 并提示”只是可选后续,当前版本不含该功能;是否开关 VPN 由用户自行决定。
|
|
64
|
+
|
|
65
|
+
英文版见 [IROH.en.md](IROH.en.md)。
|
|
@@ -116,6 +116,14 @@ Constrain overlays to their actual container and visible area, rather than using
|
|
|
116
116
|
|
|
117
117
|
Add `env(safe-area-inset-*)` only where content really reaches a screen edge and the host has not already handled it, avoiding duplicate space. Save, Cancel and errors must remain visible or reachable by scrolling with the keyboard open. An independent scroll area should not move the page underneath.
|
|
118
118
|
|
|
119
|
+
### Overlay layering and portals
|
|
120
|
+
|
|
121
|
+
The dedicated mobile shell uses `isolation:isolate` so its drawer and panels share a local stacking context. A positioned menu portalled directly to `document.body` with a positive `z-index` can paint above that shell; its value does not have to exceed the drawer's internal value. The stock-layout adapter retains its existing layer values.
|
|
122
|
+
|
|
123
|
+
This is not a guarantee that every body child outranks every dialog. Negative values, zero/auto paint order, ancestor stacking contexts, and the browser's top layer affect the result. Menus inside the shell remain constrained by their ancestors. Prefer the host's overlay facilities where available, and dismiss a menu when its owning panel closes or a modal takes focus. Do not globally raise third-party widgets or change DSH's dialog layers.
|
|
124
|
+
|
|
125
|
+
Verify actual visibility, focus and `document.elementFromPoint()` while the drawer, detail panel and settings are open, in narrow and wide layouts. The owned `smoke:plugin-portal-layers` fixture checks positive-z-index body menus and a top-layer dialog; it is not acceptance of every community plugin.
|
|
126
|
+
|
|
119
127
|
## Verification: what code checks, browsers and devices establish
|
|
120
128
|
|
|
121
129
|
### Structural checks
|
|
@@ -116,6 +116,14 @@ h('button', { className: 'icon-button', 'aria-label': label }, h(Icon, { 'aria-h
|
|
|
116
116
|
|
|
117
117
|
只有确实延伸到屏幕边缘且未被宿主处理的区域才追加 `env(safe-area-inset-*)`,避免重复留白。键盘打开后,保存、取消和错误提示仍应可见或可滚动到达;独立滚动区域不应拖动背后的页面。
|
|
118
118
|
|
|
119
|
+
### 浮层层级与 portal
|
|
120
|
+
|
|
121
|
+
专用移动布局通过 `isolation:isolate` 将抽屉和面板限制在外壳自身的层叠上下文。直接挂载到 `document.body`、具有定位和正 `z-index` 的菜单可以显示在外壳上方,无需超过抽屉内部的层级值。原生布局适配器保留既有层级值。
|
|
122
|
+
|
|
123
|
+
这不代表任意 body 子元素都能覆盖所有对话框。负值、零值/auto 的绘制顺序、祖先的层叠上下文以及浏览器顶层都会影响结果;外壳内部的菜单仍受祖先约束。优先使用宿主已有的浮层能力,所属面板关闭或模态层获得焦点时收起菜单,不要统一抬高第三方组件或修改 DSH 对话框层级。
|
|
124
|
+
|
|
125
|
+
在窄屏与宽屏下,分别打开抽屉、详情和设置,检查实际可见性、焦点及 `document.elementFromPoint()` 命中。`smoke:plugin-portal-layers` 的受控场景检查正层级 body 菜单和浏览器顶层对话框,不代表所有社区插件均已验收。
|
|
126
|
+
|
|
119
127
|
## 验证:代码检查、浏览器与真机分别证明什么
|
|
120
128
|
|
|
121
129
|
### 结构检查
|
package/docs/README.md
CHANGED
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
| 复用已经运行的 frps,不自动修改服务器 | [接入既有 frps](ATTACH_EXISTING_FRPS.md) | [Existing frps](ATTACH_EXISTING_FRPS.en.md) |
|
|
14
14
|
| 复用 Lucky、Nginx 或 Caddy 的公开 HTTPS 入口 | [自有反向代理](SELF_HOSTED_ORIGIN.md) | [Own reverse proxy](SELF_HOSTED_ORIGIN.en.md) |
|
|
15
15
|
| 由插件安装 Caddy 并管理 HTTPS 证书(0.6.9 起) | [托管 Caddy](CADDY_MANAGED.md) | [Managed Caddy](CADDY_MANAGED.en.md) |
|
|
16
|
+
| iroh 加密直连实验通道 | [iroh 实验通道](IROH.md) | [iroh experimental channel](IROH.en.md) |
|
|
16
17
|
| 页面加载慢、实时连接反复重连或长操作超时 | [慢链路与重连](SLOW_CONNECTIONS.md) | [Slow links and reconnection](SLOW_CONNECTIONS.en.md) |
|
|
17
18
|
| 了解输入栏换行、附加控件与触摸目标 | [移动输入栏](MOBILE_COMPOSER_TOOLBAR.md) | [Mobile composer](MOBILE_COMPOSER_TOOLBAR.en.md) |
|
|
18
19
|
| 保留电脑皮肤,排查移动页面的背景与可读性 | [皮肤与移动布局](SKIN_COMPATIBILITY.md) | [Skins and mobile layout](SKIN_COMPATIBILITY.en.md) |
|
|
19
20
|
|
|
20
|
-
在 Android App 中连接公开隧道或自建入口时请选择 **远程访问**。公开受信任证书也可用手机浏览器配对;既有 frps 的自签入口需要 **0.4.6 或更高版本 App** 固定远程 CA
|
|
21
|
+
在 Android App 中连接公开隧道或自建入口时请选择 **远程访问**。公开受信任证书也可用手机浏览器配对;既有 frps 的自签入口需要 **0.4.6 或更高版本 App** 固定远程 CA。**iroh 则使用完整版 App 0.7.0 的独立配对流程**,不支持兼容版 APK 或手机浏览器。各通道仍要求 DSH Mobile 配对,不能直接暴露普通 DSH 或私有 HTTP 后端。
|
|
21
22
|
|
|
22
23
|
## 页面与网络设置 / Page and network settings
|
|
23
24
|
|
|
@@ -8,7 +8,7 @@ This guide covers the default **external proxy** mode, where you manage public H
|
|
|
8
8
|
|
|
9
9
|
## When to use it
|
|
10
10
|
|
|
11
|
-
Use an existing Lucky, Nginx or Caddy HTTPS proxy rather than a tunnel. Under **Mobile Access →
|
|
11
|
+
Use an existing Lucky, Nginx or Caddy HTTPS proxy rather than a tunnel. Under **Mobile Access → Own reverse proxy**, DSH Mobile creates a separate private HTTP backend with device authentication:
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
14
|
phone → https://phone.example.com:8815 (your proxy terminates TLS)
|
|
@@ -53,6 +53,31 @@ Select **Save and start backend**. An active backend reloads its settings. The p
|
|
|
53
53
|
|
|
54
54
|
For a separate proxy host, configure routing and a firewall rule limited to its actual private source yourself. This provider does not change the firewall.
|
|
55
55
|
|
|
56
|
+
### When you already run cloudflared yourself
|
|
57
|
+
|
|
58
|
+
If you already configured a named tunnel the official way (`cloudflared tunnel`, possibly as a systemd unit, a launchd job or a Windows service), **you do not need the plugin to install a second copy, and you do not need to store a tunnel Token in it**. Use the own-reverse-proxy provider above and point your existing tunnel at the plugin's HTTP backend:
|
|
59
|
+
|
|
60
|
+
1. In **Mobile access → Own reverse proxy**, fill in: the public HTTPS address as your tunnel's public hostname (for example `https://dsh.example.com`), the private listen IP as `127.0.0.1`, an unused backend port (default `3444`), and the allowed source as `127.0.0.0/8`.
|
|
61
|
+
2. Select **Save and start backend** and copy the HTTP backend address the panel shows.
|
|
62
|
+
3. For a locally managed tunnel, update the matching `ingress` entry's `service` while keeping your existing tunnel identity, credentials and other routes. This is an excerpt, not a replacement configuration:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
tunnel: <your tunnel ID>
|
|
66
|
+
ingress:
|
|
67
|
+
- hostname: dsh.example.com
|
|
68
|
+
service: http://127.0.0.1:3444
|
|
69
|
+
- service: http_status:404
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
4. Apply the change using your existing deployment procedure, restarting the service if required. For a dashboard-managed tunnel, edit the public hostname's service in Cloudflare instead of creating a local configuration file. **The plugin never starts, stops, reloads or removes that service, and never reads its configuration or Token.**
|
|
73
|
+
|
|
74
|
+
The split is deliberate: **you own the tunnel, DNS and public certificate; the plugin owns device pairing and DSH authentication.** The backend still enforces device pairing, exact Host/Origin checks and CSRF, so pointing a tunnel at it does not expose a bare DSH to the internet.
|
|
75
|
+
|
|
76
|
+
Two things to watch:
|
|
77
|
+
|
|
78
|
+
- **Avoid the port clash.** The plugin's own cloudflared component also defaults to `3444`. Only one remote provider should run at a time; stop the plugin-managed one before switching, or pick a different port here.
|
|
79
|
+
- **Use the HTTP backend address shown by the panel.** The original DSH Web port (which may differ from `3080`) is not this protected backend. The LAN HTTPS entry (commonly `3443`) has separate trust and pairing and is not the HTTP backend for this mode; do not assume that the panel rejects every incorrect external tunnel target.
|
|
80
|
+
|
|
56
81
|
### Nginx and IPv6
|
|
57
82
|
|
|
58
83
|
The phone's public IPv6 connection to Nginx and Nginx's HTTP connection to DSH Mobile are separate connections. **An IPv6 public entry does not require an IPv6 backend.** On the same computer, Nginx can accept public IPv6 and forward to `http://127.0.0.1:3444`; the source CIDR remains IPv4 loopback. For an explicit `::1` backend, use `http://[::1]:3444` and `::1/128` instead.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
## 适用场景与安全边界
|
|
10
10
|
|
|
11
|
-
你已有 Lucky、Nginx、Caddy 等 HTTPS 反向代理,不需要 Funnel、cpolar 或 FRP 隧道。进入电脑端 **移动访问 →
|
|
11
|
+
你已有 Lucky、Nginx、Caddy 等 HTTPS 反向代理,不需要 Funnel、cpolar 或 FRP 隧道。进入电脑端 **移动访问 → 自有反向代理**,让插件提供一个独立的、带设备配对认证的私有 HTTP 后端:
|
|
12
12
|
|
|
13
13
|
```text
|
|
14
14
|
手机 → https://phone.example.com:8815(你的代理终止 TLS)
|
|
@@ -54,6 +54,31 @@ CIDR 限制针对 TCP 连接的**直接来源地址**,不是手机 IP,也不
|
|
|
54
54
|
|
|
55
55
|
若代理在另一台机器上,还需由管理员保证网络可达,并将主机防火墙入站范围限制到代理的实际私网来源;此功能不自动改防火墙。
|
|
56
56
|
|
|
57
|
+
### 已经自己运行 cloudflared 时
|
|
58
|
+
|
|
59
|
+
如果你已经在用官方方式配置了命名隧道(`cloudflared tunnel`,可能是 systemd、launchd 或 Windows 服务),**不需要让插件再装一份 cloudflared,也不需要在插件里保存隧道 Token**。用上面的自备代理模式,把你已有的隧道指向插件的 HTTP 后端即可:
|
|
60
|
+
|
|
61
|
+
1. 在电脑端 **移动访问 → 自有反向代理** 里填写:公网 HTTPS 地址填你隧道的公开主机名(例如 `https://dsh.example.com`),私有监听 IP 填 `127.0.0.1`,后端端口填一个未占用的值(默认 `3444`),允许来源填 `127.0.0.0/8`。
|
|
62
|
+
2. 点击 **保存并启动后端**,复制面板给出的 HTTP 后端地址。
|
|
63
|
+
3. 本地管理的隧道只需修改对应 `ingress` 的 `service`,保留已有隧道身份、凭据及其他路由。以下仅为片段,不要覆盖整份配置:
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
tunnel: <你的隧道 ID>
|
|
67
|
+
ingress:
|
|
68
|
+
- hostname: dsh.example.com
|
|
69
|
+
service: http://127.0.0.1:3444
|
|
70
|
+
- service: http_status:404
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
4. 按现有部署方式应用变更,必要时重启服务。通过 Cloudflare 控制台管理的隧道,应在那里修改公开主机名对应的服务地址,不必新建本地配置文件。**插件不会启动、停止、重载或删除这个服务**,也不会读取它的配置或 Token。
|
|
74
|
+
|
|
75
|
+
这样分工是清楚的:**隧道、DNS 和公网证书由你管理,设备配对与 DSH 认证由插件管理**。插件的后端仍然执行完整的设备配对、Host/Origin 校验和 CSRF 检查,所以隧道指向它并不会把裸 DSH 暴露到公网。
|
|
76
|
+
|
|
77
|
+
需要注意的两点:
|
|
78
|
+
|
|
79
|
+
- **端口别冲突。** 插件自己的 cloudflared 组件也默认用 `3444`。同一时刻只应有一个远程提供方在运行;改用自备代理前先停掉插件托管的那份,或者在这里换一个端口。
|
|
80
|
+
- **以面板实际展示的 HTTP 后端地址为准。** DSH 原始网页端口可能不是 `3080`,它也不是这里受保护的后端;局域网 HTTPS 入口(常见为 `3443`)有独立的信任和配对,也不是本模式的 HTTP 后端。不要假定面板会拒绝所有错误的外部隧道目标。
|
|
81
|
+
|
|
57
82
|
### Nginx 与 IPv6
|
|
58
83
|
|
|
59
84
|
手机到 Nginx 的公网 IPv6 连接,与 Nginx 到 DSH Mobile 的 HTTP 后端连接是两段独立连接。**公网入口使用 IPv6,不要求后端也使用 IPv6**。同机 Nginx 可以通过 IPv6 接收手机请求,再代理到 `http://127.0.0.1:3444`;这时来源 CIDR 仍是 IPv4 回环。若后端明确监听 `::1`,Nginx 目标改为 `http://[::1]:3444`,来源改为 `::1/128`。
|
|
@@ -86,7 +111,7 @@ location / {
|
|
|
86
111
|
|
|
87
112
|
**“后端已监听”只证明本地 HTTP 监听成功,不代表公网 HTTPS、证书或 WebSocket 已成功。** 自备代理模式的连接诊断也只报告本地状态,不自动向公网地址探测。
|
|
88
113
|
|
|
89
|
-
在电脑端点击
|
|
114
|
+
在电脑端点击 **生成配对二维码**,再在 Android App **0.4.0 或更高版本**的 **远程访问** 流程扫码(不要使用 LAN 扫码流程)。使用受信任证书的自有域名也可在手机浏览器中完成配对;现有自有域名逻辑保留 HTTPS 自定义端口,无需为此功能修改 APK。
|
|
90
115
|
|
|
91
116
|
上线前,必须用手机从目标外网实际检查:
|
|
92
117
|
|
|
@@ -18,6 +18,8 @@ If saving reports a dependency conflict, retain the required module or also dese
|
|
|
18
18
|
- First compare the normal computer layout. Developers can also open `/?frontend=stock` on the authenticated mobile Origin to diagnose boot and layout behavior. Gateway authentication and HTTPS checks remain; this entry skips dedicated mobile boot rewriting and module selection, but installed client adaptation and authenticated customizations may still apply. It is not a plugin-free control; some API operations may be rejected, so it does not replace normal mobile access.
|
|
19
19
|
- When stock has a background but mobile does not, inspect the skin's mounting container and fallback for other layouts. WebSocket approval, APK reinstallation or making every container transparent cannot guarantee a fix.
|
|
20
20
|
- A see-through sidebar, unreadable text shadows and an empty trace need separate diagnosis, not a single missing-background explanation. For an empty trace, inspect the first client exception, actual layout dimensions and whether its module loaded. A static screenshot does not distinguish rendering failure, occlusion and connection failure.
|
|
21
|
+
- **The mobile chrome does not depend on the skin**: the drawer, the right detail panel and the panel return bar composite `--dsw-alias-bg-layer-1` (the return bar uses `--dsw-alias-bg-base`) over the opaque CSS `Canvas` system color. A skin that makes those tokens translucent, or leaves them unresolvable, no longer shows conversation text behind the sidebar labels; a still see-through surface then comes from the skin's own decoration layer, not the chrome base.
|
|
22
|
+
- **Page-level styles injected by a skin travel with the page**: wallpapers and whole-page palettes are injected into the HTML by the plugin on the host, so deselecting that skin's client module does not remove them (module selection covers client modules only). Disable the whole plugin to stop loading them.
|
|
21
23
|
|
|
22
24
|
For example, the published `maid-atelier` 0.1.7 package mounts its background in the stock `centerCol` container. Its repository's fallback for other layouts does not establish that an installed package contains the same correction. DSH Mobile does not imitate that class or move the skin's nodes. A skin supporting mobile can locate its decoration with the existing `[data-pane="conversation"]` and `[data-pane="sidebar"]` markers, or use DSH public theme variables. The skin must still own sizing, stacking, cleanup and light/dark behavior without obscuring text or hit targets. This guide does not establish full skin acceptance. See [#202](https://github.com/saya-ch/dsh-mobile/issues/202).
|
|
23
25
|
|
|
@@ -18,6 +18,8 @@ DSH Mobile 在已认证的移动页面中使用专用根布局,继续渲染 DS
|
|
|
18
18
|
- 先与电脑上的原生布局对照。开发者还可在已认证移动 Origin 打开 `/?frontend=stock` 作启动与布局诊断;它保留 Gateway 认证与 HTTPS 检查,跳过专用移动启动改写和模块选择,但仍可能加载客户端移动适配及已认证定制,并非不含插件的对照页。部分 API 操作可能被拒绝,不能代替正常移动入口。
|
|
19
19
|
- 若原生布局有背景而移动布局没有,检查皮肤背景的挂载容器及其对非原生布局的回退逻辑;单纯放行 WebSocket、重装 APK 或把全部容器设为透明不能保证修复。
|
|
20
20
|
- 侧栏透底、文字阴影难读和轨迹空白应分别排查,不能都归为“少了一张背景图”。轨迹空白时还需检查首个客户端异常、实际布局尺寸和是否加载了相应模块;仅从静态截图不能判断是渲染异常、遮挡还是连接问题。
|
|
21
|
+
- **移动外壳的底板不依赖皮肤**:抽屉、右侧详情和面板返回栏把 `--dsw-alias-bg-layer-1`(返回栏用 `--dsw-alias-bg-base`)叠在不透明的 CSS `Canvas` 系统色上。皮肤把这些 token 设成半透明,或让它们解析不到,会话内容都不会再透到侧栏文字后面;若此时仍透底,来源是皮肤自己的装饰层,而不是外壳底板。
|
|
22
|
+
- **皮肤注入的整页样式会跟着页面走**:壁纸与整页配色由插件在服务端注入 HTML,取消该皮肤的客户端模块不会移除它们(模块选择只覆盖客户端模块)。要彻底不加载,请在插件层禁用。
|
|
21
23
|
|
|
22
24
|
例如 `maid-atelier` 0.1.7 发布包的背景依赖原生 `centerCol` 容器;皮肤仓库源码中的非原生布局回退不代表已安装包包含相同修正。DSH Mobile 不伪造这个类名或移动皮肤节点。支持移动布局的皮肤可以利用现有 `[data-pane="conversation"]`、`[data-pane="sidebar"]` 标记定位自己的装饰,或使用 DSH 公共主题变量;皮肤仍需自行处理尺寸、层叠、关闭清理和明暗主题,装饰不得遮挡文字或点击区域。本指南不表示整套皮肤已完成兼容验收。参见 [#202](https://github.com/saya-ch/dsh-mobile/issues/202)。
|
|
23
25
|
|
|
@@ -108,6 +108,14 @@ These optional fields sit alongside `paths`; their defaults normally suffice:
|
|
|
108
108
|
|
|
109
109
|
Restart DSH and reopen the phone page after a change. Developer tools can show whether the WebSocket handshake negotiated `permessage-deflate`; a client that does not negotiate it can still connect without compression. Compare actual channel transfer volume and CPU use for the same Session, rather than judging savings from the decompressed message lengths shown in developer tools. Set `paths` back to `[]` to disable it. Compression does not raise provider quotas or replace connection troubleshooting.
|
|
110
110
|
|
|
111
|
+
### When it is worth enabling
|
|
112
|
+
|
|
113
|
+
Compression is most useful when large, compressible live messages cross a bandwidth-limited link. In an isolated Gateway/`ws` test with synthetic repetitive text, an 8 KiB message used 8,196 bytes without compression and 249 bytes with it; a 64 KiB sample used 65,546 and 692 bytes. These are WebSocket frame bytes, not TLS/TCP overhead or measurements of a real Session. Savings depend strongly on the content.
|
|
114
|
+
|
|
115
|
+
With the default 1 KiB threshold and no-context-takeover negotiation, the tested 1,023-byte message stayed uncompressed. Incompressible 64 KiB data grew by 22 bytes and added about 1.1 ms to the local round trip. A local bandwidth-shaped test showed lower delivery latency for repetitive messages, but does not predict any particular cellular, relay or LAN result; sustained-load memory and physical-device behavior were not measured.
|
|
116
|
+
|
|
117
|
+
Compare the same workload with and without compression before drawing a conclusion. HTTP history responses and `/api/remote.mux` frames are separate paths: a faster Session switch after several simultaneous upgrades cannot be attributed to WebSocket compression alone. Compression remains opt-in.
|
|
118
|
+
|
|
111
119
|
## Static-resource caching and repeated downloads
|
|
112
120
|
|
|
113
121
|
Owned static resources (`/mobile-access/compat.js`, `mobile-layout.js` and custom CSS/JS) use quoted content ETags and `Cache-Control: private, no-cache`. A browser may store the body but must revalidate before reuse. The gateway checks device authorization and the current resource before processing conditional GET/HEAD requests: unchanged content receives `304`, while a file edit returns the new body. Actual cache reuse still depends on browser behavior.
|
package/docs/SLOW_CONNECTIONS.md
CHANGED
|
@@ -108,6 +108,14 @@ websocketCompression:
|
|
|
108
108
|
|
|
109
109
|
修改后重启 DSH 并重新打开手机页面。可在开发者工具中查看 WebSocket 握手是否协商了 `permessage-deflate`;客户端没有协商时仍可无压缩连接。对比同一会话的实际通道传输量与 CPU 使用,不能仅以开发者工具显示的解压后消息长度判断节省比例。需要关闭时把 `paths` 改回 `[]`;压缩不能提高服务商限额,也不能替代连接故障排查。
|
|
110
110
|
|
|
111
|
+
### 什么时候值得开启
|
|
112
|
+
|
|
113
|
+
可压缩的大消息经过受限带宽时,压缩通常更有价值。在隔离 Gateway/`ws` 测试中,合成的重复文本样本从 8 KiB 对应的 8,196 字节降到 249 字节,从 64 KiB 对应的 65,546 字节降到 692 字节。这是 WebSocket 帧字节,不含 TLS/TCP 开销,也不是实际会话的测量;压缩率取决于具体内容。
|
|
114
|
+
|
|
115
|
+
使用默认 1 KiB 阈值并协商禁用上下文复用时,测试中的 1,023 字节消息没有压缩。不可压缩的 64 KiB 数据反而增加 22 字节,本地往返增加约 1.1 ms。局部限速测试中,重复文本的传递延迟有所下降,但不能据此预测具体蜂窝、转发或局域网体验;本轮未测量持续负载内存和真机行为。
|
|
116
|
+
|
|
117
|
+
请对相同工作负载分别开关压缩再比较。HTTP 历史响应与 `/api/remote.mux` 消息是不同路径,不能将同时升级多个组件后的会话切换改善全部归因于 WebSocket 压缩。该功能仍默认关闭、按需开启。
|
|
118
|
+
|
|
111
119
|
## 静态资源缓存与重复下载
|
|
112
120
|
|
|
113
121
|
网关自有静态资源(`/mobile-access/compat.js`、`mobile-layout.js`、自定义 CSS/JS)使用带引号的内容 ETag 和 `Cache-Control: private, no-cache`。浏览器可保存正文,但每次复用前必须重新校验;网关先检查设备授权和当前资源,再处理 GET/HEAD 条件请求,内容未变时返回 `304`,文件改动后返回新正文。实际缓存复用仍取决于浏览器行为。
|