为 DeepSeek Harness Web 提供带鉴权的反向代理:通过公网隧道或局域网访问时保持设置、凭据、目录浏览等特权接口可用,按设备管理登录会话。
- 语言
- TypeScript
- License
- MIT
- 分支
- main
安装
$ dsh plugin --profile web add dsh-full-remote在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程
对话式安装
帮我安装 DeepSeek Harness 插件 JUANWANG-BUAA/dsh-full-remote:先查看仓库 https://github.com/JUANWANG-BUAA/dsh-full-remote 确认安全性,然后执行安装命令并验证插件加载成功。
把这段指令粘贴给 DSH Web GUI 里的助手,由它代你完成安装与验证。
一句话定位
为 DeepSeek Harness Web 提供一层带访问令牌和设备会话校验的反向代理,让 Web 界面可以通过公网隧道、临时 cloudflared 隧道或局域网 IP 在其他设备上使用,同时保持设置、凭据、目录浏览等特权接口可用。
核心能力
- 反向代理 + 头部改写:把请求的
Host和Origin改写为127.0.0.1,使经过隧道的请求也能通过 DSH 自身的回环信任校验 - 访问令牌与一次性邀请:192 位访问令牌保存在权限
0600的状态文件;面板可生成 15 分钟有效、单次使用的二维码邀请链接,邀请中不含长期令牌 - 按设备会话管理:每台登录设备获得独立的会话凭据(cookie 里只有哈希),可在本地面板重命名、撤销、查看登录 IP 与最近活跃 IP
- 一键 Cloudflare 临时隧道:面板内启动 trycloudflare.com 隧道,免账号、免公网 IP,cloudflared 二进制按需下载并 SHA256 校验
- 可选安全开关:首访审批、CIDR 远程 IP 白名单、固定延时 + 按 IP 锁定登录失败次数、WebSocket 升级失败限流
- 响应优化:对可压缩的 HTTP 响应(HTML/JS/CSS/JSON/SVG)做 gzip,对带内容 hash 的
/assets/*加上 immutable 长期缓存 - 移动端适配:手机端的工具审批、
ask_user_question选项、计划评审以底部抽屉形式呈现,不需切回宿主机 - 健康检查与审计日志:提供
/_dsh_reverse_proxy/healthz,所有登录、审批、撤销、令牌轮换、启停、WebSocket 拒绝事件写入 JSONL 审计日志(超 8 MB 自动轮转)
技术实现
- 语言: TypeScript(双端:Node host + Web client)
- 关键依赖:
@deepseek-ai/schemastery(配置 schema 与运行时校验)、uqr(二维码生成)、@deepseek-ai/cordis(宿主框架)、@deepseek-ai/dsh-client-runtime+@deepseek-ai/dsh-client-ui-slots(Web 端 React UI 插槽) - 架构模式: Cordis 双端插件。host 端通过
cordis.patch.yml插入reverse-proxy行(id 冻结),并禁用 DSH 自带的自适应目录选择器(默认改用应用内浏览);client 端通过settings.sectionslot 注册「反向代理」设置页(顺序 30,排在 General / Models / Plugins / Agent presets 之后),并注册shell.overlay提供远程确认浮层 - 入口文件:
src/index.ts:48-52(host,apply(ctx, config)注册/dsh-reverse-proxy/*控制路由并通过tapIndex注入页面引导),src/client/index.ts:32-156(client)
适用场景
经常需要在另一台设备(手机、另一台电脑)上使用 DSH Web 但又不想只用回环地址的人:用 cloudflared 临时隧道在家里调试、用局域网 IP 让手机走同一 WiFi 直连、或者在外用 SSH/ngrok/frp 隧道远程处理工作会话。插件把 DSH 默认只在回环信任的特权接口开放给经过鉴权的远程客户端,避免被 403 拦截。
前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DeepSeek Harness | >=0.1.0-rc.5 <0.2,已在 0.1.1-rc.1 验证 | 必须使用 web profile(依赖 webServer 服务),不适用于 headless profile |
| Node.js | `^22.19.0 | |
| 平台 | macOS、Linux、Windows x64 | 主机跨平台;cloudflared 二进制支持 darwin-x64/arm64、linux-x64/arm64、win32-x64;Windows ARM64 无官方 cloudflared,需自行安装并通过 cloudflaredPath 指定 |
| 原生模块 | 无 | 纯 Node.js http/https/child_process/zlib,无 node-gyp 编译依赖 |
安装方式
dsh plugin --profile web add github:JUANWANG-BUAA/dsh-full-remote
配置项
src/config.ts 定义的 Schemastery schema 完整配置;下表列出常用项,所有未列出字段保留默认值。
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
listenHost | 字符串 | 代理监听地址;0.0.0.0 / :: 表示绑定全部网卡但不是可打开的地址 | 127.0.0.1 |
listenPort | 数字(0-65535) | 代理监听端口;0 表示自动选择空闲端口 | 3081 |
backendHost | 字符串 | DSH Web 后端地址,必须保持回环(127.0.0.1/localhost/::1),否则加载时拒绝 | 127.0.0.1 |
backendPort | 数字 | DSH Web 后端端口;0 表示跟随 webServer.port | 0 |
approvalMode | 布尔 | 新设备首次访问是否需在本地面板手动批准 | false |
allowedCidrs | 字符串数组 | 远程 IP 白名单(CIDR 或单 IP),空表示允许任何已认证 IP,回环始终放行 | [] |
trustForwardedFor | 布尔 | 是否在直连 peer 为回环时信任 X-Forwarded-For 最右值作为真实客户端 IP(CIDR / 限流 / 审计) | false |
trustCloudflareConnectingIp | 布尔 | 是否同时信任 Cloudflare 的 CF-Connecting-IP,需先开启 trustForwardedFor | false |
compressResponses | 布尔 | 是否对可压缩的 HTTP 响应做 gzip(SSE/WebSocket/字体/<1KB 响应不压缩) | true |
cacheHashedAssets | 布尔 | 是否对带内容 hash 的 /assets/* 加 immutable 长期缓存 | true |
auditLog | 布尔 | 是否记录登录、审批、撤销、令牌轮换等 JSONL 审计事件 | true |
allowTokenRead | 布尔 | 是否允许通过回环接口的 GET /token 读取访问令牌(轮换时仍返回新令牌) | false |
sessionIdleSeconds | 数字 | 设备会话空闲超时秒数,0 表示关闭 | 0 |
maxRequestBytes | 数字 | 请求体上限,默认 160 MiB 与 DSH Web /api 桥一致 | 160 MiB |
requestTimeoutMs | 数字 | 整个请求(头+体)超时,默认 5 分钟覆盖远程图片上传 | 300000 |
upgradeMaxAttempts / upgradeLockoutSeconds | 数字 | WebSocket 升级失败次数阈值与锁定时长 | 10 / 300 |
tlsCertFile / tlsKeyFile | 字符串 | 本地 TLS 证书 / 私钥(PEM),为空表示纯 HTTP;与快速隧道互斥 | "" |
cloudflaredPath | 字符串 | cloudflared 二进制路径,为空时按 PATH → 下载缓存查找 | "" |
stateFile | 字符串 | 持久化状态文件路径,为空使用 $DSH_HOME/reverse-proxy.json | "" |
另外可通过环境变量 DSH_FULL_REMOTE_USE_NATIVE_PICKER=1 让插件保留 DSH 原生的自适应目录选择器(默认禁用,否则远程添加工作区会触发宿主机原生对话框)。
常见问题
Q: 这个插件解决的是什么问题?
A: DSH 默认只在回环地址放行设置、凭据、目录浏览等特权接口,公网隧道或局域网访问时这些接口会被 403 拦截。插件在隧道和 Web 服务之间插入鉴权代理,改写 Host/Origin 为 127.0.0.1 让特权接口继续可用,并提供自己的访问控制层。
Q: 必须用 Cloudflare 快速隧道吗?
A: 不是。快速隧道只是面板里的一键选项,适合临时共享或救急;日常建议把已有的 cloudflared 命名隧道、ngrok、frp、SSH、Tailscale 指向 127.0.0.1:3081,或者把监听地址改成局域网 IP 让手机走同一 WiFi。
Q: 快速隧道每次启动 URL 都变吗?
A: 是的。trycloudflare.com 临时隧道每次启动随机分配地址,旧的邀请链接和已登录的设备会话都会失效。要稳定地址请用 named tunnel 或自建隧道。
Q: 可以只在局域网使用,不暴露公网吗?
A: 可以。把监听地址从 127.0.0.1 改成局域网 IP(如 192.168.x.x),手机在同一 WiFi 内直接访问;面板里可复制的「隧道目标」就是该地址。
Q: 远程打开设置页保存设置报「settings are unavailable in this browser」怎么办?
A: 升级到 DSH 0.1.1-rc.1(CHANGELOG 0.3.6 已修复),客户端页面引导会钉住 isLoopback。DSH 0.1.0-rc.8 起的官方 ModuleLoader create() 替换了 load,插件已适配,但更早版本会触发该报错。
Q: 怎样卸载?
A: 从 cordis.patch.yml 中删除 dsh-full-remote 对应的两层(reverse-proxy 插入和 directory-picker 行),重启 dsh web;状态文件 $DSH_HOME/reverse-proxy.json 可保留也可手动删除。
Q: 多设备同时登录会被互相踢下线吗?
A: 不会。每台设备登录后获得独立的设备凭据和会话 cookie,状态文件里只保存哈希;最多 16 台设备同时在线(可调 maxSessions),超过会淘汰最久未活跃的一台。
Q: 头像/图片从手机粘贴会被 413 拒绝吗?
A: 不会。默认请求体上限 160 MiB,与 DSH Web /api 桥保持一致,远程粘贴 deepseek-v4-flash-vision-exp 图片走同一条已认证 /api 路径不会被代理拦截。
上手难度
进阶 — 安装后基本可用,但要真正用稳需要理解 listenHost / backendHost / trustForwardedFor / quick tunnel URL 漂移 等概念;推荐先从默认 loopback + cloudflared 临时隧道起步,再视需求切换到 named tunnel 或局域网 IP。
已知问题与限制
cordis.patch.yml与其他 remote-access 插件(如deepseek-harness-auth)的directory-picker-browse插入可能冲突;本插件运行时只在该 id 不存在时插入,但仍应在组合前检查最终 loader 树backendHost必须是回环地址(127.0.0.1 / localhost / ::1),通配符或非回环值会在加载时被validateBackendHost拒绝allowTokenRead默认关闭,启用后任何能发本地控制头的进程都能读取长期访问令牌trustCloudflareConnectingIp必须在trustForwardedFor已开启时才允许开启(src/config-validation.ts:37-39)- TLS 证书 / 私钥必须同时配置,单独配置会被拒绝(
src/config-validation.ts:34-36) - 默认开启 cloudflared 临时隧道后,所有远程客户端对插件都呈现为 127.0.0.1,CIDR 白名单和按 IP 锁定实际作用于隧道整体;只有显式开启
trustForwardedFor才能看到真实 IP - Cloudflare 临时隧道地址每次启动随机变化,旧邀请链接和登录会话全部失效;Cloudflare 自身对临时隧道有非 HTML 重内容限制;首次使用会按需下载 cloudflared(18-52 MB),Windows ARM64 无官方构建需用户自行安装并设置
cloudflaredPath - WebSocket / SSE 响应不做 gzip;登录 / 等待 / 设备主页等栅栏页也不压缩(每页约 1 KB 节省)
- DSH 0.1.0-rc.8 起的 ModuleLoader
create()替换load,需要插件在客户端引导中重新绑定isLoopback(CHANGELOG 0.3.6 已修复并继续在 0.1.1-rc.1 验证) - 同一台机器本地启动的隧道使所有远程用户共享一个回环限流桶,必须开启
trustForwardedFor才能区分 - 控制操作(启动 / 停止 / 查令牌 / 改监听地址)只能从本机 Harness 窗口执行,隧道 URL 上无法调用
- 多插件组合时建议跑
pnpm run test:composition校验 row id 冲突,并参考docs/compatibility.md的运行时验收清单
Listed in awesome-dsh-plugin · DeepSeek Harness plugin
English | 中文
dsh-full-remote is a plugin for
DeepSeek Harness. It
places an authenticated reverse proxy in front of the Harness Web server,
so the Web UI can be used through a public tunnel or from a device on the
local network while privileged APIs such as settings, credentials, and
directory browsing remain available.
60-second quick start
dsh plugin --profile web add dsh-full-remote
dsh --profile web
In Settings → Reverse proxy, press Start proxy, then Start Cloudflare quick tunnel and scan the generated QR code. The invite is one-time and never contains the standing access token. For a controlled network, point an existing SSH, frp, ngrok, Tailscale, or cloudflared tunnel at the proxy target shown in the panel instead.
The quick tunnel is optional and temporary, not a managed production deployment. Read Security model before exposing a listener to the Internet. For composition details, see Compatibility and composition.
| Desktop control panel | Mobile workspace |
|---|---|
![]() | ![]() |
| Phone confirmation sheet | Remote desktop confirmation |
|---|---|
![]() | ![]() |
Problem
DeepSeek Harness binds its Web server to a loopback address and only
accepts privileged requests when the Host and Origin headers refer to
a loopback address. When the UI is reached through a generic tunnel, these
headers carry the public hostname and the trust check fails. The page
loads, but the following methods return 403:
settings.*credentials.*host.listDirectory
| Approach | Result |
|---|---|
Generic tunnel (SSH port forward, Caddy, binding 0.0.0.0) | Page loads; settings.* / credentials.* / host.listDirectory return 403 |
| LAN-only plugin without authentication | Usable on the local network; not suitable for public exposure |
| Password prompt without header rewriting | Requests are authenticated, but the privileged APIs remain blocked |
Solution
The plugin inserts a reverse proxy between the tunnel and the Harness Web server. The proxy:
- rewrites
HostandOriginto127.0.0.1before forwarding, so the privileged APIs pass Harness's trust check; - requires an access token or a valid device session before any request is forwarded;
- forwards HTTP, SSE, and WebSocket traffic; compressible HTTP responses may be gzipped (not SSE or WebSocket);
- provides a settings page (Settings → Reverse proxy) for starting and stopping the proxy, changing the listen address, rotating the token, and managing device sessions.
Because the rewrite disables Harness's original trust check for remote clients, the plugin provides its own access-control layer in its place. This layer is described under Security model.
The plugin can optionally start a temporary Cloudflare quick tunnel. Any managed tunnel (cloudflared, ngrok, frp, SSH, Tailscale) can also point at the local endpoint it publishes.
How it works
flowchart LR
A[Phone or remote browser] --> B[Public tunnel<br>cloudflared / ngrok / frp / SSH]
B --> C[dsh-full-remote<br>127.0.0.1:3081<br>authentication + header rewrite]
C --> D[DeepSeek Harness Web<br>127.0.0.1:3080]
- The remote browser connects to the public tunnel, which forwards to the
plugin's listener (
127.0.0.1:3081by default). - A request is accepted only with an access token, a valid one-time invite, or an existing device session. Requests that fail authentication do not reach the backend.
- The proxy rewrites
Host/Originto loopback, removes untrusted headers, and forwards the request to the Harness Web server at127.0.0.1:3080. Compressible HTTP responses (HTML/JS/CSS/JSON/SVG, ≥1 KB) may be gzipped; SSE and WebSocket are not. Hashed/assets/*files may receive a long-cache header. See HTTP gzip.
Features
Privileged APIs
settings.describe/update/replace/mutatecredentials.describe/set/unsethost.listDirectory/pickDirectory/openPathagentPreset.*,llm.discoverModels
Access control
- 192-bit access token, stored in a state file with mode
0600; reveal and rotation are performed from the local panel - Per-device sessions: each login creates an independent device credential, and only a hash is persisted. Devices can be renamed or revoked from the panel, which also shows each device's source IP (at login and most recently seen).
- Optional first-visit approval: a new device waits on a page until it is approved from the local panel
- Phone invite: a QR code or a one-time link (single use, 15-minute expiry). Same-IP browser retries within 60 s reuse the original device session so a flaky tunnel dropping the redirect cannot deadlock the phone into the token form or spawn a duplicate device. The link does not contain the standing token.
- Fixed delay and per-IP lockout on failed logins
- Optional CIDR allowlist for remote IPs
- Optional
trustForwardedForto use real client IPs from a trusted local tunnel in CIDR / rate-limit / audit via its rightmostX-Forwarded-Forvalue;CF-Connecting-IPis a separate Cloudflare-only opt-in, and loopback or malformed forwarded values are never trusted
Operation
- Fence self-check: probes
settings.describewith the same Host/Origin rewrite the proxy uses - Structured JSONL audit log (login, approval, revocation, token rotation, start, stop, WebSocket open/deny/reject) with an in-panel viewer for recent events and JSON export; rotates past 8 MB, keeping one previous generation
- Runtime listen-address changes with automatic rollback when a bind fails
- Optional local TLS (
tlsCertFile/tlsKeyFile) - Health endpoint at
/_dsh_reverse_proxy/healthz - WebSocket upgrade rate limiting: repeated failed upgrades are locked out per remote IP
- Stream-level request body limit; hop-by-hop and spoofable headers are
stripped; upstream
set-cookieis removed - Gzip for compressible HTTP responses (JS/CSS/HTML/JSON/SVG) when the
client advertises gzip; SSE, WebSocket, fonts, gate pages, and bodies
under 1 KB are skipped. Measured first-load of the Harness shell:
−72.7% (1.29 MB → 351 KB).
vendor-*.js−75.7%. Tiny JSON grows, so it is not compressed. Issue #11's "95%+" is not a general result. Off:compressResponses: false. Details: HTTP gzip - Long-cache
Cache-Controlon hashed/assets/*(notindex.htmlor/api). Off:cacheHashedAssets: false
One-click public tunnel (Cloudflare quick tunnel)
- Start a cloudflared quick tunnel from the panel (free, no account) and
get a
https://…trycloudflare.comaddress — no public IP or port forwarding required - Binary resolution:
cloudflaredPath→ PATH → a pinned (2026.8.2), SHA256-verified download cache; failed checksums are discarded - While the tunnel is up, forwarding-header trust applies dynamically (rate limiting / CIDR / audit see real client IPs) and reverts when the tunnel stops; the tunnel forwards to the proxy listener, so the token gate, approval and audit all keep applying
- Invites automatically use the tunnel URL: start the tunnel, generate the QR, scan from the phone (the panel shows it and the Origin can still override it)
- Mutually exclusive with local TLS (the Cloudflare edge already provides HTTPS); the quick-tunnel address is random per start and is meant for temporary sharing / emergencies
Device home (opt-in)
- A second button on the login form opens
/_dsh_reverse_proxy/home: device facts (label, login IP/time, expiry estimate, security posture), self-rename, and self-logout (revokes only this device) - The default login landing stays
/; the original flow is unchanged
Mobile use
- Settings edits persist when the page is opened through a tunnel hostname
- Add workspace uses the in-app directory browser; no native dialog appears on the host display
- Tool approvals,
ask_user_questionoption lists, and plan reviews appear as a confirmation sheet on the remote page: a bottom drawer on a phone, a centered card on a wider remote window. You can choose and submit there; you do not have to go back to the host. The official composer still only sits on the current session - For a phone-friendly layout (full-width session area, directory drawer, adapted dialogs), pair it with a mobile-layout plugin such as dsh-web-mobile
Requirements
- Node.js
^22.19.0 || >=24 - A DeepSeek Harness web profile. The plugin depends on
webServerand is not intended for headless profiles. Verified against 0.1.0-rc.8 (npm dist-tagnext).
Installation
dsh plugin --profile web add dsh-full-remote
dsh --profile web
- Open
http://127.0.0.1:3080. - Open Settings → Reverse proxy (last entry in the left navigation).
- Press Start proxy and copy the local target.
- Point the tunnel at the target:
# Examples only. The plugin does not execute these commands.
cloudflared tunnel --url http://127.0.0.1:3081
ngrok http 3081
For devices on the same network, set the listen address to a LAN IP instead of using a tunnel.
The package was previously published as dsh-reverse-proxy; that legacy name
is deprecated. Install dsh-full-remote for new deployments.
Usage
Starting and stopping
On the settings page, press Start proxy to start the listener and Stop proxy to stop it.
Listen address
| Bind | Purpose |
|---|---|
127.0.0.1 (default) | The tunnel runs on the same machine |
192.168.x.x | A device on the same network, without a tunnel |
0.0.0.0 / :: | Bind every interface. This is not an address to open; the panel reports a separate reachable address. |
The listen address can be changed at runtime and persists across restarts. If a new address fails to bind, the proxy rolls back to the previous working address.
The copyable tunnel target (and any extra reachable URL the panel
lists) is what a remote client should open. Binding 0.0.0.0 only
listens; it is not a URL.
backendHost is the address the proxy connects to, not the address it
listens on. Keep it at 127.0.0.1.
Phone invite
The QR encodes a one-time login URL. Public / reachable Origin is the
host the scanning device will request: the tunnel's https://…, or the
LAN URL from the panel. Leave it empty only when the tunnel target above
is already that address.
Do not put 127.0.0.1 in Origin. That address is the Harness machine; a
phone would open its own loopback and never reach the proxy.
Then press Generate invite. After a scan (or opening the link) the login page submits once. The invite expires in 15 minutes, works once (same-IP retries within 60 s reuse the original session), and does not contain the standing token. Invites can only be generated while the proxy is running.
Upgrade
dsh plugin forwards to pnpm. If you installed with an exact pin such as
add [email protected], a bare update dsh-full-remote reports
Already up to date and stays on the old version. To jump to the latest npm
release:
dsh plugin --profile web update --latest dsh-full-remote
Then restart dsh web. --latest ignores the current range, installs the
newest version, and rewrites package.json. For a specific version use
dsh plugin --profile web update [email protected].
Screenshots
The gallery is hosted in the repository; the npm package keeps only runtime files and links back here so installation stays small.
Desktop
The full settings page: running status and fence self-check, listen address, recommended setup, tunnel target, one-click quick tunnel, one-time invite QR, access token, connected devices with source IPs (inline rename), and the audit viewer.

| One-time phone invite (QR) | Connected devices with inline rename |
|---|---|
![]() | ![]() |
Mobile
| Login page | Control panel | Add workspace |
|---|---|---|
![]() | ![]() | ![]() |
Remote confirmation
When the model asks a question, requests a tool approval, or presents a plan review, the remote browser shows its own sheet. You do not have to look at the host display.
| Phone bottom sheet | Remote desktop card |
|---|---|
![]() | ![]() |
Gate pages
The token login (with an opt-in Device home button), the device home itself, and the first-visit approval wait page.
| Device home | Waiting for approval |
|---|---|
![]() | ![]() |
Configuration
Common options:
- id: reverse-proxy
name: dsh-full-remote
config:
listenHost: 127.0.0.1
listenPort: 3081
approvalMode: false # true: approve each new device locally
allowedCidrs: [] # e.g. ["192.168.1.0/24"]; empty: any IP after login
trustForwardedFor: false # true: trust rightmost X-Forwarded-For from a trusted local tunnel
trustCloudflareConnectingIp: false # true only with trustForwardedFor for a local Cloudflare connector
upgradeMaxAttempts: 10 # failed WebSocket upgrades before lockout
upgradeLockoutSeconds: 300 # lockout for repeated failed WebSocket upgrades
headersTimeoutMs: 15000 # timeout for request headers
requestTimeoutMs: 120000 # timeout for the complete request; effective value is >= headersTimeoutMs
sessionIdleSeconds: 0 # 0: off; otherwise idle timeout in seconds
auditLog: true
allowTokenRead: false # safer default; enable only for local token re-read
cloudflaredPath: "" # optional path to cloudflared for the one-click tunnel
tlsCertFile: "" # optional local HTTPS
tlsKeyFile: ""
compressResponses: true # gzip JS/CSS/JSON/HTML ≥1KB; skip SSE/WebSocket/fonts/gate pages
cacheHashedAssets: true # immutable Cache-Control on hashed /assets/* only
The complete option list, with defaults and validation, is defined in the
package Config schema (src/config.ts) and
src/config-validation.ts (source is not included in the published package).
Two points to note:
- Installing the plugin pins the in-app directory picker so that a phone
can add workspaces. By default the stock adaptive picker is disabled and
the browse pair is created at runtime unless another plugin already
inserted it. Set
DSH_FULL_REMOTE_USE_NATIVE_PICKER=1before boot only when you deliberately want the host's native chooser and do not need remote directory browsing. backendHostmust remain a loopback address. A wildcard or non-loopback value is rejected at load time.
Security model
The Host/Origin rewrite restores the privileged APIs and, at the same time, disables Harness's original protection for remote clients. The access-control layer provided by this plugin consists of:
- a 192-bit access token, stored locally with file mode
0600; - an
HttpOnly,SameSite=Strictsession cookie per device, carrying a per-device secret of which only a hash is stored; - a fixed delay plus a per-IP
429lockout on failed logins; - loopback-only control routes (
/dsh-reverse-proxy/*), which require a control header and are never forwarded through the public proxy; - removal of spoofable forwarding and hop-by-hop headers, so the proxy's own cookie never reaches the backend;
- optional
trustForwardedFor: when enabled, only a loopback peer's rightmostX-Forwarded-Forvalue is trusted for CIDR / rate-limit / audit.CF-Connecting-IPneeds the separate, Cloudflare-onlytrustCloudflareConnectingIpopt-in; loopback or malformed values are never trusted. Keep both disabled for direct LAN access.
The access token must be treated as a secret. Terminate TLS on the public
side of the tunnel. For LAN use without a tunnel, set
tlsCertFile / tlsKeyFile (for example with
mkcert).
Limitations
- Control actions (start, stop, reveal token, change listen address) can only be performed from the local Harness window, not from the tunnel URL.
- Settings persistence on a remote page relies on a temporary trust pin
until Harness provides a proper deployment trust field. On Harness
0.1.0-rc.8, that pin must survive the official ModuleLoadercreate()replacingload; otherwise Settings → Models showssettings are unavailable in this browser. "Open on host" from a phone acts on the machine running Harness. allowTokenReaddefaults tofalse. When explicitly enabled,GET /tokenis served over loopback HTTP, so any local process that sends the control header can read the token; rotation always returns the replacement token.- By default, a tunnel running on the same machine makes every remote
client appear as
127.0.0.1to the proxy.allowedCidrsand per-IP login lockout therefore apply to the tunnel as a whole unlesstrustForwardedFor: trueis set behind a trusted local edge. - The plugin replaces Harness's remote trust check with its own access-control layer. A defect in this layer has serious consequences. If Harness provides official remote access in the future, the role of this plugin should be reassessed.
- The one-click quick tunnel: the URL is random per start (old invites
and logins stop working), Cloudflare positions quick tunnels as
temporary/testing with terms limiting heavy non-HTML content, and the
first use downloads cloudflared on demand (18–52 MB depending on the
platform; Windows ARM64 has no official build — install it yourself
and set
cloudflaredPath). For a stable daily entry, bring your own frp / ngrok / named tunnel. - Gzip at the proxy helps LAN and SSH/frp. A Cloudflare quick tunnel already compresses HTML/JS/CSS/JSON at the edge, so that path sees little extra saving. Live model output uses WebSocket and is not gzipped. Plugin login/wait/home pages are not gzipped (about 1 KB of potential saving). Full contract: HTTP gzip.
Development
Build from source
pnpm pack
dsh plugin --profile web add ./dsh-full-remote-0.3.6.tgz
Git installs run the prepare build. On pnpm ≥ 10 allow it:
allowBuilds:
dsh-full-remote: true
Checks and CI
pnpm install
pnpm run check:ci
check:ci runs lint, typecheck, unit and client tests, and a build. CI
adds a real dsh plugin add smoke test against a live Harness
composition. .github/workflows/canary.yml runs a weekly smoke test
against the harness default-branch tip.
The loopback control API lives at /dsh-reverse-proxy/* and is never
forwarded through the public proxy. The settings page is the intended
interface; the raw routes are rarely needed. For example, recent audit
events can be read with GET /dsh-reverse-proxy/audit?limit=50&event=login.ok
from the local control surface.
Contributing · Security · License
查看使用指南 →
该插件的安装步骤、关键要点、FAQ 与兼容性说明(基于已收录字段派生)。
收录徽章
[](https://deepseek-plugin.org/plugins/JUANWANG-BUAA/dsh-full-remote)把这段 markdown 粘贴到你的 GitHub README,链接回本插件详情页。徽章只声明已被本站收录,不代表安全认证。








