Back to home@ankhishtar2-lang

dsh-sakurafrp

dsh-mobile satellite: manage the SakuraFrp phone-desktop link from DSH UI (status, gateway toggle, pairing QR, devices, self-heal). Vibe-coded.

Stars
0
Language
JavaScript
Created
Sep 11, 2026
Updated
Sep 11, 2026
GitHub repo

Introduction

dsh-sakurafrp

[!WARNING] 本仓库是 vibe coding 产物。 代码由作者与 AI(DeepSeek Harness 会话)对话生成、 多轮迭代而来,没有经过人工逐行审计,也只在作者本机环境(WSL2 + dsh 0.1.5-rc.1 系列) 实测过。请自行审阅后再使用;不要直接用于生产或安全敏感场景。 代码按 MIT「按原样(AS IS)」提供,不附带任何担保,风险自负。

A vibe-coded satellite plugin for dsh-mobile: manage the phone ↔ desktop link (SakuraFrp TCP tunnel + dsh-mobile gateway) entirely from DSH's own UI — status, gateway on/off, one-time pairing QR, device revoke, self-heal.

把「手机通过 SakuraFrp 隧道访问电脑上的 DSH」这条链路图形化: 原先要跑脚本、改 YAML、盯 systemd 才能完成的事,现在在 DSH 会话头部点一个手机图标就能做。


1. 它做什么

能力说明
链路状态一屏可见网关是否在跑、监听端口、公网入口、四个公网节点的实测延迟、已配对设备数、外部守护服务状态
一键开关手机访问直接控制 dsh-mobile 的移动网关(等价于 POST /api/mobile-access/lan/control
配对二维码申请一次性配对窗口(默认 5 分钟有效),二维码与链接内联返回,手机相机/浏览器扫码即可
设备管理逐台撤销、一键清空;配合 maxDevices 实现「只信任某一台设备」
网关自愈每 15 秒检查移动网关,发现被静默关闭(已知问题)立刻重新拉起
公网可达性探测每 60 秒并发探测主入口 + 三个运营商前缀节点,面板直接显示谁通谁不通
外部守护开关可启停 dsh-mobile-lan-watchdog.service(冗余层,插件自带自愈已能独立兜底)

入口:DSH 会话头部工具区的手机图标按钮order: -15,位于「重启」与「打开文件管理器」之间)。 手机端也能打开这个面板,但只能看状态(原因见第 6 节)。


2. 依赖声明(重要)

2.1 运行期硬依赖:dsh-mobile 插件

本插件不自己实现任何网关,它只是 dsh-mobile 的移动访问网关的图形化管理壳。 没有 dsh-mobile 时:/status 会显示网关无响应,/pair 会报「网关未启动或 dsh-mobile 未启用」。

它依赖 dsh-mobile 提供的这些本机(loopback)管理接口

