在手机上安全驱动桌面智能体,审批工具调用
30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add huanghantao/dsh-gateway该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:需留意
是什么
dsh 原生插件 · security
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 29 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
⚠︎ 实装验证未通过(unknown · 2026/10/3) ——可能是验证环境差异,装前建议到 GitHub 仓库确认最近更新与 issue。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 8 处,展示前 8 条)
- 发起网络请求internal/dshplugin/answerer.mjs:107
response = await fetch(endpoint.url, { - 读取本地文件internal/dshplugin/answerer.mjs:189
const parsed = JSON.parse(await readFile(path, "utf8")); - 发起网络请求web/public/sw.js:65
return await fetch(request); - 发起网络请求web/public/sw.js:80
const network = fetch(request) - 发起网络请求web/src/api.ts:190
response = await fetch(withQuery(`${API_BASE}${path}`, options.query), { - 发起网络请求web/src/api.ts:281
const response = await fetch(path, { method: "GET", credentials: "same-origin", headers: { Accept: "application/json" } }); - 发起网络请求web/src/api.ts:642
const response = await fetch(`${API_BASE}/readyz`, { method: "GET", credentials: "same-origin" }); - 写入剪贴板web/src/clipboard.ts:31
await clipboard.writeText(text);
扫描于 2026/10/31(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
功能亮点
- 手机远程操控桌面智能体
- 自托管部署
- 操作需人工审批
在手机上通过公网远程操控桌面上的 DeepSeek Harness 智能体,自托管,任何操作都不会自动批准。
README
由 DeepSeek 最新模型翻译生成dsh-gateway
在手机上通过公网,驱动跑在你桌面上的 DeepSeek Harness 智能体。
你的桌面智能体能读你的文件、跑命令。这个网关是你唯一暴露到公网的东西,它的设计目标就是:面对「万一有人发现了这个 URL 会怎样?」这个问题,答案是「什么都不会发生」。
PHONE ──HTTPS──▶ VPS: Caddy (TLS) ──▶ frps ─┐
│ encrypted tunnel
HOME MAC: frpc ◀───────────────────────────┘
│
▼ 127.0.0.1 only
dsh-gateway ──▶ /m/ mobile PWA
──▶ /api/v1/* versioned API (REST + WebSocket)
──▶ ACP stdio ──▶ dsh --profile acp
──▶ read-only ──▶ ~/.dsh/sessions/.jsonl.zstd
为什么是这个样子
DeepSeek Harness 的三个特性决定了这套设计,每一个都是靠读代码、探真实安装环境确认的,不是拍脑袋假设的:
1. dsh web 只绑回环地址,自身不带 TLS,也没有认证。 所以网关必须和它共用同一台机器,也必须把整个安全面扛下来。
展开完整文档(README 剩余 1 节)
dsh-gateway
在手机上通过公网,驱动跑在你桌面上的 DeepSeek Harness 智能体。
你的桌面智能体能读你的文件、跑命令。这个网关是你唯一暴露到公网的东西,它的设计目标就是:面对「万一有人发现了这个 URL 会怎样?」这个问题,答案是「什么都不会发生」。
PHONE ──HTTPS──▶ VPS: Caddy (TLS) ──▶ frps ─┐
│ encrypted tunnel
HOME MAC: frpc ◀───────────────────────────┘
│
▼ 127.0.0.1 only
dsh-gateway ──▶ /m/ mobile PWA
──▶ /api/v1/* versioned API (REST + WebSocket)
──▶ ACP stdio ──▶ dsh --profile acp
──▶ read-only ──▶ ~/.dsh/sessions/.jsonl.zstd
为什么是这个样子
DeepSeek Harness 的三个特性决定了这套设计,每一个都是靠读代码、探真实安装环境确认的,不是拍脑袋假设的:
1. dsh web 只绑回环地址,自身不带 TLS,也没有认证。 所以网关必须和它共用同一台机器,也必须把整个安全面扛下来。
2. ACP 配置档(dsh --profile acp)是唯一有文档、有版本、面向第三方客户端的线级接缝。 它在 stdio 上跑换行分隔的 JSON-RPC,说的是 ACP v1——这是公开规范,不是 DSH 内部实现——而且明确不加任何私有方法。所以网关能驱动智能体,不用逆向任何东西,DSH 升级也不太可能把它弄坏。
3. DSH 用文件锁保证每个会话只有一个活跃写入方。 挂到网关上的会话,就没法在桌面上打开了。这是实打实的限制,网关把它摆到明面上,而不是藏起来——见下面的会话租约。
你能得到什么
- 一个移动端 Web 应用,在 /m/,可以装到主屏幕,专门服务你在手机上真正会用的那套流程:浏览会话、读历史、发提示词、看工具跑、批准或拒绝工具、切换模型和推理强度。
- 有分量的审批。 任何东西都不会自动批准。每个需要授权的工具都会连它的确切参数一起展示,超时没答复的一律拒绝。
- 你的桌面会话。 网关读的是 DSH 自己的会话存储,所以你在工位上开的对话,会带着历史和标题出现在你手机上。
- 一个可选的逃生通道。 打开 desktopUI.enabled,就能通过同一个已认证的源,够到完整的 DSH 浏览器 GUI,用来干那些手机应用不该干的事——终端、文件浏览器、插件管理。
这东西不适合用来做什么
值得在快速开始之前读一读,因为上面那套安全说法,只在这些边界内成立:
- 不是多租户服务。 每台配对过的设备都有完整权限:它能驱动任何会话、批准任何工具调用、够到工作区里的任何东西
- 不是针对不可信用户的沙箱。 不要把配对的手机交给一个你不敢给他桌面机器 shell 的人。
- 不是替别人把桌面安全暴露到公网的方式。 网关只绑定 loopback,其他一律拒绝,但它周边的部署——VPS、隧道、手机上的 CA 根证书——是实打实的安全决策,docs/security.md 这份文档会说明你要承担什么。
快速开始
你需要一份 checkout,桌面端仅支持 macOS(它会安装一个 launchd 任务)。VPS 端面向 Ubuntu 24.04。
git clone https://github.com/huanghantao/dsh-gateway
cd dsh-gateway
除此之外的前置条件:一台运行 DeepSeek Harness 的机器、一台公网 Linux 服务器、Go 1.25+ 工具链、Node 22+ 与 npm,以及一个主机名。需要 Node 是因为网关内嵌了它的 web 应用,构建时会编译这个应用——用的就是安装 DSH 时用的那个 Node。如果你没有域名,<ip-with-dashes>.sslip.io 会解析到你服务器的 IP。
这是针对什么版本构建的。 DeepSeek Harness 0.1.7-rc.2,使用 ACP v1——这个版本是适配器直接断言的,而不是协商出来的,因为 DSH 会忽略客户端提出的版本,而 v1 是它唯一会说的版本。transcript 投影另外还受 session.v4 磁盘日志格式约束,遇到更新的格式时会降级为「历史不可用,请在桌面端打开」。因此 DSH 升级最可能先表现为 ACP 握手被拒或历史缺失,而不是崩溃。这两个版本号在代码里都没有固定住,因为代码里没有任何东西能强制它们——如果你用的是别的版本又出了问题,这就是在 issue 里首先要提的事。
go install github.com/huanghantao/dsh-gateway/cmd/dsh-gateway@latest 行不通:前端会编译进二进制,且没有提交到仓库,所以 module proxy 里没有东西可内嵌。请从 checkout 构建——安装脚本就是这么做的。
Caddy 在 8443 端口提供 HTTPS,因为很多服务商在域名完成 ICP 备案前会封禁入站 80 和 443。在这些主机上拿不到公开可信的证书——所有不需要 DNS API 的 ACME 挑战都需要 80 或 443 端口——所以 Caddy 用自己的 CA 签发,并从同一个文件在两处提供这个根证书:TLS 上是 https://<domain>:8443/ca.crt(主路由,需要点掉一个警告),明文上是 http://<domain>:8080/ca.crt(没有警告,但按主机名过滤明文 HTTP 的服务商可以拦截它)。下面的命令就是按这个前提写的。如果你的服务商不封 80/443,--tls acme --https-port 443 --http-port 80 能拿到公开可信的证书,手机上什么都不用装。
1. 在服务器上。--domain 必填;名字随你取。
sudo ./deploy/vps/install.sh --domain 203-0-113-10.sslip.io
默认值:HTTPS 走 8443,Caddy 自带的 CA,CA 根证书在 8443(TLS)以及明文 8080 上提供
2. 在桌面机器上(复制安装脚本打印出来的 frp token)。
三个参数都必填:隧道通向哪里、手机将信任哪个名字、这台机器上手机可以访问什么。
./deploy/mac/install.sh \
--server 203.0.113.10 \
--public-url https://203-0-113-10.sslip.io:8443 \
--workspaces "$HOME/code" \
--token <token>
3. 让手机信任这套部署(默认 --tls internal 模式)。
打开 https://203-0-113-10.sslip.io:8443/ca.crt,接受一次性的证书警告:它提供的根证书,正是用来验证它自己的那个。
iOS: 安装描述文件,然后在「证书信任设置」里启用完全信任。
两步都必做。
Android:设置 → 安全 → 加密与凭据 → 安装证书 → CA 证书。
如果你所在的地方没有过滤纯 HTTP,也可以用: http://203-0-113-10.sslip.io:8080/ca.crt(无警告),或者走带外:
ssh root@203.0.113.10 \
'cat /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt' \
dsh-gateway-ca.crt
用 --tls acme 可以完全跳过这一步;退役这套部署时,把 CA 删掉。
4. 检查整条链路,然后配对手机 dsh-gateway doctor # 指出第一个出错的地方,以及怎么修 dsh-gateway pair # 打印一个链接和二维码
扫码,应用打开时配对码已经填好。
完整操作流程见 docs/deployment.md,威胁模型见 docs/security.md, API 约定见 docs/api.md。
ACP 配置档需要你的服务商
acp 配置档出厂时配的是内置的 deepseek-official 路由,需要 DEEPSEEK_API_KEY。如果你的桌面端用的是别的服务商,手机端每次提示词都会失败,报:
no API key for provider route "deepseek-official"
把 ACP 配置档指向你 web 配置档用的同一个服务商,编辑 ~/.dsh/profiles/acp/cordis.patch.yml 即可。关键的那一行是 acp 本身——只覆盖 agent-default-model 没有用,因为决定 ACP 上创建的会话回退选择的是另一行:
- id: acp
name: "@deepseek-ai/dsh-acp"
config:
provider: your-provider
model: your-model
或者,把凭据放进网关的环境变量:
dsh:
extraEnv:
- DEEPSEEK_API_KEY=sk-…
会话租约
DSH 每个会话只允许一个活跃写入者,用内核文件锁强制。因此,网关已经接管的会话,无法在你的桌面端打开,而且反之亦然。与其让这个问题以令人困惑的错误形式暴露出来,网关把它变成一等概念:
- 打开会话、发送提示词,或显式请求租约时,就会建立挂接。
- 最后一个客户端离开后,在 session.idleTimeout(默认 5 分钟)内没有任何活动,租约就会被释放。
- 正在进行的回合会钉住租约;任何操作都不会在回合中途释放会话。
- GET /sessions/{id} 会报告 leased,这样应用就能告诉你为什么桌面端拒绝打开某个东西。
设计
六边形架构,组合根是唯一知道各部分如何拼装的地方。依赖指向内部:
| 层 | 包 | 了解什么 |
| 端口 | internal/harness、internal/authn | 除了标准库,什么都不了解 |
| 适配器 | internal/harness/acp、internal/authn/devicetoken、internal/sessionlog、internal/edge | 各自只了解一个外部系统 |
| 应用 | internal/app/{events,lease,approvals,bridge} | 端口 |
| 驱动适配器 | internal/httpapi/v1、web | 应用 |
| 组合 | cmd/dsh-gateway | 了解一切,其他任何地方都不了解 |
适配器层之上没有任何地方提到 ACP、stdio 或 JSON-RPC。把 ACP 适配器换成进程内桥接——这样就能镜像实时桌面会话——只需要改一个包。
四个运行时依赖,每一个都有存在的理由:
| 包 | 理由 |
| coder/websocket | 感知上下文、极简、设计上只允许一个并发写入者 |
| klauspost/compress | 纯 Go 的 zstd,读取 DSH 的会话日志需要它 |
| yaml.v3 | 配置 |
| skip2/go-qrcode | 配对二维码 |
开发
make check # gofmt + tsc, go vet, and the full test suite
make lint # golangci-lint v2 (the tree is lint-clean; CI fails on findings)
make build # builds web/dist then the binary
make run # runs against ./.dev-state
make pair # prints a pairing code for the dev instance
make test-llm # opt-in: drives a real model turn (costs money)
如果某个部署不工作,dsh-gateway doctor 是找出原因最快的方式。参见 docs/deployment.md。
make build 先编译前端,因为 Go 二进制会嵌入它。这个顺序是强制执行的,而不是写在文档里的:如果 web/dist 不存在,go build 会直接失败,这比一个提供空白页面的二进制要好。web/dist 是生成的,不提交到仓库,所以每条构建网关的路径都会先构建它——包括 deploy/mac/install.sh。因此全新克隆的仓库需要 PATH 上有 Node 和 npm,而一个能正常工作的 dsh 安装(它本身就是一个 npm 包)已经隐含了这一点。前端只有一个 devDependency,没有打包器,所以这一步就是对 web/src 跑一次 tsc。
测试套件分三层:
- 单元 —— 到处都能跑,没有外部依赖。
- 集成 —— 驱动一个真实的 dsh --profile acp 子进程。未安装 dsh 时自动跳过。
- 需手动开启 —— 一次真实的模型轮次,由 DSH_GATEWAY_TEST_LLM=1 控制。
- 浏览器 —— make e2e 在真实 Chrome 里驱动真实应用,对接一个正在运行的网关:扫描配对链接、完成配对、打开事件流、创建会话,然后跑一次真实轮次。
集成测试这一层之所以存在,是因为这段代码依赖的 ACP 字段本身就是靠探测一台在线服务器才确定的。只做文档层面的测试,会一直通过,而线上格式早已在底下漂移。
浏览器这一层存在的理由相同,只是换到了另一条边界上。这里没有前端单元测试套件 —— tsc 只能证明类型对得上,没有任何东西证明应用能跑 —— 而 Go 测试直接调 API,两者都没跨过它们交汇的地方。到目前为止在那里发现的每个 bug,都恰好活在这条缝上:一个 /me 响应,字段名和设备列表对不上;一条事件流,配对之后一直停着,因为手机发的第一个请求是 401;还有一个「复制会话 id」,在任何纯 HTTP 源上都复制不出东西,因为一个可选链把整条语句短路了。这三个 bug 对其他任何测试都不可见,而用真实浏览器跑一分钟就一目了然。
浏览器这一层不属于 CI:它需要一台正在运行的网关,而网关又需要装好 dsh 和一份模型凭据,这两样都不该出现在跑 fork pull request 的工作流里。CI 只检查它能解析;跑不跑在你。
已知限制
这些都是真实存在的,每一条都是有意取舍,而非疏漏。
1. 网关无法镜像桌面正在主动驱动的会话。 DSH 的单写者锁让这在 ACP 上不可能实现。你能拿到桌面的空闲*会话,带完整历史,也能接管它们 —— 但没法旁观另一个进程里正在跑的轮次。想看实时会话,可选方案是桌面 GUI 代理;要彻底解决,需要一个进程内的 DSH 插件,端口设计已经为此留了空间。
2. 没有 token 级流式输出。 ACP 交付的是已提交的消息块,不是部分增量,所以文字一段一段地出现,中间由工具活动填补。转录内容不受影响。
3. 历史依赖 DSH 的磁盘日志格式,它带版本号,但不是公开契约。网关对它做版本门控,降级为「历史不可用,请在桌面打开」而不是直接失败。所有格式知识都局限在 internal/sessionlog 里。
4. DeepSeek 账号登录无法通过桌面 GUI 代理工作。 上游把 OAuth 重定向限制在回环源。GUI 里其他功能都正常。
5. sslip.io 是第三方 DNS 服务。 它很方便,换成你自己控制的域名只需改一行配置。
6. 基线认证器是配对设备 token。 TOTP 和 WebAuthn 尚未实现;internal/authn 是一个端口,所以它们只是额外的适配器,不需要重新设计。
参与贡献、安全、隐私
- CONTRIBUTING.md —— 构建顺序的坑、CI 会跑但 make check 不跑的东西,以及这份提交历史遵循的提交信息风格。
- SECURITY.md —— 如何私下报告漏洞,以及哪些内容故意不在范围内,因为 docs/security.md 已经把它作为权衡写清楚了。
- PRIVACY.md —— 一次部署可能牵涉到的每一方、审计日志记录什么不记录什么,以及「已删除」的会话实际会在磁盘上留多久。
许可
MIT。见 LICENSE;第三方声明在 THIRD_PARTY_NOTICES.md。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
huanghantao/dsh-gateway
在手机上安全驱动桌面智能体,审批工具调用
相关插件 · 安全 / 权限
智能体逆向工程morluto/rea
用智能体逆向工程任何东西,从应用行为到原生二进制。
★ 415,267已汉化DSH
73.3
智能体集群框架ruvnet/ruflo
面向 Claude Code 与 Codex 的智能体元基座meta-harness。
★ 80,414已汉化MCP
71.9
WorkBuddy 模型接入corrinehu/dsh-workbuddy-connect
2026 年 10 月 20 日起,WorkBuddy…
★ 1,604已汉化DSH
71.7
破甲一键通z91772524-ai/pojia-next
十五个 AI…
★ 967已汉化DSH
71.6
AI 代理虚拟终端strukto-ai/mirage
Mirage 是给 AI 智能体用的虚拟终端:一层 shell,统管数据、命令行和运行时。Unix…
★ 3,756已汉化DSH
70.8
Agent 操作层agentic-os-org/ANOLISA
ANOLISA(Agentic Nexus Operating Layer & Interface System Architecture)|智能体操作系统,带运行时、安全、可观测性,以及 Tokenless 响应压缩,降低 token 用量与成本。
★ 649已汉化DSH
70.7