dsh-lan-gate
LAN gateway for DeepSeek Harness. Use your desktop DSH from a phone or iPad over Wi-Fi, with per-device approval and a mobile layout. Nothing to install.
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 3, 2026
- Updated
- Oct 5, 2026
Introduction
dsh-lan-gate
English | 中文
电脑上的 DeepSeek Harness,手机和 iPad 也能用。 同一个 WiFi 下打开一个网址就能接着聊,不用装客户端,DSH 仍然只监听本机、不对外暴露。

上排是加到主屏幕之后的图标,下排是手机上、iPad 上实际用起来的样子。
⚠️ 只在你信得过的局域网里用。 它默认监听
0.0.0.0,走明文 HTTP 没有 TLS,不用的时间记得.\lan-gate.ps1 stop。完整的安全边界和防不住的几件事见后面那节。
手机 / iPad ──▶ dsh-lan-gate(本机 Node,0.0.0.0:3089)──▶ DSH Web(127.0.0.1:19387,只认本机)
↑
未批准设备只看到「等待批准」页;
批准动作只能在这台电脑上点。
DSH 本体仍然只监听 127.0.0.1,从不暴露到局域网。网关是唯一入口。
目录
- 它解决什么问题
- 快速开始
- 手机 / iPad 怎么连
- 门禁:新设备批准
- 在 DSH 里一键批准
- 手机端排版
- 加到主屏幕(PWA)
- 调试开关
- 配置项
- 文件结构
- 数据落盘位置
- 测试
- 换 App 图标
- 排障
- 安全边界
- 已知限制
它解决什么问题
官方 CLI 有意禁止 --host 0.0.0.0——因为 DSH Web 的 /api 没有独立的认证层,直接暴露等于把控制权交给同网段任何人。
而 DSH Web 本身带一套真实鉴权(启动令牌 + Host 绑定的签名 Cookie),这套机制恰好允许一个反向代理在本机替远端客户端完成认证。本网关做的就是这件事,并在其上叠加一层设备审批门禁。
为什么不用现成的第三方插件:调研过 dsh-mobile-gate、@linxin666/dsh-remote-web-ui、dsh-auth-gate 等(记录在 Mnemon 文档《dsh-lan-gate 总档》)。主要顾虑是 desktop profile 兼容性与供应链风险,且自己实现只需要一个零依赖的 Node 脚本。
快速开始
需要 Node.js 18+(用到 crypto.randomUUID、fetch 等内置能力;实测在 Node 24 上运行)。零第三方依赖。
# 在 dsh-lan-gate 目录下
.\lan-gate.ps1 start # 启动(后台)
.\lan-gate.ps1 status # 看状态(默认动作)
.\lan-gate.ps1 stop # 停止
.\lan-gate.ps1 restart # 重启
.\lan-gate.ps1 log # 看最近 30 行日志
也可以双击 lan-gate.cmd(内部已带 -ExecutionPolicy Bypass,不受脚本执行策略影响)。
启动后 status 会打印:
状态: 运行中 PID 12345 启动于 ...
监听: 0.0.0.0:3089
访问地址:
电脑本机 : http://127.0.0.1:3089
手机/iPad: http://192.168.x.x:3089
管理台 : http://127.0.0.1:3089/__langate/admin
已批准设备 2 台 待批准 0 台
[已批准] Android · Chrome @ 192.168.1.42
[已批准] Mac · Safari @ 192.168.1.77
⚠️
lan-gate.ps1必须存成 UTF-8 带 BOM。 Windows PowerShell 5.1 默认按 GBK 读取.ps1,无 BOM 时中文会被读成乱码,产生未闭合字符串并导致脚本卡死(不报错、不输出)。改这个文件后如果卡住,先查 BOM。
开机自启与网络守卫
网关是普通 Node 进程,默认不会自动启动。当前配置为:登录 Windows 后延迟 30 秒自动启动(等 DSH 先把 19387 监听起来),之后每 5 分钟检查一次,进程不在了就补起来,并且只在指定网段才启动。
网络守卫(重要)
本机防火墙三个 profile 全是关的(作者机器实测)。网关一旦监听 0.0.0.0:3089,在任意网络下端口都对同网设备开放。配合开机自启,就会出现「带着笔记本去咖啡厅,网关照样裸挂在公共网络上」。
所以网关在 listen 之前会检查本机 IP:
网络守卫通过: 本机 192.168.1.23 命中允许网段 [192.168.1.]
网络守卫拦截: 本机 IP [10.0.2.15] 都不在允许网段 [192.168.1.]
这是为了防止在不可信网络下暴露 3089 端口。确实要启动就清掉环境变量 LAN_GATE_ALLOW_NET。
拦截时端口根本不会打开(不是"先监听再关闭"——那会留下一个能被扫到的窗口),进程立即退出。
- 默认允许网段写在
lan-gate.ps1顶部(默认192.168..,覆盖绝大多数家用路由器) - 想更严就写死成你自己那一段(例如
192.168.1.),这样手机热点等同样是192.168.x的网络也会被挡 - 可写多段:
$env:LAN_GATE_ALLOW_NET = '192.168.1.,10.0.0.' - 临时放行(本次运行有效):
$env:LAN_GATE_ALLOW_NET='off'; .\lan-gate.ps1 start
自启是怎么配的
一个叫 dsh-lan-gate 的计划任务:
| 项 | 值 |
|---|---|
| 触发 | ① 登录时(当前用户),延迟 30 秒;② 每 5 分钟重复一次(自愈检查) |
| 动作 | wscript.exe lan-gate-hidden.vbs |
| 效果 | 用 WScript 拉起 lan-gate.ps1 start,窗口完全隐藏(否则每次登录会闪一下黑窗口) |
| 幂等 | start 检测到进程已在就立刻返回,所以常态下每 5 分钟只是空跑一次 |
| 退出码 | 0(正常);被守卫拦截时也是 0,原因写在 run.log |
为什么需要每 5 分钟那一趟。 网关可能被硬杀——不是崩溃,也不是优雅退出。判据很明确:lan-gate.ps1 的 SIGINT 处理函数会打印「退出。运行 Ns …」,run.err.log 为空就说明两者都没发生,进程是被 TerminateProcess 直接干掉的。2026-10-04 就这么无声无息没了 22 小时,期间手机上任何设备申请都到不了电脑。
最容易踩的一条:从 DSH 的命令里起网关,它会跟着那条命令的进程树一起死。 DSH 命令里 spawn 的进程属于该命令的进程树,命令结束或被清理时整棵树被硬杀(自己的信号处理函数都不会跑)。所以网关一律交给计划任务起,plugin/ 里的「一键启动」也是走 schtasks /run,不是自己 spawn node。
带重复触发的任务由脚本注册,可重跑:
# 重新注册(会先把现有定义导出到 tools/autostart-task-backup-<时间戳>.xml)
.\tools\install-autostart.ps1
.\tools\install-autostart.ps1 -EveryMinutes 3 # 想更灵敏
日常操作:
# 查看
Get-ScheduledTask -TaskName 'dsh-lan-gate' | Select-Object TaskName, State
Get-ScheduledTaskInfo -TaskName 'dsh-lan-gate' # 看上次运行结果
# 手动触发一次(不重启也能验证)
Start-ScheduledTask -TaskName 'dsh-lan-gate'
# 停用 / 恢复
Disable-ScheduledTask -TaskName 'dsh-lan-gate'
Enable-ScheduledTask -TaskName 'dsh-lan-gate'
# 彻底删除(回到纯手动)
Unregister-ScheduledTask -TaskName 'dsh-lan-gate' -Confirm:$false
自启的前置条件:网关只是代理,DSH 桌面端自己也得在跑(监听 19387)。如果 DSH 没起,手机访问会看到 502——不是崩溃,DSH 起来后自动恢复。
手机 / iPad 怎么连
- 确认同一 WiFi:手机/iPad 与电脑连同一个网络,不要用蜂窝流量。
- 打开浏览器:
- 安卓:Chrome
- iPad/iPhone:必须用 Safari(Chrome 在 iOS 上无法「添加到主屏幕」)
- 访问
http://<电脑局域网IP>:3089(IP 见status输出) - 首次会看到「等待电脑批准」页 → 见下一节
- 批准后自动进入 DSH,此后该设备免批准
门禁:新设备批准
新设备首次访问碰不到 DSH,只会拿到一张等待页。批准动作只能在电脑本机完成:
- 电脑浏览器打开
http://127.0.0.1:3089/__langate/admin - 在「待批准」里找到那台设备(显示设备名 + IP)
- 点「允许」
- 手机页 1~2 秒内自动跳入 DSH
管理台还支持「拒绝」「撤销」「撤销全部已批准设备」。撤销过的设备需要重新批准。
第 1 步那串地址不用手敲 —— 装了配套插件之后,DSH 左栏底部就有一个按钮直接打开它。见下一节。
三条防线
| 防线 | 实现 |
|---|---|
| 未批准设备碰不到 DSH | 网关前置拦截,只返回等待页;DSH 本体不暴露 |
| 批准动作只能在电脑上做 | 管理页与批准 API 只接受回环地址,手机猜到网址也被 403 |
| 令牌不可复用 | 设备令牌用独立随机密钥签 HMAC,绑定设备 ID 与有效期,可随时撤销 |
另有每 IP 每分钟 600 次限流。
本机自己不算「设备」:网关把「回环地址 + 本机各网卡地址」都视为自己人,直接直通、不登记。 否则从本机用局域网 IP 访问自己时(来源 IP 不是
127.0.0.1),门禁会把它当成一台新设备登记进状态文件—— 端到端测试的场景 3 正好就是这么访问的,每跑一次就留下一台假设备。这个问题已在网关层根治。
在 DSH 里一键批准
plugin/ 是一个配套的 DSH 插件,在左栏底部、设置图标旁边加一个「手机接入」按钮:
| 按钮显示 | 含义 | 点下去 |
|---|---|---|
| ● 手机接入 | 网关在跑,没人在等 | 系统默认浏览器打开批准页 |
| ● 待批准 2 | 有两台在等批准 | 同上 |
| ○ 启动手机网关 | 网关没在跑 | 先拉起网关,就绪后打开批准页 |
状态点绿色 = 正常,琥珀色 = 有人等,灰色 = 没在跑。侧栏折叠成 56px 轨道时只留状态点。
它在手机上不显示,这是故意的。手机/iPad 看到的界面是网关代理过去的,在那边点这个按钮只会拿到 403(批准页只认回环地址)。判据是 location.protocol === 'dsh-app:' 或回环主机名,只在电脑上渲染。
点一下怎么就能打开浏览器:桌面端主窗口装了 setWindowOpenHandler,把 http/https 一律 shell.openExternal 出去,也就是系统默认浏览器。插件只要调 window.open,不需要 IPC,也不用改 Electron 配置。
装法、配置项和实现细节见 plugin/README.md。
手机端排版
窄屏下网关会注入一套排版修正(DSH 桌面端不受影响,因为桌面 App 根本不经过网关)。
做了什么:
| 项 | 处理 |
|---|---|
| 侧栏 | 改成覆盖式抽屉,默认收起;左上角 ☰ 开合 |
| 主内容 | 占满屏宽(宿主用 CSS Grid 把侧栏宽度写死在轨道里,必须改轨道而非元素宽度) |
| 会话题头 / 对话·轨迹 tab | 隐藏 |
| QQ2005 皮肤浮层与工具条 | 隐藏(否则遮挡对话区) |
| 上下文百分比圆环 | 隐藏 |
| 触摸目标 | 按钮最小 40px;输入区贴底并适配安全区 |
| 根容器 | 禁止横向溢出 |
手机 vs 平板(重要差异):
-
手机:规则包在
@media (max-width: 860px)里。 -
iPad:iPadOS 的 Safari 把自己伪装成 Mac(UA 里没有
iPad),服务端认不出来;而且 iPad 竖屏 1024px、横屏 1366px 都超过 860,靠断点永远命中不了。所以网关同时注入第二套「无断点」样式(默认
media="not all"关闭),由客户端脚本用「触点数 > 1 且 UA 像 Mac」识别 iPad(Mac 笔记本触点为 0)后打开。结果:iPad 竖屏横屏都走手机排版。
加到主屏幕(PWA)
局域网 http:// 下浏览器不会自动弹安装横幅(那需要 HTTPS),但「添加到主屏幕 / 图标 / 全屏无地址栏」都能做到。
iPad / iPhone(Safari):分享按钮 → 往下滑 → 「添加到主屏幕」→ 添加
安卓(Chrome):右上角 ⋮ → 「添加到主屏幕」/「安装应用」
⚠️ 换过图标后必须删掉旧图标再加一次。 iOS 会把桌面图标按网址缓存,连「这个网站没有图标」这个失败结论也一起缓存。
实现要点:宿主 HTML 里已经有一个 manifest 链接且排在最前面,浏览器只认第一个,所以网关再注入一个会被完全忽略。正解是在网关层接管宿主那个路径(/manifest.webmanifest),返回我们自己的清单。
调试开关
都是 URL 查询参数,只对当前这次导航有效,刷新需重新附加。
| 参数 | 作用 |
|---|---|
?mobile=0 | 完全不注入排版样式(手机/iPad 上出现异常时的紧急回退) |
?mobile=1 | 强制注入(用于在桌面浏览器上对比手机排版效果) |
?dev=1 | 页面加载 1.8 秒后自动上报真实 DOM 结构,底部弹绿/红提示条告知成败 |
?hide=<选择器>[,<选择器2>] | 临时把匹配元素设为 display:none,并告知每个选择器命中几个元素 |
?hide= 的用途:现场试出「是哪个元素在挡视线」,确认后固化进 lib/mobile.mjs。
配置项
全部通过环境变量,均有默认值:
| 变量 | 默认 | 说明 |
|---|---|---|
LAN_GATE_PORT | 3089 | 网关监听端口 |
LAN_GATE_HOST | 0.0.0.0 | 网关监听地址 |
LAN_GATE_UPSTREAM_HOST | 127.0.0.1 | 上游 DSH 地址 |
LAN_GATE_UPSTREAM_PORT | 19387 | 上游 DSH 端口 |
LAN_GATE_RATE_LIMIT | 600 | 每 IP 每分钟请求上限 |
LAN_GATE_ALLOW_NET | 由 lan-gate.ps1 设为 192.168. | 网络守卫:本机 IP 命中这些前缀才允许启动(逗号分隔)。留空 = 不限制;off = 显式关闭守卫 |
上游地址会被固定改写成
host:port形式发给 DSH。这是方案核心:DSH 用sha256(Host)计算会话 Cookie 名,固定 Host 后鉴权才闭环。
LAN_GATE_ALLOW_NET由lan-gate.ps1在启动 node 前注入;直接node lan-gate.mjs时它默认是空的(不限制)。
文件结构
dsh-lan-gate/
├── lan-gate.mjs 入口:代理、Host/Origin 改写、门禁拦截、页面注入
├── lan-gate.ps1 启停脚本(start/stop/restart/status/log)+ 网络守卫默认值
├── lan-gate.cmd 双击入口(绕过脚本执行策略)
├── lan-gate-hidden.vbs 计划任务用的隐藏窗口启动器(ASCII-only)
├── README.md 本文件(中文)
├── README.en.md English version
├── LICENSE MIT
├── NOTICE.md 说明与免责(安全须知、依赖与素材、商标)
├── .gitignore 排除运行日志与本地状态
├── plugin/ 配套 DSH 插件:左栏底部的「手机接入」按钮
│ ├── package.json `dsh.bundle.patch` + `dsh.client` 声明(无构建步骤)
│ ├── cordis.patch.yml 挂载声明;端口与计划任务名在这里配
│ ├── index.js 宿主半区:同源转发 summary、经 schtasks 拉起网关
│ ├── client.js 客户端半区:注册进 sidebar.footer.action
│ └── README.md 插件的装法与实现细节
├── lib/
│ ├── auth.mjs 两套令牌:DSH 会话 Cookie 自签 + 本网关设备令牌
│ ├── store.mjs 状态持久化(原子写)+ 每 IP 限流
│ ├── pages.mjs 网关自有页面(等待批准页 / 管理台 / 限流页)
│ ├── mobile.mjs 手机与平板排版注入 + 诊断上报
│ └── pwa.mjs manifest、图标路由、Apple 全屏 meta
├── assets/ App 图标(由 tools/make-icons.py 生成)
├── docs/ README 用的示例图(由 tools/make-example.py 拼)
│ ├── icon-180.png iOS apple-touch-icon
│ ├── icon-192.png
│ ├── icon-512.png
│ ├── icon-maskable-512.png
│ └── icon-source.png 裁掉留白后的母图(备查)
├── test/ 自动化测试
│ ├── run-all.mjs 一次跑全部并汇总
│ ├── test-gate.mjs 门禁:审批状态机 + 管理接口
│ ├── test-mobile.mjs 排版注入(含桌面不受影响的断言)
│ ├── test-pwa.mjs manifest / 图标 / 注入标签
│ ├── test-mobile-flow.mjs 端到端:模拟手机走完整路径
│ ├── test-ws.mjs WebSocket 升级穿透
│ └── test-routes.mjs 真实 API 路径 + Origin 栅栏
└── tools/ 开发与运维辅助(运行时不需要)
├── make-icons.py 从一张母图生成全套 App 图标
├── make-example.py 把几张截图拼成 README 用的 2x2 示例图
├── check-templates.mjs 防再犯:检查模板字符串里的裸反引号
├── verify-inject.mjs 断言注入结构(断点位置、花括号配平)
├── verify-ua.mjs 各 UA 下的注入行为对照
├── probe-lan.mjs 走局域网 IP 探查(会在状态里留一台待批准设备)
├── probe-ipad-icon.mjs iPad 实际拿到的图标标签与资源
├── recon-layout.mjs 量宿主布局(盒模型链路)
├── exp-cookie.mjs 自签 Cookie 可行性实验(历史取证)
└── install-autostart.ps1 注册/重注册带 5 分钟自愈的计划任务(可重跑)
数据落盘位置
都在 %USERPROFILE%\.dsh\ 下:
| 文件 | 内容 |
|---|---|
lan-gate-state.json | 已批准 / 待批准设备清单、限流桶 |
lan-gate-secret | 本网关的设备令牌签名密钥(首次运行自动生成,权限 600) |
lan-gate-domreport.json | ?dev=1 时手机上报的真实 DOM 结构(诊断用,覆盖写) |
日志在项目目录下:run.log、run.err.log。
DSH 自身的会话 Cookie 签名密钥从 ~/.dsh/.credentials.yaml 读取(client-connection/browser-session.secret)——网关照 DSH 自己的算法自签会话 Cookie,因此不需要重启 DSH、也不需要用户粘贴启动令牌。
测试
node test\run-all.mjs
依次运行全部套件并打印汇总表。每个套件都有真实断言和退出码(不是只打印结果):
| 套件 | 断言数 | 覆盖什么 |
|---|---|---|
test-gate.mjs | 7~11 | 管理台、批准/撤销状态机落盘、限流、轮询接口 |
test-mobile.mjs | 19 | 手机/平板注入、桌面不受影响、?mobile=0/1 回退 |
test-pwa.mjs | 45 | manifest 字段、4 张图标(PNG 魔数+尺寸+长度)、注入标签、宿主路径接管 |
test-mobile-flow.mjs | 7 | 端到端四场景(首访/带 Cookie/局域网 IP/静态资源) |
test-ws.mjs | 3 | WebSocket 握手穿透 + 非端点不被误接 |
test-routes.mjs | 4 | 真实 API 返回 JSON、404 而非 401、Origin 改写生效 |
需要走真实局域网路径的那条用例(「从局域网 IP 访问」)要你提供本机 IP,否则会自动跳过:
$env:DSH_LAN_IP='192.168.1.23'; node test\run-all.mjs
代码里不写死任何个人 IP —— 写死了别人 clone 下来就跑不了,对自己也没好处。 配置集中在
test/config.mjs。
另有结构性验证(在 tools/ 下,不常跑):
node tools\verify-inject.mjs—— 断言注入的 CSS 花括号配平、每条关键规则都落在@media内node tools\verify-ua.mjs—— 9 项:各 UA(iPad 伪装 Mac / 手机 / 桌面 / Mac 笔记本)下的注入行为对照node tools\check-templates.mjs <文件>—— 防再犯守卫:检查模板字符串里的裸反引号(见排障)
门禁测试只操作它自己造的设备,绝不碰真实设备。 早期版本用过「设备列表里第一个」,会对你真实的手机做「撤销→重新批准」,中途出错就把设备锁在门外。这个坑已修:脚本只认 id 以
testdevice开头的条目,没有就跳过并提示。
测试脚本自身的坑:
test-ws.mjs早期漏了 socket 的close事件处理——上游直接断开而不发数据时 Promise 永不 settle,事件循环变空,Node 静默 exit 0,后面的路径根本没测(表现为「只打印第一行就结束」)。任何用裸 socket 写测试的地方都要记住处理close。
换 App 图标
tools/make-icons.py 从一张母图生成全套图标:
python tools\make-icons.py <母图路径>
脚本会:自动裁掉母图四周留白 → 按比例缩放居中 → 输出 180 / 192 / 512 / maskable-512 四张。背景取母图四角采样色(必须不透明,iOS 对带透明通道的 apple-touch-icon 合成行为不确定)。
换完图标后必须做一件事:把 lib/pwa.mjs 里的 ICON_VERSION 加一。
export const ICON_VERSION = '3'; // 换成 '4'
原因:iOS 会缓存「本网站没有可用图标」这个结论。早期版本这里指向 SVG(iOS 不支持 SVG 作桌面图标),那次失败被缓存住,之后换成 PNG 只要 URL 不变,iOS 仍可能沿用旧结果。加版本号可强制重新抓取。
排障
「改了配置但行为没变」
先确认进程真的重启了、再怀疑代码。 网关崩溃时旧进程可能还活着并继续服务,表现为「改了半天毫无效果」。
排查顺序:
node --check <改过的文件>—— 语法错误会让新版根本起不来.\lan-gate.ps1 restart后看status的 PID 有没有变- 看
run.err.log有没有报错 - 还不对再怀疑浏览器缓存
特别提醒:lib/mobile.mjs 里的 CSS / JS 是模板字符串。在里面的注释里写裸反引号会提前截断字符串 → 语法错误 → 网关启动即崩。这个错犯过两次,所以有 tools/check-templates.mjs 守着,改完先跑它。
「手机浏览器还是旧页面」
HTML 响应已带强禁缓存头(no-store, no-cache, must-revalidate, max-age=0 + pragma + expires)。仍然怀疑缓存时,加个时间戳参数开新 URL:
http://<IP>:3089/?t=12345
「iPad 上还是桌面排版」
- 确认已在电脑上批准该 iPad
- 检查页面里有没有
langate-tablet-css(用node tools\probe-ipad-icon.mjs) - iPad 识别依赖「触点数 > 1 且 UA 像 Mac」。若 UA 是别的形态,需要扩充
lib/mobile.mjs里的detectTablet()
「手机连不上」
- 确认同一 WiFi、没用蜂窝
.\lan-gate.ps1 status看监听是否正常- Windows 防火墙:作者机器实测三个 profile 全关,无需加规则;若你开了防火墙,需要放行 3089 入站
探针脚本污染了设备列表
tools/probe-lan.mjs 等从本机用局域网 IP 访问时,来源 IP 不是回环,会被当成一台新设备登记。清理:
# 手工编辑 %USERPROFILE%\.dsh\lan-gate-state.json,删掉 ip 为本机局域网 IP 的条目
probe-ipad-icon.mjs 已改为默认走回环,不会再污染。
安全边界
能防住的:
- 同 WiFi 的陌生设备:看不到 DSH,只能看到等待页
- 猜到管理台网址的手机:回环校验直接 403
- 令牌伪造:设备令牌用独立密钥签 HMAC,密钥不在设备上
- 暴力尝试:每 IP 每分钟 600 次限流
- 设备丢失:可在管理台撤销,该设备立即失效
防不住的(重要):
- 明文 HTTP。Cookie 与内容都不过 TLS,同一 WiFi 下的抓包者能看到流量。只应在可信局域网使用。
- 手机被解锁后被人拿到。设备令牌存在浏览器里,解锁即等于放行。这是「不做指纹」的既定取舍。
- 网关进程持有 DSH 会话的签名能力(它要替远端客户端签 Cookie)。也就是说,能读
~/.dsh/.credentials.yaml的本机进程,本来就能直接进 DSH——网关没有扩大这个信任边界,但也没有缩小它。 - 本机三个防火墙 profile 全关(作者机器实测)。这意味着本机其他开放端口同样对局域网可见。网关只负责自己那一个端口。 → 缓解措施:网络守卫。它让网关只在指定网段启动,避免带着笔记本连公共网络时端口照常敞开。
已知限制
- 不含 HTTPS。要指纹/面容认证必须先有安全上下文——局域网 HTTP 下 WebAuthn 被浏览器规范禁用。原生壳(Tauri/Capacitor)可绕过,但需重打包,且 iOS 侧载受签名限制。当前选择:不加指纹,用「电脑上亲手批准」代替。
- Android APK 未做。评估结论:工具链全缺(无 JDK/SDK/Rust)、C 盘空间紧张;若做应走原生 WebView 壳(约 1 MB),唯一实质收益是原生指纹。暂缓。
- iPad 识别是启发式的(触点数 + Mac UA)。若未来 iPadOS 改变 UA 形态,需要更新
detectTablet()。 - 静态资源未发 ETag / Last-Modified。HTML 用强禁缓存兜住了,但静态资源的缓存行为依赖上游。
- 宿主类名是哈希的,排版规则依赖运行时实测出的类名(
BynINW_*、_2H3hWW_*、dsh-skin-qq2005-*)。DSH 升级后如果改名,排版会失效——重新用?dev=1上报 DOM 即可定位。
相关文档
完整的设计取证、踩坑记录与维护者结论见 Mnemon 项目文档:
《dsh-lan-gate 总档:手机局域网操控 DSH 的网关(鉴权取证/门禁/手机排版/PWA/CDP 调试)》
包含:DSH Web 鉴权机制的宿主源码取证(asar 行号)、Cookie 精确格式、/api 的 Origin 栅栏、真实路由命名、adb→Chrome CDP 真机调试通道、以及全部踩坑的根因分析。
许可与免责
MIT,见 LICENSE。
使用前请读 NOTICE.md,里面有必须知道的安全前提:必须与 DSH 同机运行、明文 HTTP 无加密、会读取本机 DSH 凭据文件、不要暴露到公网、设备令牌存在浏览器里。
本项目是非官方社区工具,与 DeepSeek 官方无关联。