deepseek-harness-relay

DSH PLUGIN浏览器与联网

仓库 sorsama/deepseek-harness-relay · npm dsh-relay

浏览器与联网 L5 · 运行验证通过 安全审查:低风险 可信度:金 可安装插件
安装这个插件

dsh-relay 为 DeepSeek Harness 网页配置提供带认证的远程访问,通过 TLS、密码登录、QR/配对码设备绑定及按设备撤销,在保持环回监听的同时安全地从手机访问。

验证等级 L5 L5 · 运行验证通过
GitHub 星标 9 复刻 7 · 未关闭 issue 7
最近更新 2026-08-28 创建于 2026-08-21
README 18.2 KB 正文体量,越大说明说明书写得越细

安装

可安装插件
在 DSH 里执行,装完用 dsh plugin list 确认已挂上;标了 L4 / L5 的命令已在沙盒里真实跑通
dsh plugin --profile web add [email protected]
兼容 DSH 版本0.2.0-rc.2 等 9 个版本版本不匹配时安装会报错
Node.js 要求^22.19.0 || >=24来自仓库 package.json 的 engines 字段
前置依赖@deepseek-ai/cordis@>=4.0.1、@deepseek-ai/dsh-client-ui-settings-plugins@>=0.1.0-rc.7 等 3 个需要同时安装的同级依赖
npm 包名dsh-relay可用 npm view 查看包信息

能做什么

4 项功能
字段来自项目仓库与公开索引,中文说明由本站标注

deepseek-harness-relay:DeepSeek Harness 的安全远程访问中继

  • TLS 加密远程访问
  • 密码登录与设备配对
  • 按设备撤销权限
  • mDNS 自动发现

适用场景

联网搜索

上游标签

androidauthenticationdeepseek-harnessdsh-pluginmdnsmobilerelayreverse-proxyself-hostedtls

验证等级与安全审查

L5 · 运行验证通过
自动化流程结论,不是人工审核
验证等级L5 · 运行验证通过在 dsh 沙盒中完成安装与运行冒烟——装得上、跑得起来
安全审查低风险静态分析结论,未标注不代表已通过审查
仓库健康度稳定按仓库活跃度与最近更新时间给出
可信度金综合仓库活跃度与验证结果

上面的结论来自上游最近一次自动化验证(2026-10-08): 同一套检查会在插件发新版后重跑,页面显示的是最新一次的逐项结果。

逐项检查结果

L1 · 已找到 ✓ 通过
  • L1.0_DEFAULTED_FROM_L3 由 L3 推定 默认通过——L3 安装规范已通过(全库 L3 扫描策略)
L2 · 结构化 ✓ 通过
  • L2.0_DEFAULTED_FROM_L3 由 L3 推定 默认通过——L3 安装规范已通过(全库 L3 扫描策略)
L3 · 安装规范 ✓ 通过
  • L3.1_INSTALL_COMMAND_PARSED 安装命令已解析 方式:安装规范解析
  • L3.2_DSH_VERSION_DECLARED DSH 版本已声明 方式:DSH 版本声明
L4 · 安装已测 ✓ 通过
  • L4.1_INSTALL_EXECUTED_CONFINED 受限沙盒内执行安装 方式:沙盒安装执行
  • L4.2_INSTALL_SUCCESS 沙盒安装成功 方式:沙盒安装执行
  • L4.3_ARTIFACT_CONFIRMED 安装产物已确认 方式:沙盒安装执行
L5 · 运行已测 ✓ 通过
  • L5.1_INSTALL_PASSED_SANDBOXED 沙盒安装通过 方式:沙盒安装执行
  • L5.2_WEB_BOOT_READY Web 启动就绪 方式:Web 启动
  • L5.3_HTTP_SERVED HTTP 服务响应 方式:HTTP 请求
  • L5.4_PLUGIN_INVENTORY_ACTIVE 插件清单中已激活 方式:插件清单查询

风险详情

  • 审查结论 通过
  • 结论说明 vet 静态分析未发现可疑行为,判定为干净(clean)。
  • 风险计数 严重 0 / 高 0 / 中 0
  • 风险详情 未发现严重或警告级问题。

仓库档案