方法路径用途
GET/POST/api/mobile-access/lan/control网关开关({running: bool}
GET/api/mobile-access/lan/status网关/配对/设备数/资源占用
GET/api/mobile-access/lan/devices设备列表
POST/api/mobile-access/lan/devices/revoke撤销设备({deviceId},32 位小写 hex)
POST/api/mobile-access/lan/devices/reset清空设备
POST/api/mobile-access/lan/pairing/open申请一次性配对窗口,返回 pairUrl + qrSvg

2.2 运行期硬依赖:SakuraFrp 隧道(非 npm)

公网入口由 SakuraFrp 启动器常驻的 frpc 提供。本插件不启动、不配置、也绝不接触访问密钥。 面板预建一条 TCP 隧道,并且:

  • 隧道远程端口必须等于 profile 里 mobile-access.listenPort(插件硬约束:显式 authority 的端口必须等于监听端口);
  • 隧道不要设置「访问密码」——那会让 frpc 接管 TLS 去渲染认证页,破坏端到端自签证书与 Host 校验;
  • Windows 端 frpc 经 WSL2 networkingMode=mirrored 的 localhost 直通,回连 WSL 回环监听。

2.3 运行期软依赖

项目必需用途
dsh-mobile-lan-watchdog.service(本仓库 scripts/dsh-mobile-lan-watchdog.sh网关被静默关闭时的第二层自愈;插件自带 15s 自愈已可独立兜底
systemctl --user外部守护服务的启停

2.4 平台与版本

项目要求
操作系统Linux / WSL2(插件用 systemctl --user、路径按 POSIX 拼)
Node>= 20(用到全局 fetchAbortSignal.timeout
dsh 核心0.1.5-rc.1 系列的 web profile 上验证;其它版本自测
dsh profileweb$DSH_HOME/profiles/web

2.5 dsh-dependencies 声明

除 README 外,package.json 里也带了机器可读的声明:

"peerDependencies": { "dsh-mobile": "*" },
"dshDependencies": {
  "core": ">=0.1.5-rc.1",
  "plugins": [{ "name": "dsh-mobile", "required": true, "provides": "..." }],
  "programs": [{ "name": "SakuraFrp 启动器 / frpc", "required": true, "platform": "Windows" }],
  "services": [{ "name": "dsh-mobile-lan-watchdog.service", "required": false }],
  "platform": ["Linux", "WSL2"], "node": ">=20"
}

3. 安装

3.1 一键安装(推荐)

git clone https://github.com/ankhishtar2-lang/dsh-sakurafrp.git
cd dsh-sakurafrp
bash scripts/install.sh          # 拷贝到 profile + 注册 bundles + 静态自检
systemctl --user restart dsh-web-profiled.service   # 生效(由你手动执行)

scripts/install.sh 只做三件事:把包拷进 $DSH_HOME/profiles/web/node_modules/、 把 dsh-sakurafrp 追加进 profile package.jsondsh.profile.bundles、做静态自检 (host 是否有 apply 导出是硬红线:缺了会让整棵插件树 boot 失败)。它也不会替你重启。

3.2 手动安装

cp -r dsh-sakurafrp "$DSH_HOME/profiles/web/node_modules/"
# 再把 "dsh-sakurafrp" 加进 $DSH_HOME/profiles/web/package.json 的 dsh.profile.bundles
dsh --profile web --dump-config >/dev/null && echo 配置可解析
systemctl --user restart dsh-web-profiled.service

3.3 前置:配置 dsh-mobile 的移动网关

$DSH_HOME/profiles/web/cordis.patch.yml 里加一段(值都要换成你自己的):

- id: mobile-access
  config:
    initiallyEnabled: true
    maxDevices: 1                     # 只信任一台设备就写 1
    pairingTtlMs: 300000              # 配对窗口 5 分钟
    listenHost: 127.0.0.1
    listenPort: 33782                 # 必须等于 SakuraFrp 隧道的远程端口
    publicAuthorities:
      - node.example.com:33782        # 手机访问时用的 Host,必须与隧道入口一致
      - yd.node.example.com:33782     # SakuraFrp 运营商前缀节点(可选)
    allowedCidrs: ['127.0.0.0/8', '::1/128']   # 隧道过来的源地址恒为回环
    upstreamOrigin: http://127.0.0.1:3080
    tls:
      mode: provided
      certFile: !!js dshHomePath('mobile-access/tls/server.crt')
      keyFile: !!js dshHomePath('mobile-access/tls/server.key')
    pairingCaFile: !!js dshHomePath('mobile-access/tls/ca.crt')
    instanceId: '<你的自签 CA 的 SHA-256 指纹,小写去冒号>'   # 必须与 CA 指纹完全一致

⚠️ 两个会让你起不来 dsh 的坑(作者踩过)

  1. pairingCaFile 配了却漏了 instanceId:插件会拿 sha256(stateFile 路径) 当 instanceId, 永远对不上 CA 指纹 → start() 抛错 → loader fail-fast → 整个 dsh web 起不来
  2. maxDevices 在「配对准入」时只数活跃设备,但在启动加载时数全部记录(含已撤销)。 撤销过设备后忘记清理、又把 maxDevices 改小,重启即崩。本仓库的 scripts/dsh-mobile-security-audit.sh 会自动清理已撤销记录。

安装方式补充:从 npm 安装(可选)

本包的 package.json 已按 npm 发布要求准备好(去掉 private、用 files 白名单控制内容)。 发布到 npm 后即可用 dsh 自己的命令安装(dsh plugin add 本质就是 pnpm add):

dsh plugin --profile web add dsh-sakurafrp

自己发布(需要你自己的 npm 账号scripts/publish.sh 不接触也不保存任何 token):

npm login
bash scripts/publish.sh --dry     # 自检 + 列出将要发布的文件,不发布
bash scripts/publish.sh           # 真正发布(改过代码要先升 version,同版本号不可覆盖)

发布后请同步更新 README 与上游收录表的描述,保持「描述属实」这一条成立。

4. 配置(环境变量)

不想改 profile 的话,全部行为都可以用环境变量覆盖(在 systemd unit 或启动脚本里设):

变量默认值说明
DSH_SAKURA_ORIGINhttps://node.example.com:33782(占位符)必填:你自己的公网入口。占位符不改的话探测与配对都会失败
DSH_SAKURA_AUTHORITIES额外探测的公网权威(逗号分隔),主入口自动取自 origin;SakuraFrp 的 yd./dx./lt. 前缀节点写在这里
DSH_SAKURA_SELFHEAL_MS15000网关自愈轮询间隔
DSH_SAKURA_PROBE_MS60000公网节点探测间隔
DSH_SAKURA_LOG$HOME/dsh/memory/dsh-sakurafrp.log事件日志(自动以 0600 创建)
DSH_SAKURA_WATCHDOG_UNITdsh-mobile-lan-watchdog.service外部守护服务单元名
DSH_SAKURA_ALLOW_REMOTE_ADMIN未设设为 1取消「仅电脑端」闸门(见第 6 节,不建议)

📌 与本机实际运行版本的差异:作者本机那份把 DSH_SAKURA_ORIGIN 默认值写成了自己的隧道入口 (省去配置环境变量)。为了不公开个人隧道地址,本仓库把默认值改成了占位符 https://node.example.com:33782,并且默认不再探测 yd./dx./lt. 前缀节点。 因此从本仓库安装后,必须显式设置 DSH_SAKURA_ORIGIN(必要时再设 DSH_SAKURA_AUTHORITIES)。


5. HTTP 路由

全部注册在 dsh web(默认 3080)上,并且只接受回环来源(非回环一律 403):

方法路径仅电脑端说明
GET/dsh-sakurafrp/status否(手机端会脱敏)聚合状态:网关/探测/设备/守护/自愈计数
POST/dsh-sakurafrp/control{running: bool} 开关手机访问
POST/dsh-sakurafrp/pair申请配对窗口,返回 pairUrl + 内联 qrDataUrl
GET/dsh-sakurafrp/qr.svg最近一次二维码 SVG(调试用;前端已改用内联 data URL)
POST/dsh-sakurafrp/revoke{deviceId} 撤销设备
POST/dsh-sakurafrp/devices/reset清空全部配对设备
POST/dsh-sakurafrp/selfheal立刻执行一次「确保网关在跑」
POST/dsh-sakurafrp/probe立刻探测全部公网节点
POST/dsh-sakurafrp/watchdog{active: bool} 启停外部守护服务

6. 安全说明(请务必读)

6.1 为什么有「仅电脑端」闸门

dsh-mobile 的移动网关是一个带特权上游 cookie 的反向代理:它把除 /mobile-access/* 以外的 一切路径转发到 dsh web(3080),并附上它用 DSH 进程内 launch token 换来的认证 cookie。 后果是:任何已配对设备都能触达 3080 上注册的全部插件路由,而来源地址看上去都是 127.0.0.1

作者在安全审查中实测过(用真实配对设备从公网):

请求加固前加固后
POST /dsh-sakurafrp/pair200(手机可自行铸造配对码、再拉设备进来)403 desktop_only
POST /dsh-sakurafrp/watchdog {"active":false}200(守护服务被停 → 自愈失效)403 desktop_only
POST /dsh-whale-tools/restart200(dsh 真的被重启403 desktop_only

闸门怎么工作:网关转发时会用白名单重建请求头(只放行 accept* / content-* / if-* / range / user-agent / origin / sec-fetch-site), 所以任何自定义头经网关都会被剥掉,而电脑端浏览器直连 3080 时原样送达。 本插件的电脑端请求都带 x-dsh-sakurafrp-desktop: 1,服务端对变更类路由要求该头。

  • 逃生舱:DSH_SAKURA_ALLOW_REMOTE_ADMIN=1 会让手机端也能管理(不建议)。
  • 本插件不接触任何密钥:SakuraFrp 访问密钥始终只在 Windows 启动器手里; 前端只处理一次性、短时有效的配对 token。
  • 事件日志以 0600 创建,内容不含密钥与 token。

6.2 安全边界在哪

真正的安全边界是「设备配对」本身:配对成功后,那台设备就拥有完整的 DSH 会话 (能驱动 agent、读写会话)——这是移动访问的功能本意。本插件收紧的是控制面 (铸造配对码、开关网关、撤销设备、停守护、重启 dsh),不是会话面。 手机丢失/借人 ≈ 把 DSH 交出去;要收回就在电脑端面板撤销该设备。


7. 仓库脚本

脚本用途
scripts/install.sh幂等安装(拷贝 + 注册 bundles + 静态自检)
scripts/uninstall.sh卸载(移除 bundles + 删目录)
scripts/dsh-mobile-lan-watchdog.sh移动网关守护:发现 running:false 就拉起(systemd user 服务用)
scripts/dsh-mobile-security-audit.sh可复跑的安全审查:匿名面 / 文件权限 / 「已配对设备越权」实测 / 自动清理设备记录(刻意非破坏性:不碰 watchdog 与 restart)
scripts/legacy/dsh-mobile-pair.sh早期命令行配对脚本,已被插件面板取代,留作备用

审计脚本用法:

bash scripts/dsh-mobile-security-audit.sh [公网origin]
# 关键项全过 → 退出码 0;发现越权/暴露 → 1

8. 已验证 / 已知限制

已在作者本机验证

  • 4 个公网节点探测全部 200 {"ok":true},延迟 161–235 ms(真实隧道,自签证书校验关闭);
  • 真实配对 → 带会话拉取手机端首页 200;手机端所有控制面请求 403;
  • host 半桩测试 37 项 + 浏览器半 bundle 复核;dsh --profile web --dump-config 退出码 0;
  • 网关被静默关闭后 15 秒内自愈(多次实测)。

已知限制

  • 只做「管理壳」,不提供隧道:隧道断了要去看 SakuraFrp 启动器/面板;
  • 依赖 dsh-mobile 的内部管理接口,上游改路径就会失效(接口非公开契约);
  • 公网探测会周期性向公网节点发 HTTPS 请求(每 60 秒 4 条,可 DSH_SAKURA_PROBE_MS 调大或关掉插件);
  • 面板为原生 DOM + 内联样式,深色/浅色主题都做了变量兜底,但主题差异大的皮肤下观感可能一般;
  • 手机端只能读状态,这是设计而非缺陷。

9. 卸载

bash scripts/uninstall.sh
systemctl --user restart dsh-web-profiled.service

卸载本插件不会影响:SakuraFrp 隧道、mobile-access 配置与自签证书、设备状态文件、 外部守护服务。这些要单独处理(见第 3.3 节的配置段)。


10. 目录结构

dsh-sakurafrp/
├── package.json          # dsh.bundle / dsh.client / peerDependencies / dshDependencies
├── cordis.patch.yml      # loader 条目:id=sakurafrp, name=dsh-sakurafrp
├── lib/
│   ├── index.js          # host 半:9 条路由 + 网关自愈 + 公网探测 + 桌面闸门
│   └── client.js         # 浏览器半:头部手机按钮 + 管理面板 + 每秒倒计时
├── scripts/              # 安装/卸载/守护/安全审计/早期配对脚本
├── LICENSE               # MIT
└── README.md

11. 许可

MIT © 2026 ankhishtar2-lang —— 见 LICENSE。 再次提醒:vibe coding 产物,未经人工逐行审计,按「原样」提供,风险自负。