← 返回列表
✓ 可直接安装
为 DeepSeek Harness Web 配置文件提供经过身份验证的远程访问——通过 Wi-Fi 从手机访问你的…
自动检查通过:npm 包已发布且 engines 声明满足基线(声明 Node ^22.19.0 || >=24);该结论来自程序自动检查,未经人工实机验证。 · 最近上游提交 2026/8/28 · 已提供中文文档
DeepSeek Harness Web 配置文件的已认证远程访问:在未改动的回环 harness 前提供 TLS、QR/密码设备配对、密码登录以及按设备撤销。
综合分
37.2
GitHub 分
37.2
用户评分
—
★ Stars
7
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add dsh-relaynpm 包 dsh-relay 已校验归属本仓库,走 npm 安装最省事
数据截至 2026/9/19(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查✓ 自动检查通过
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-relay @ 0.2.1
✓Node 引擎要求 ^22.19.0 || >=24 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/19 16:50:02
依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-client-runtime@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-client-ui-settings-plugins用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
dsh-relay
npm
CI
为 DeepSeek Harness Web 配置文件提供经过身份验证的远程访问——通过 Wi-Fi 从手机访问你的 harness,或者如果你转发端口,则可以从任何地方访问,而无需在网络中留下一个未经身份验证的编码代理。
基于 DeepSeek Harness 构建。并非 DeepSeek 官方项目。
为什么会有这个项目
harness 在回环地址上提供其浏览器 API,并且明确说明了它不做什么:
- packages/client/connection/src/api-request-trust.ts —— /api 防护栏“不是认证层”。
- packages/bundle/web-app/src/startup.ts —— dsh web --host 0.0.0.0 会被拒绝,因为“这会将远程代码执行暴露到网络上”。
Harness 0.1.2 添加了自己的身份验证:一个签名的浏览器会话
cookie,通过交换 harness 每个进程打印一次的启动令牌获得,现在整个 /api 接口都要求提供它。这是一个真正的改进,它改变了这个中继的用途——但并没有改变它是否被需要。harness 仍然拒绝 --host 0.0.0.0,仍然没有 TLS,而且它的 cookie 只在其自身的索引路由上生成,因此仍然没有任何东西能让手机安全地跨网络访问它。中继仍然是终止 TLS、固定密钥并决定谁可以进入的那一层。
确实发生变化的是,harness 删除了其仅限回环的方法层级。
直到 0.1.1,它都将 settings.、credentials. 和主机选择器固定到回环地址本身,而这个中继镜像了该列表,因此它的 Host 重写无法解除该固定。0.1.2 有一个统一的经过身份验证的接口:任何持有会话的人都可以访问它的全部内容。因此,privilegedMethods 不再是任何东西的镜像——它是这个中继自己的策略,也是配对手机与操作员凭据存储之间的唯一屏障。
人们今天使用的变通方法是一个配置补丁,它将 Web 服务器重新绑定到 0.0.0.0,且完全没有身份验证。同一 Wi-Fi 上的任何人都可以驱动该代理,这意味着在你的计算机上运行命令。
dsh-relay 是缺失的那一层,它被挂载在 harness 旁边,而不是内部。harness 保持其回环绑定;中继是第二个监听器,负责终止 TLS、进行身份验证并转发。
phone / browser ──TLS──▶ relay :3443 ──plain HTTP──▶ harness 127.0.0.1:3080
├─ /relay/password
├─ /relay/login
├─ /relay/pair
├─ /relay/devices
└─ everything else ─proxy─▶ /api · WebSocket downlinks · web UI
由于 harness 始终监听回环地址,一个启动失败或配置错误的 relay 会让 harness 从网络上无法访问——而绝不会对其开放。如果 relay 发现 harness 已经绑定到 0.0.0.0,它会完全拒绝启动。
你将获得
- 浏览器密码登录,以签名的 HttpOnly; SameSite=Strict cookie 形式,基于 scrypt 哈希,并带有按地址的锁定机制。
- 设备二维码与配对码配对,签发可撤销的 bearer token。配对码一次性使用、有效期短,且只能从运行 harness 的机器上签发。
- 设备列表,支持按设备撤销,以及一个会轮换签名密钥的“在所有位置退出登录”。
- TLS,可使用你自己的证书,或使用自签名并公布 SPKI pin。
- 在 _dsh._tcp 上的 mDNS 广播,这样客户端无需扫描子网即可找到 relay。
- 整个 Web UI,保持不变。代理是透明的,因此浏览器应用在手机上运行的效果与本地完全一致,角落处有一个 Relay 链接指向上述页面。有两处小改动搭载在 index 文档中,而非代理中:那个链接,以及一个 shim,让页面在浏览器不提供 crypto.randomUUID 时也能生成请求 id。
- 在运行 harness 的机器上的 设置 → 插件 中有一张卡片,用于那些属于配置而非操作的开关。它遵循 harness 自身的插件卡片惯例:开关先暂存,然后由 保存 一并写入——这在这里很重要,因为保存会重新绑定监听器并断开正在进行的连接。
安装
初次使用?请从 docs/GETTING_STARTED.md 开始——一份针对 npx @deepseek-ai/dsh web 的分步指南,包括你必须先移除的 LAN 补丁。
简版说明。如果你的 harness 当前绑定 0.0.0.0(DSH Mobile LAN 补丁),请先从 ~/.dsh/profiles/web/cordis.patch.yml 中移除那一行——relay 拒绝在一个已经开放的服务器前面启动。然后:
dsh plugin --profile web add dsh-relay
dsh web
PATH 中没有 dsh?每条命令都与 npx @deepseek-ai/dsh ... 等效。
注册表包以预构建形式发布,因此你的机器上不会运行任何构建;添加 bundle 需要重启。终端随后会打印 relay URL。从运行 harness 的机器上打开其上的 /relay/password 并设置密码——在密码存在之前,该页面仅限回环访问,因此网络上没有人能抢先占用 relay。
来自该机器的请求永远无需登录:回环即操作者,因为坐在键盘前的人已经拥有 shell。密码是网络所需要的。
配对手机
1. 在运行 harness 的机器上,打开 https://127.0.0.1:3443/relay/pair。
2. 用手机扫描二维码,或在手机上打开同一路径并输入配对码。
3. 为设备命名。它现在已注册,并出现在 /relay/devices 下。
与 DSH Mobile 配合使用
DSH Mobile 0.8.0 完整实现了
docs/CLIENT_INTEGRATION.md:它通过二维码或配对码进行配对,在每次
/api 调用以及两个 WebSocket 升级请求中都携带 bearer token,并且固定此中继的密钥,而不是信任
证书颁发机构。从 Relay → Pair a relay 进行配对,无需其他操作。
一旦你使用的所有客户端都升级到 0.8.0 并完成配对,就关闭桥接:
compat:
addressGrants: false
0.5.0 到 0.7.0 —— 兼容路径及其代价
这些版本早于本插件,有两个局限,本插件只能绕过它们,而不能假装它们不存在。
它们无法访问自签名监听器。 直到 0.6.0,应用为其 RPC
调用和两个下行链路硬编码了 http://;0.7.0 学会了 https://,但会依据证书
颁发机构进行验证,而自签名中继并不是证书颁发机构。请在 TLS 监听器之外再运行一个明文监听器:
DSH_RELAY_PLAIN_PORT=3444 dsh web
该监听器不提供任何登录或配对页面,也永远不会触及 harness 设置或
凭据。
它们无法出示凭据。 它们不发送 Authorization 头、不发送 cookie,也不发送
Origin —— 其中没有任何字段可以携带 token。因此,从手机浏览器进行配对会记录该
手机的网络地址,然后应用从同一地址进行连接。
要清醒地认识到这意味着什么:源地址不是身份验证。它在 NAT 之后被共享,会被 DHCP 重新分配,会被 IPv6 隐私扩展轮换,并且可被同一
Wi-Fi 上的任何设备伪造。中继会尽可能缩小其范围——仅限私有地址范围、有 TTL、随创建它的设备一起失效,并且永远不会触及配置平面——但它只是一座桥,不是终点。
升级客户端才是解决办法。
docs/CLIENT_INTEGRATION.md 是契约,供任何编写其他客户端的人使用。
配置
每个值都位于你配置文件的 cordis.patch.yml 中 relay 行下。你的层在 bundle 的层之后应用,因此会生效。补丁会替换该行的整个 config,所以请重新声明你想要的每个键——包括 stateDir,它没有默认值。
- id: relay
name: 'dsh-relay'
config:
bind: '0.0.0.0'
port: 3443
stateDir: !!js dshHomePath('relay')
tls: 'files'
tlsCertPath: '/path/to/fullchain.pem'
tlsKeyPath: '/path/to/privkey.pem'
publicHostnames: ['relay.example.com']
privilegedMethods: 'allow-authenticated'
compat:
addressGrants: true
addressGrantTtlMs: 86400000
plainPort: 3444
| 字段 | 默认值 | 它决定什么 |
|---|---|---|
| bind / port | 0.0.0.0 / 3443 | 主监听器。 |
| tls | self-signed | files 表示使用浏览器信任的证书,self-signed 表示供客户端固定,off 表示明文。 |
| publicHostnames | [] | 访问此中继所用的额外名称——证书 SAN,以及可接受的 Host 值。 |
| trustedHosts | [] | fence 额外接受的 authority,形式为裸 host 或 host:port。 |
| auth | both | 接受哪些凭据类别。 |
| sessionTtlMs | 12 小时 | 浏览器 cookie 生命周期。 |
| deviceTokenTtlMs | 30 天 | 设备令牌生命周期。 |
| pairingWindowMs | 5 分钟 | 配对码保持可认领的时长。 |
| maxFailedAttempts / lockoutMs | 5 / 15 分钟 | 登录锁定。 |
| rateLimitPerMinute | 600 | 每地址请求上限。 |
| privilegedMethods | allow-authenticated | 已认证的远程客户端是否可以访问设置、凭据、模型发现和主机选择器。无论如何,通过地址授予的客户端永远不能。从 harness 0.1.2 起,这是唯一控制它们的东西——harness 不再自行固定它们。 |
| extraProxyPaths | [] | 写入可以寻址的额外路径前缀。 |
| compat.addressGrants | true | 上文描述的 0.8.0 之前的 DSH Mobile 桥接。 |
| compat.plainPort | 0 | 为无法使用 TLS 的客户端提供的纯 HTTP 监听器。同时接受 bearer token 和 grant,因此它的生命周期比 addressGrants 更长。 |
| uiLink | true | 将 Relay 链接添加到 harness Web UI。 |
| mdns | true | 通告 _dsh._tcp。 |
每次调用覆盖
不存在 --relay- 标志,也不可能有。 harness 的 Web 应用拥有该调用的解析器,并拒绝任何它未声明的选项,因此由 bundle 添加的标志会在任何插件加载之前就让 dsh web 失败。随附的补丁改为读取环境变量:
| 变量 | 效果 |
|---|---|
| DSH_RELAY_PORT | 主监听端口 |
| DSH_RELAY_BIND | 监听地址 |
| DSH_RELAY_TLS | self-signed、files 或 off |
| DSH_RELAY_PLAIN_PORT | 纯 HTTP 监听端口;0 禁用它 |
| DSH_RELAY_DISABLE=1 | 本次运行完全跳过 relay |
DSH_RELAY_PLAIN_PORT=3444 dsh web
证书
手机浏览器会对自签名证书发出警告。若要在浏览器中使用,请将 tls: files 指向已经受信任的内容——局域网上的 mkcert,或转发名称上的 ACME 证书。
自签名模式是为固定客户端而存在的:relay 会在 QR 负载中和设备页面上发布其 SubjectPublicKeyInfo 的 SHA-256,固定该值的客户端无需证书颁发机构即可获得真正的传输安全。该固定针对的是公钥而非证书,因此使用同一密钥续期后,已配对的设备仍可正常工作。
生成的密钥以 0600 模式写入。在 Windows 上这是空操作——文件会继承你的 harness home 的 ACL。如果该目录是共享的,请用 icacls 限制它,或使用 tls: files 自行管理证书。
将其暴露到互联网
转发 relay 的端口,而不是 harness 的端口。然后:
- 将 publicHostnames 设置为你访问它时使用的名称,否则 fence 会拒绝该请求。
- 使用真实证书。自签名加固定是局域网方案。
- 保持 compat.addressGrants 关闭。在运营商 NAT 后面,公网地址与陌生人共享,而且中继无论如何都会拒绝授予一个。
- 设置 privilegedMethods: loopback-only。在 harness 0.1.2 及更高版本中,这不是第二层谨慎措施;它是唯一的一层,因为 harness 会将其整个 API 提供给任何已认证的调用者。
不要把 Funnel、Serve、nginx 或 Caddy 放在 http://127.0.0.1:3443 前面。 这些代理从 loopback 连接,而 loopback 就是操作员:中继不会要求密码或设备令牌。将代理指向此进程正在监听的某个非 loopback 地址(Tailscale IP 或 VPC 地址),保持 bind: 0.0.0.0,并在公网网卡上关闭 :3443,这样该地址就不会成为第二扇门。
当中继能够看到这种捷径时,它会拒绝:一个携带 Forwarded、X-Forwarded-、X-Real-IP 或 Via 的 loopback 请求会被归类为网络流量——它会遇到登录门禁和速率限制,而不是操作员的席位。Funnel、Serve 和 Caddy 总是会打上 X-Forwarded-For,因此即使出现上述错误配置,它们的客户端也会撞上门禁。这是一个迹象,而不是证明:一个被配置为剥离其转发头的代理与你自己浏览器无法区分,因此非 loopback 目标仍然是应当运行的部署方式。
验收标准:通过公共 URL 未经认证的 GET / 必须返回 403。200 的 harness HTML 意味着代理来自 loopback 并隐藏了其转发头——将其目标移出 loopback。
Funnel HTTPS 在边缘终止 TLS。在其后面运行 tls: off。来自 http://127.0.0.1:3443/relay/pair 的二维码编码的是那个 loopback 源;从外部配对时,请键入公共 https:// 名称,而不是扫描那个二维码。参见 #1。
安全模型
阅读 docs/SECURITY.md 获取完整声明。简而言之:登录所授予的权力等同于在主机上拥有一个 shell,因为 agent 在那里运行命令。此插件中的一切都由此而来。
故障排除
没有任何响应,连接超时。 防火墙正在丢弃数据包。在 Windows 上,未识别的网络会进入 Public 配置文件,该配置文件会阻止入站 TCP:
New-NetFirewallRule -DisplayName "DSH Relay" -Direction Inbound
-Action Allow -Protocol TCP -LocalPort 3443 -Profile Private,Domain
将网络设置为 Private。如果仍然超时,请检查路由器是否启用了 AP/客户端隔离——访客 SSID 几乎总是启用了它。
连接被拒绝。 没有任何东西在监听。确认中继已启动(netstat -ano | findstr 3443),并且 --no-relay 未生效。
来自中继的 403。 你用来访问它的 Host 不受信任。请通过中继自行推导出的 IP 字面量连接,或将该名称添加到 publicHostnames。
插件拒绝启动,并指出 webserver 那一行。 你仍然保留着旧的 LAN 补丁,它把 harness 绑定到 0.0.0.0。移除它——中继无法保护一个已经在响应网络的服务器。
DSH Mobile 显示“the harness rejected this address”。 这是 403。要么地址授权已过期,要么手机地址已更改;请从手机浏览器重新配对。
页面加载了,但侧边栏一直为空,控制台反复输出 connection lost, retry #N。 浏览器仅在 HTTPS 或 localhost 上暴露 crypto.randomUUID,而 harness 的浏览器客户端用它来生成每个 RPC id——因此从 LAN 地址通过纯 HTTP 访问时,就绪握手会抛错,两个事件套接字在打开前就被关闭。一元调用仍然可用,这就是登录看起来正常的原因。中继在索引文档中为此提供了一个 shim,所以如果你仍然看到该问题,说明页面并非来自该文档:硬刷新以绕过缓存副本,并检查中继前面是否有任何东西在提供自己的 index.html。通过 TLS 或从 127.0.0.1 浏览可完全避免此问题。
每个请求都被拒绝,包括 /relay/health,并且日志显示 untrusted-host。 中继只响应 loopback、它自己的地址,以及 publicHostnames 指定的任何地址——因此,一个它自己不知道的地址会拒绝一切,甚至包括未认证的存活探测,应用会把正在运行的中继报告为缺失。日志行会指出它拒绝的 Host;将其添加到 publicHostnames。Android 模拟器的 10.0.2.2 是唯一的例外,自 0.2.1 起会从 loopback 对等端自动允许。
一切都被 401 拒绝,或者应用报告一个无法打开的流。 中继无法生成 harness 浏览器会话。Harness 0.1.2 对其整个 /api 表面进行认证,而中继使用 harness 自己的持久密钥在 client-connection/browser-session 处签署 cookie——该密钥由 harness 在首次运行 dsh web 时创建。先启动一次 dsh web,然后重新加载插件;当中继找不到该密钥时,会在启动时记录一行日志。
模型选择器为空,设置页面显示“settings are unavailable in this browser”。 在任何非 loopback 地址上都是预期行为,通过 TLS 也是如此,并且不是中继在其所在位置能修复的问题。harness 的浏览器客户端通过读取 location.hostname 来决定配置平面是否存在;在 LAN 地址上,它会在内存中创建设置镜像,并且从不发送这些调用——而本中继本可以承载这些调用,因为 privilegedMethods 默认允许已认证客户端通过。会话、工作区和聊天不受影响。请从运行 harness 的机器上的浏览器访问设置、提供程序目录和模型发现。修复属于上游,在客户端而非中继;参见 #4。
开发
pnpm install
pnpm build # tsc -b, then tsdown
pnpm test
pnpm typecheck
将源代码检出指向正在运行的测试框架,并使用一个直接命名已构建入口点的覆盖层:
yaml
- insert:
- id: relay
name: 'file:///D:/path/to/deepseek-harness-relay/lib/index.js'
config:
stateDir: 'D:/path/to/scratch/relay-state'
tls: 'off'
sh
dsh web --patch ./dev.cordis.yml
在 Windows 上,路径必须是 file:// URL,而不是裸绝对路径——加载器会将其交给 ESM 解析器,而后者会拒绝 d: 协议。
命名
测试框架的品牌指南要求生态系统项目在其名称中使用 DSH 缩写,而不是完整商标,并且不得展示官方品牌图案。npm 包名为 dsh-relay,页面带有本项目自己的标识。
许可证
MIT同作者(sorsama)的其他插件
扫码进群