浏览器与联网
字段来自项目仓库与公开索引,中文说明由本站标注
作者 / 维护方sorsama仓库 deepseek-harness-relay · 默认分支 main
分类浏览器与联网search
收录类型可安装插件声明了 dsh.bundle、可用 dsh plugin add 安装的条目
开发语言TypeScript仓库主语言
许可证MIT商用或二次分发前请确认条款原文
星标 / 复刻 / issue9 / 7 / 7收录时的数值,非实时
创建 / 更新2026-08-21 · 2026-08-28最后推送 2026-08-28
本站收录2026-08-27首次进入本站目录的时间

README 全文

18.2 KB
作者原文,中文版优先;只做安全转义,不改内容

dsh-relay


Authenticated remote access for a DeepSeek Harness web profile — reach your harness from your phone over Wi-Fi, or from anywhere if you forward a port, without leaving an unauthenticated coding agent open on the network.

Built on DeepSeek Harness. Not an official DeepSeek project.

Why this exists

The harness serves its browser API on loopback and is explicit about what it does not do:

  • packages/client/connection/src/api-request-trust.ts — the /api fence "is not an auth layer".
  • packages/bundle/web-app/src/startup.ts — dsh web --host 0.0.0.0 is refused, because "it would expose remote code execution to the network".

Harness 0.1.2 added authentication of its own: a signed browser-session
cookie, obtained by exchanging a launch token the harness prints once per
process, now required on the whole /api surface. That is a real improvement
and it changes what this relay is for — but not whether it is needed. The
harness still refuses --host 0.0.0.0, still has no TLS, and its cookie is
minted only at its own index route, so there is still nothing that lets a phone
reach it across a network safely. The relay remains the layer that terminates
TLS, pins a key, and decides who gets in.

What did change is that the harness deleted its loopback-only method tier.
Through 0.1.1 it pinned settings.*, credentials.* and the host pickers to
loopback itself, and this relay mirrored that list so its Host rewrite could
not lift the pin. 0.1.2 has one uniform authenticated surface: whoever holds a
session reaches all of it. privilegedMethods is therefore no longer a mirror
of anything — it is this relay's own policy, and the only thing between a
paired phone and the operator's credential store.

The workaround people use today is a config patch that rebinds the web server to 0.0.0.0 with no authentication at all. Anyone on the same Wi-Fi can then drive the agent, which means running commands on your computer.

dsh-relay is the missing layer, mounted beside the harness rather than inside it. The harness keeps its loopback bind; the relay is a second listener that terminates TLS, authenticates, and forwards.

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

Because the harness stays on loopback, a relay that fails to start or is misconfigured leaves the harness unreachable from the network — never open to it. The relay refuses to start at all if it finds the harness already bound to 0.0.0.0.

What you get

  • Password sign-in for a browser, as a signed HttpOnly; SameSite=Strict cookie over a scrypt hash, with per-address lockout.
  • QR and passcode pairing for devices, minting a revocable bearer token. Codes are single-use, short-lived, and can only be issued from the machine running the harness.
  • A device list with per-device revoke and a "sign out everywhere" that rotates the signing key.
  • TLS, either from your own certificate or self-signed with a published SPKI pin.
  • mDNS advertisement on _dsh._tcp, so a client can find the relay without sweeping the subnet.
  • The whole web UI, unchanged. The proxy is transparent, so the browser app works from a phone exactly as it does locally, with a Relay link in the corner for the pages above. Two small additions ride in the index document rather than the proxy: that link, and a shim that lets the page mint request ids where a browser withholds crypto.randomUUID.
  • A card in Settings → Plugins, on the machine running the harness, for the switches that are configuration rather than operations. It follows the harness's own plugin-card idiom: the switches stage, and a Save writes them together — which matters here, because saving rebinds the listeners and drops the connections in flight.

Install

New here? Start with docs/GETTING_STARTED.md — a step-by-step walkthrough for npx @deepseek-ai/dsh web, including the LAN patch you have to remove first.

The short version. If your harness currently binds 0.0.0.0 (the DSH Mobile LAN patch), remove that row from ~/.dsh/profiles/web/cordis.patch.yml first — the relay refuses to start in front of an already-open server. Then:

dsh plugin --profile web add dsh-relay
dsh web

No dsh on your PATH? Every command works the same as npx @deepseek-ai/dsh ....

The registry package ships prebuilt, so nothing runs a build on your machine; adding a bundle needs a restart. The terminal then prints the relay URL. Open /relay/password on it from the machine running the harness and set a password — until one exists that page is loopback-only, so nobody on the network can claim the relay first.

A request from that machine never has to sign in: loopback is the operator, because whoever is at the keyboard already has a shell. The password is what the network needs.

Pair a phone

  1. On the machine running the harness, open https://127.0.0.1:3443/relay/pair.
  2. Scan the QR with the phone, or open the same path on the phone and type the code.
  3. Name the device. It is now enrolled, and appears under /relay/devices.

Using it with DSH Mobile

DSH Mobile 0.8.0 implements
docs/CLIENT_INTEGRATION.md in full: it pairs by QR or passcode, carries a bearer token on every
/api call and both WebSocket upgrades, and pins this relay's key rather than trusting a
certificate authority. Pair it from Relay → Pair a relay and nothing else is needed.

Once every client you use is on 0.8.0 and paired, turn the bridge off:

compat:
addressGrants: false

0.5.0 through 0.7.0 — the compatibility path, and what it costs

Those releases predate this plugin and have two limits it works around rather than pretends away.

They cannot reach a self-signed listener. Up to 0.6.0 the app hardcoded http:// for its RPC
calls and both downlinks; 0.7.0 learned https://, but validates it against a certificate
authority, which a self-signed relay is not. Run a plain listener alongside the TLS one:

DSH_RELAY_PLAIN_PORT=3444 dsh web

That listener serves no sign-in or pairing pages and never reaches the harness settings or
credentials.

They cannot present a credential. They send no Authorization header, no cookies, and no
Origin — no field in them could carry a token. So pairing from the phone's browser records that
phone's network address, and the app then connects from the same address.

Be clear-eyed about what that is: a source address is not authentication. It is shared behind
NAT, reassigned by DHCP, rotated by IPv6 privacy extensions, and spoofable by anything on the same
Wi-Fi. The relay narrows it as far as it can — private ranges only, a TTL, dies with the device that
created it, and never reaches the configuration plane — but it is a bridge, not a destination.
Upgrading the client is the fix.

docs/CLIENT_INTEGRATION.md is the contract, for anyone writing another client.

Configuration

Every value lives in your profile's cordis.patch.yml under the relay row. Your layer applies after the bundle's, so it wins. A patch replaces the row's whole config, so restate every key you want — including stateDir, which has no default.

- 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
Field Default What it decides
bind / port 0.0.0.0 / 3443 The primary listener.
tls self-signed files for a certificate a browser trusts, self-signed for pinning clients, off for plaintext.
publicHostnames [] Extra names this relay is reached by — certificate SANs, and accepted Host values.
trustedHosts [] Additional authorities the fence accepts, as bare host or host:port.
auth both Which credential classes are accepted.
sessionTtlMs 12 h Browser cookie lifetime.
deviceTokenTtlMs 30 d Device token lifetime.
pairingWindowMs 5 min How long a pairing code stays claimable.
maxFailedAttempts / lockoutMs 5 / 15 min Sign-in lockout.
rateLimitPerMinute 600 Per-address request ceiling.
privilegedMethods allow-authenticated Whether an authenticated remote client reaches settings, credentials, model discovery, and host pickers. Address-granted clients never do, regardless. From harness 0.1.2 this is the only thing gating them — the harness no longer pins them itself.
extraProxyPaths [] Additional path prefixes a write may address.
compat.addressGrants true The pre-0.8.0 DSH Mobile bridge described above.
compat.plainPort 0 Plain-HTTP listener for clients that cannot use TLS. Accepts a bearer token as well as a grant, so it outlives addressGrants.
uiLink true Add the Relay link to the harness web UI.
mdns true Advertise _dsh._tcp.

Per-invocation overrides

There are no --relay-* flags, and there cannot be. The harness's web app owns the invocation's parser and rejects any option it does not declare, so a flag added by a bundle fails dsh web before any plugin loads. The shipped patch reads the environment instead:

Variable Effect
DSH_RELAY_PORT primary listener port
DSH_RELAY_BIND listen address
DSH_RELAY_TLS self-signed, files, or off
DSH_RELAY_PLAIN_PORT plain-HTTP listener port; 0 disables it
DSH_RELAY_DISABLE=1 skip the relay entirely this run
DSH_RELAY_PLAIN_PORT=3444 dsh web

Certificates

A phone browser will warn on a self-signed certificate. For browser use, point tls: files at something already trusted — mkcert on a LAN, or an ACME certificate on a forwarded name.

Self-signed mode exists for pinning clients: the relay publishes the SHA-256 of its SubjectPublicKeyInfo in the QR payload and on the devices page, and a client that pins that value gets real transport security with no certificate authority involved. The pin covers the public key rather than the certificate, so renewing with the same key leaves paired devices working.

The generated key is written mode 0600. On Windows that is a no-op — the file inherits the ACL of your harness home. If that directory is shared, restrict it with icacls or manage the certificate yourself with tls: files.

Exposing it to the internet

Forward the relay's port, not the harness's. Then:

  • Set publicHostnames to the name you reach it by, or the fence will refuse the request.
  • Use a real certificate. Self-signed plus pinning is a LAN answer.
  • Leave compat.addressGrants off. Behind carrier NAT a public address is shared with strangers, and the relay refuses to grant one anyway.
  • Set privilegedMethods: loopback-only. On harness 0.1.2 and later this is not a second layer of caution; it is the only one, since the harness serves its whole API to any authenticated caller.

Do not put Funnel, Serve, nginx, or Caddy in front of http://127.0.0.1:3443. Those proxies connect from loopback, and loopback is the operator: the relay will not ask for a password or a device token. Point the proxy at a non-loopback address this process is listening on (the Tailscale IP, or a VPC address), keep bind: 0.0.0.0, and drop :3443 on the public NIC so that address is not a second door.

The relay refuses that shortcut when it can see it: a loopback request carrying Forwarded, X-Forwarded-*, X-Real-IP, or Via is classified as network traffic — it meets the sign-in gate and the rate limit, not the operator's chair. Funnel, Serve, and Caddy always stamp X-Forwarded-For, so their clients hit the gate even in the misconfiguration above. It is a tell, not a proof: a proxy configured to strip its forwarding headers is indistinguishable from your own browser, so the non-loopback target is still the deployment to run.

Acceptance: unauthenticated GET / through the public URL must be 403. 200 harness HTML means the proxy is coming from loopback and hiding its forwarding headers — move its target off loopback.

Funnel HTTPS terminates TLS at the edge. Run tls: off behind it. The QR from http://127.0.0.1:3443/relay/pair encodes that loopback origin; pair from outside by typing the public https:// name, not by scanning that QR. See #1.

Security model

Read docs/SECURITY.md for the full statement. The short version: signing in grants the same power as a shell on the host machine, because the agent runs commands there. Everything in this plugin follows from that.

Troubleshooting

Nothing answers, the connection times out. The firewall is dropping packets. On Windows, an unrecognised network goes into the Public profile, which blocks inbound TCP:

New-NetFirewallRule -DisplayName "DSH Relay" -Direction Inbound `
-Action Allow -Protocol TCP -LocalPort 3443 -Profile Private,Domain

Set the network to Private. If it still times out, check the router for AP/client isolation — guest SSIDs almost always have it.

Connection refused. Nothing is listening. Confirm the relay started (netstat -ano | findstr 3443) and that --no-relay is not in effect.

403 from the relay. The Host you reached it by is not trusted. Connect by an IP literal the relay derived itself, or add the name to publicHostnames.

The plugin refuses to start, naming the webserver row. You still have the old LAN patch that binds the harness to 0.0.0.0. Remove it — the relay cannot protect a server that is already answering the network.

DSH Mobile says "the harness rejected this address". That is a 403. Either the address grant expired or the phone's address changed; pair again from the phone's browser.

The page loads but the sidebar stays empty, and the console repeats connection lost, retry #N. Browsers expose crypto.randomUUID only over HTTPS or on localhost, and the harness's browser client mints every RPC id with it — so over plain HTTP from a LAN address the readiness handshake throws and both event sockets are closed before they open. Unary calls still work, which is why sign-in looks fine. The relay ships a shim for this in the index document, so if you still see it the page did not come from that document: hard-reload past a cached copy, and check that nothing in front of the relay is serving its own index.html. Browsing over TLS or from 127.0.0.1 avoids it outright.

Every request is refused, including /relay/health, and the log says untrusted-host. The relay answers only to loopback, its own addresses, and whatever publicHostnames names — so an address it does not know itself by refuses everything, even the unauthenticated liveness probe, and the app reports a running relay as missing. The log line names the Host it refused; add that to publicHostnames. The Android emulator's 10.0.2.2 is the one exception, admitted automatically from a loopback peer since 0.2.1.

Everything is refused with 401, or the app reports a stream that would not open. The relay could not mint a harness browser session. Harness 0.1.2 authenticates its whole /api surface, and the relay signs a cookie using the harness's own durable secret at client-connection/browser-session — which the harness creates the first time dsh web runs. Start dsh web once, then reload the plugin; the relay logs a line at startup when it could not find that secret.

The model picker is empty, and settings pages say "settings are unavailable in this browser". Expected on any address but loopback, over TLS as well, and not something the relay can fix from where it sits. The harness's browser client decides whether the configuration plane exists by reading location.hostname; on a LAN address it creates the settings mirror in memory and never sends the calls — which this relay would have carried, since privilegedMethods defaults to letting an authenticated client through. Sessions, workspaces, and chat are unaffected. Reach settings, the provider directory, and model discovery from a browser on the machine running the harness. The fix belongs upstream, in the client rather than the relay; see #4.

Development

pnpm install
pnpm build        # tsc -b, then tsdown
pnpm test
pnpm typecheck

Point a source checkout at a running harness with an overlay that names the
built entry points directly:

- insert:
- id: relay
name: 'file:///D:/path/to/deepseek-harness-relay/lib/index.js'
config:
stateDir: 'D:/path/to/scratch/relay-state'
tls: 'off'
dsh web --patch ./dev.cordis.yml

On Windows the path must be a file:// URL, not a bare absolute path — the
loader hands it to the ESM resolver, which rejects a d: protocol.

Naming

The harness's brand guidelines ask ecosystem projects to use the DSH abbreviation rather than the full trademark in their names, and not to present official brand art. The npm package is dsh-relay and the pages carry this project's own mark.

License

MIT

常见问题

8 问
答案由本站依据条目字段整理,不引用上游原文
deepseek-harness-relay 怎么安装?

在 DSH 里执行 `dsh plugin --profile web add [email protected]` 即可。对应的 npm 包名是 `dsh-relay`。

deepseek-harness-relay 的验证等级是什么意思?

L5 · 运行验证通过。在 dsh 沙盒中完成安装与运行冒烟——装得上、跑得起来

deepseek-harness-relay 安全吗?

低风险。vet 静态分析未发现可疑行为,判定为干净(clean)。

deepseek-harness-relay 支持哪些 DSH 版本?

上游记录的兼容版本包括 0.2.0-rc.2、0.2.0-rc.1、0.1.7-rc.2、0.1.7-rc.1、0.1.7-alpha.2、0.1.6-alpha.2、0.1.6-alpha.1、0.1.5-rc.2。版本较多,只列出前 8 个。

deepseek-harness-relay 需要什么运行环境?

Node.js ^22.19.0 || >=24;需要同时安装 @deepseek-ai/cordis@>=4.0.1、@deepseek-ai/dsh-client-ui-settings-plugins@>=0.1.0-rc.7、@deepseek-ai/dsh-host-webserver@>=0.1.0-rc.7。环境不满足时安装会失败,建议先确认本机版本。

deepseek-harness-relay 用的是什么许可证?

上游标注为 MIT。商用或二次分发前请到仓库确认许可证原文。

deepseek-harness-relay 有哪些功能?

TLS 加密远程访问、密码登录与设备配对、按设备撤销权限、mDNS 自动发现。

这条记录的数据可靠吗?

本站按仓库名去重后合并整理,字段以作者仓库与 npm 元数据为准;安装命令、仓库地址与 README 保持原文,带 L4 / L5 标记的安装命令已在隔离沙盒里真实跑通。

浏览器与联网
按星标排序,可作替代方案

dsh-our-free-model

dsh-our-free-model

在 DeepSeek Harness 中装上这个 DSH plugin,无需登录、注册或填 API Key,即可直接调用 DeepSeek

L3 · 安装规范通过 8,796 星

dsh-web

dsh-web

侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。

L3 · 安装规范通过 8,549 星

BrowserSkill

BrowserSkill

BrowserSkill 的 DeepSeek Harness 浏览器自动化桥接插件,通过原生浏览器工具控制可见的 Chrome 和 Edg

L4 · 沙盒安装通过 8,456 星

dsh-pocket

dsh-pocket

DSH Pocket 让 DeepSeek Harness 装进口袋:电脑跑 dsh web,手机扫码即实时同步访问(局域网+公网),人在外

L5 · 运行验证通过 1,656 星

Browser4

Browser4

L4 · 沙盒安装通过 1,152 星

agentrq

agentrq

L4 · 沙盒安装通过 1,140 星

sorsama 的其它插件

1 个

插件的元信息来自作者仓库;分类、验证等级与安全结论的中文说明为中文整理,最近核验 2026-10-08。