🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

手机安全隧道 frederico-kluser/dsh-guard-messenger

DeepSeek Harness
frederico-kluser8 天前有提交已提供中文文档扫描:含敏感能力

用密钥链接在手机上安全访问本地编码界面

28.8/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/1 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

该插件未发布到 npm,按作者文档从 GitHub 源安装即可(静态检查只覆盖 npm 路径,不代表插件不可用)。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add frederico-kluser/dsh-guard-messenger
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · platform
装得上吗
本站已真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 8 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查需源码安装
要求 Node:>=24(dsh 本身要求 Node ≥ 22.19)

engines.node 要求 >=24,不满足 Node 22.19.0

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✗npm 包dsh-guard-messenger(未发布到 npm,仅可源码安装)
✗Node 引擎要求 >=24 · 基线 Node 22.19 不满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

源码安装时若 pnpm 拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑。

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/9/24 04:24:16

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 37 处,展示前 37 条)
  • 发起网络请求
    lib/client.js:1009
    const resposta = await fetch(API_BASE + "/csrf", {
  • 发起网络请求
    lib/client.js:1025
    const resposta = await fetch(API_BASE + caminho, {
  • 发起网络请求
    lib/client.js:1039
    resposta = await fetch(API_BASE + caminho, {
  • 写入剪贴板
    lib/client.js:1200
    await navigator.clipboard.writeText(bloco.comando);
  • 写入剪贴板
    lib/client.js:1372
    await navigator.clipboard.writeText(props.estado.codigo);
  • 写入本地文件
    dist/audit/log.js:182
    mkdirSync(dir, { recursive: true, mode: AUDIT_DIR_MODE });
  • 读取本地文件
    dist/audit/log.js:294
    const st = lstatSync(dir);
  • 读取本地文件
    dist/config/assert.js:310
    isDirectory = statSync(value).isDirectory();
  • 读取本地文件
    dist/agents/worktrees.js:154
    return existsSync(caminhoDe(nome));
  • 读取本地文件
    dist/agents/worktrees.js:173
    if (existsSync(caminho)) {
  • 读取本地文件
    dist/telegram/onboarding.js:361
    return readFileSync(fd, 'utf8');
  • 写入本地文件
    dist/telegram/onboarding.js:425
    renameSync(tmp, ctx.caminhoSecrets);
  • 删除本地文件
    dist/telegram/onboarding.js:429
    unlinkSync(tmp);
  • 读取本地文件
    dist/state/store.js:120
    text = readFileSync(fd, 'utf8');
  • 删除本地文件
    dist/state/store.js:145
    unlinkSync(tmp);
  • 写入本地文件
    dist/state/store.js:221
    renameSync(tmp, paths.file);
  • 读取本地文件
    dist/state/paths.js:183
    let stat = lstatSync(paths.dir, { throwIfNoEntry: false });
  • 写入本地文件
    dist/state/paths.js:188
    mkdirSync(dirname(paths.dir), { recursive: true });
  • 写入本地文件
    dist/state/paths.js:189
    mkdirSync(paths.dir, { recursive: true, mode: STATE_DIR_MODE });
  • 读取本地文件
    dist/state/paths.js:190
    stat = lstatSync(paths.dir);
  • 写入本地文件
    dist/state/paths.js:201
    chmodSync(paths.dir, STATE_DIR_MODE);
  • 读取本地文件
    dist/tunnel/supervisor.js:252
    const conteudo = readFileSync(file, 'utf8').trim();
  • 写入本地文件
    dist/src/audit/log.js:182
    mkdirSync(dir, { recursive: true, mode: AUDIT_DIR_MODE });
  • 读取本地文件
    dist/src/audit/log.js:294
    const st = lstatSync(dir);
  • 读取本地文件
    dist/src/telegram/onboarding.js:361
    return readFileSync(fd, 'utf8');
  • 写入本地文件
    dist/src/telegram/onboarding.js:425
    renameSync(tmp, ctx.caminhoSecrets);
  • 删除本地文件
    dist/src/telegram/onboarding.js:429
    unlinkSync(tmp);
  • 读取本地文件
    dist/src/state/paths.js:183
    let stat = lstatSync(paths.dir, { throwIfNoEntry: false });
  • 写入本地文件
    dist/src/state/paths.js:188
    mkdirSync(dirname(paths.dir), { recursive: true });
  • 写入本地文件
    dist/src/state/paths.js:189
    mkdirSync(paths.dir, { recursive: true, mode: STATE_DIR_MODE });
  • 读取本地文件
    dist/src/state/paths.js:190
    stat = lstatSync(paths.dir);
  • 写入本地文件
    dist/src/state/paths.js:201
    chmodSync(paths.dir, STATE_DIR_MODE);
  • 读取本地文件
    dist/src/state/store.js:120
    text = readFileSync(fd, 'utf8');
  • 删除本地文件
    dist/src/state/store.js:145
    unlinkSync(tmp);
  • 写入本地文件
    dist/src/state/store.js:221
    renameSync(tmp, paths.file);
  • 读取本地文件
    dist/proc/introspect.js:29
    return readFileSync(`/proc/${String(pid)}/cmdline`, 'utf8').replaceAll('\0', ' ').trim();
  • 读取本地文件
    dist/proc/introspect.js:95
    raw = readFileSync(`/proc/${String(pid)}/stat`, 'utf8');
扫描于 2026/11/1(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/cosmokit@deepseek-ai/dsh-home-paths@deepseek-ai/dsh-host-frontend-static@deepseek-ai/dsh-host-webserver@deepseek-ai/dsh-invariants@deepseek-ai/dsh-subprocess@deepseek-ai/schemastery
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 密钥链接访问
  • 手机访问本地编码界面

DeepSeek Harness 的 Cordis 插件:通过 Telegram 远程控制,带认证隧道、智能体聊天、worktree 和任务指标。

README

由 DeepSeek 最新模型翻译生成
dsh-guard-messenger dsh-guard-messenger 用手机跑你自己的 DeepSeek Harness——完整的 Web UI,真能写代码——而且绝不把监听地址扩到 loopback 之外:隧道终点落在 127.0.0.1(本地访问直接打开),走隧道进来的只有拿着链接里 ?key= 密钥的人(密钥由 bot 发出)——访问开关在 Telegram 里控制。 Demo 安装(一行命令) dsh plugin --profile web add dsh-guard-messenger 通过仓库链接安装 dsh plugin add github:frederico-kluser/dsh-guard-messenger 不需要 allowBuilds,安装时也不构建:编译产物(dist/、lib/)已提交进 git,prepare/prepack 钩子被特意移除——pnpm 11 会拦截 git 依赖的构建(ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED),而这正是之前挡住链接安装的原因。 发布说明: 这条命令要等当前分支推送到 GitHub 之后才生效。只要仓库还在提供旧的文件树(仍带 prepare 的那份),pnpm add github:…
展开完整文档(README 剩余 1 节)
dsh-guard-messenger

dsh-guard-messenger

用手机跑你自己的 DeepSeek Harness——完整的 Web UI,真能写代码——而且绝不把监听地址扩到 loopback 之外:隧道终点落在 127.0.0.1(本地访问直接打开),走隧道进来的只有拿着链接里 ?key= 密钥的人(密钥由 bot 发出)——访问开关在 Telegram 里控制。

Demo

安装(一行命令)

dsh plugin --profile web add dsh-guard-messenger

通过仓库链接安装

dsh plugin add github:frederico-kluser/dsh-guard-messenger

不需要 allowBuilds,安装时也不构建:编译产物(dist/、lib/)已提交进 git,prepare/prepack 钩子被特意移除——pnpm 11 会拦截 git 依赖的构建(ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED),而这正是之前挡住链接安装的原因。

发布说明: 这条命令要等当前分支推送到 GitHub 之后才生效。只要仓库还在提供旧的文件树(仍带 prepare 的那份),pnpm add github:… 就会报 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED。

用 tarball 安装: 编译产物(lib/client.js 和 dist/)已提交进仓库(正因如此,链接安装才能不用 allowBuilds),prepublishOnly 仍作为发布钩子保留。缺了这些产物,客户端激活时会抛 MissingClientBundleError。见 docs/PANEL-TELEGRAM.md。

威胁模型,5 行讲清——先看这个,再看功能

这个插件按设计会暴露一个能在你机器上执行代码的智能体。继续之前,先读这段:

1. TLS 在 Cloudflare 边缘终止。 明文(提示词、代码、回复)会经过第三方——WAF/Access/缓存正是靠这一点实现。不是端到端加密。
2. 隧道 URL 是公开的,不是秘密。 起保护作用的是链接里的 ?key= 密钥,不是地址的隐蔽性。
3. Cloudflare Access 不能挡在 quick tunnel 前面。 在 .trycloudflare.com 上,所有认证都必须做在应用内部。
4. quick tunnel* 没有 SLA——它「intended for testing and development only」。
5. 这不是「不用想就默认安全」。 我们缩小了暴露面,用密钥/会话认证了边界,也给了你关掉的开关;但没有消除这一类风险。

细节以及每项缓解措施的作用:docs/THREAT-MODEL.md。

安全模型(代码实际保证了什么)

对保证的诚实解读——每一行都指向兑现它的代码;这里没有任何一条是「不用想就默认安全」,只是缩小暴露面加认证。

| 属性 | 实现方式 |
| bind 永远不会被放宽 | assertSecureBind 在加载时拒绝 0.0.0.0/::,失败会大声报错(src/config/bind.ts) |
| 本地访问直接放行 | 在 127.0.0.1 上 DSH 不设屏障直接响应;防护针对的是隧道暴露面,不是 loopback(src/tunnel/proxy.ts) |
| 走隧道只认会话或链接里的密钥 | 代理认证一切:会话(cookie)或链接里的 ?key=;除此之外 → 401,不带挑战(src/http/gate.ts、src/session/link-token.ts) |
| 先看 Origin 和 Host | trustedRemotes(无凭据返回 403),Host 逐字节比对,防 DNS rebinding(src/http/gate.ts:15、src/http/host-header.ts) |
| 链接里的密钥可复用,靠轮换吊销 | CSPRNG 256 位;只存 SHA-256 摘要;可一直复用到 /rotacionar(生成新密钥,作废会话并切断活动连接——WebSocket 和在途流当场断开)或关掉隧道(src/session/link-token.ts、src/tunnel/proxy.ts) |
| 401 不带挑战 | 纯文本,没有登录挑战(浏览器弹窗已移除)——绝不在提示词/表单里要密码(src/http/responses.ts、denyUnauthorized) |
| 暴力破解有上限 | 第 5 次失败开始延迟;累计 100 次失败会关掉暴露(受限模式),只有 loopback 能过,重启也绕不过去(src/ratelimit/) |
| 密钥校验用恒定时间 | 摘要用 timingSafeEqual 比对;token 在 JSON/inspect 里打码(src/session/link-token.ts、test/security/timing-constante.test.ts) |
| danger-full-access 被否决 | 禁止提权,作为纵深防御直接拒绝(src/permissions/deny.ts) |
| 只有配对的所有者能指挥 bot | 双轴 allowlist(userKey/chatKey),默认拒绝(worker/surface/auth.ts) |
| 不保证什么 | TLS 在 Cloudflare 边缘终止(明文会经过那里);隧道 URL 不是秘密;拿到链接的人在轮换前都能访问;?key= 走 query(中间人可见)——这是所有者接受的取舍;prompt injection 仍然接受——设计决策见 docs/THREAT-MODEL.md §4 和 §5.1 |

核心矛盾,我们先说,免得别人替我们说

项目当初就是为了把 DSH 锁在 loopback 上。这个插件又把它暴露到互联网。看起来矛盾——诚实的说法是这样: bind 仍然在 127.0.0.1,DSH 在那里直接打开(免登录)。变化在于多了一个受监管的子进程(cloudflared),它把 Cloudflare 边缘的流量送到一个专用代理,由这个代理在边缘完成认证(会话或 ?key=)。这和 --host 0.0.0.0 不是一回事:本地 socket 从不放宽,暴露是 opt-in、临时的,一条命令就能撤销——负责保护 /api、SPA 回退和 WebSocket 握手的,是这个代理,不是 loopback。

不变的是:逻辑攻击面变大了。以前攻击者要拿到机器访问权;现在走隧道,他要拿到链接里的密钥(或一个被窃取的会话)。我们把「不可达」换成了「可达,且在边缘完成认证」——不用登录,不用输密码。这个交换随时可以撤销,用关闭按钮就行(在 DSH 设置的「Remote Access」标签页,或 bot 里的 /desligar;换密钥用 /rotacionar)。

不接受这个交换的人,应该用 Tailscale 或 SSH——我们在 docs/TUNNEL.md 和下面的「什么时候不要用」一节里,把这点讲得更从容。

它做什么(以及为什么)

1. 保护隧道,不保护 loopback。 DSH 在 127.0.0.1 上直接打开(免登录);对外暴露的是专用代理,它认证所有从互联网进来的请求——/api、SPA 回退和 WebSocket 握手。加载时拒绝 loopback 以外的 bind 地址,也拒绝被禁的权限(danger-full-access)。这解决了上游讨论 #853 里的攻击面问题。
2. 从不向任何人要密码。 隧道访问靠会话或链接里的密钥 ?key=(CSPRNG,256 位,摘要落盘)。密钥可重复使用,直到 /rotacionar(生成新密钥,让会话失效,并主动断开已打开的连接——拿着旧链接的人当场掉线,包括 WebSocket)或关掉隧道。401 不带登录挑战——没有提示,也没有登录表单。
3. 起一条临时隧道,方便你用手机访问,TTL 到点自动关掉,还有一个 probe fail-closed 防止出现「裸」隧道(后面没有认证代理)。
4. 用 Telegram 或 DSH 设置的「Remote Access」标签页开关——手动 kill 开关。
5. 通过 bot 触发 harness 的智能体——给一个 allowlist 里的技能和一段提示词,所有者就能让 DeepSeek Harness 的子代理在本机干活(/agente),跟踪运行(/agentes),取消(/parar-agente)。dispatch 会在主机上执行代码:需要两步确认,智能体以 harness 的权限运行——绝不用 bot 的 token。完整手册:docs/AGENTS.md。

Bot 与隧道:DSH 设置的「Remote Access」标签页

bot 和隧道的控制就在 DSH 的 UI 里——设置弹窗的 「Remote Access」 标签页(插件注册到 settings.section 槽位;没有 注入首页的代码块)。标签页里显示机器人状态芯片 OFFLINE/ONLINE,与运行时保持一致;还有 token 表单、通过面板配对、隐私设置,以及 「隧道」卡片——实时状态(desligado/ligando/online/instável/desligando/falhou)、READY 时的 URL、「expira em」倒计时、尝试次数,以及按钮 Ligar túnel(两步确认)、Desligar túnel(确认)和 Repor (após falha)(两步,仅在失败后出现)。

「Ver instruções」按钮(「Como ligar o bot」)向服务器请求当前服务商的步骤:
- OFFLINE → 连接说明(Telegram:在 @BotFather 里创建机器人)、dsh-guard-setup --pedir-token、--parear、发送 /parear <código>;照这个步骤走一遍,机器人就真的上线了;
- ONLINE → 使用提示。

配对完成后(docs/ONBOARDING-TELEGRAM.md),可用这些机器人控制命令:

| 命令 | 作用 |
| /ligar | 启动隧道,READY 后自动发送带 ?key= 密钥的链接 |
| /desligar | 关闭隧道(并吊销密钥) |
| /acessar | (重新)发送带密钥的链接 |
| /rotacionar | 生成新密钥,让会话失效,并断开活动连接(WebSocket/streams)——立即吊销旧访问权限 |
| /status · /emergencia | 状态、kill switch |
| /agente · /agentes · /parar-agente | 触发、列出和取消 harness 的智能体——见下文 |
| /novo-chat · /novo-chat-wt · /worktree · /status-tarefa | 任务:harness 的聊天和 worktree——见下文 |

/ligar 发送的链接是 https://<url-pública>/?key=<token>:链接里的密钥用于认证隧道,在轮换(或关闭隧道)之前可重复使用;密钥有效时会被换成会话,浏览器随后重定向到干净的 URL(不带 ?key=)。从不要求输入密码——访问靠会话或链接里的密钥,本地访问直接打开。

为什么密钥会出现在聊天里(这是预期行为): ?key= 是认证机制,机器人是投递通道。它是链接密钥,不是「永久密码」——访问时没有密码要输入。

上面每一条承诺都对应到实现它的代码行:见 docs/ARCHITECTURE.md。

智能体:通过机器人触发 harness 技能

智能体调度器(docs/AGENTS.md——完整手册)把机器人和 harness 的 ctx.subagents 连起来:所有者选一个允许列表里的技能,写一段提示词,宿主在本机启动一个进程内子代理(全新会话,零父上下文)干活,结果回到聊天里。

| 命令 | 作用 |
| /agente <skill> <o que o agente deve fazer> | 打开两步确认并触发智能体(显示的提示词就是实际要执行的) |
| /agentes | 列出各个 run(智能体、聊天和 worktree),附带摘要指标:• <id> — <skill> — <estado> <há quanto> · <kind> · wt: <worktree> · 📊 <tokens total> / <tempo>(结束后还会附上模型摘要) |
| /parar-agente <id> | 取消一个 run(id 会出现在 /agentes 里) |

为什么要这么多道手续:dispatch 会在你的机器上执行代码。这是最会放大 bot 暴露面的操作,所以要求两步确认(host nonce,和 /ligar 一样)。被触发的智能体以 harness 的权限运行,永远拿不到 bot 的 token,也拿不到本插件的任何凭据(S3):请求里只带渲染好的技能,放在规范的 <skill_content> 块中,再加上你提示词的文本。run 按设计就是临时的:只活在内存里,DSH 一重启就全部取消(dispose 按 LIFO 顺序执行),列表从空开始。没有声明 allowlist(config.agents.skills)时,任何智能体都无法触发,从构造上就是 fail-closed。

任务:通过 bot 操作 harness 的聊天和 worktree

任务类命令会在 harness 里创建真实的东西:/novo-chat 和 /novo-chat-wt 会打开 DSH 会话并提交提示词(聊天真的会跑起来),/worktree 会创建一个隔离的 git worktree,分支为 guard/<nome>,位置在 <repo>-worktrees/<nome>,与 checkout 平级,并且绝不破坏任何已存在的东西。/status-tarefa 显示某个 run 的详情,用的是 harness 的真实指标(token、耗时、轮次);host 没测到的字段显示为 —,绝不编造数字。

| 命令 | 作用 |
| /novo-chat <prompt> | 打开新聊天(真实的 DSH 会话)并提交提示词,需两步确认 |
| /novo-chat-wt <worktree> <prompt> | 同上,但聊天在已有 worktree 内启动(先用 /worktree 创建它) |
| /worktree <nome> [base] | 基于 base(默认 HEAD)创建 git worktree guard/<nome>,位置在 <repo>-worktrees/<nome>;如果已存在,不会破坏任何东西 |
| /status-tarefa <id> | 单个 run 的详情:完整行 + 12 个真实指标字段——token(输入、输出、缓存读取、缓存写入、解码数)、耗时(模型、工具、首 token、解码)和计数(轮次、步数、带首 token 的步数)——host 没测到的字段显示 — |

真实示例:

/worktree fix-guard-regex
/novo-chat-wt fix-guard-regex corrige o teste que falha em test/security/host-header.test.ts
/novo-chat resume o estado do repositório e diz o próximo passo
/agentes
/status-tarefa 01J8ZC2A

8 位 id 会出现在 /agentes 里(它列出智能体、聊天和 worktree,各带摘要指标)。和 /agente 一样,/novo-chat 和 /novo-chat-wt 会执行代码,/worktree 会改动机器,所以它们都要求两步确认:聊天会显示即将运行的确切提示词,/worktree 会显示名称(指定了 base 时也显示 base)。确切文本与架构: docs/AGENTS.md 和 docs/ux/01-CONTRATO-BOT.md。

消息服务商

bot 的 worker 对服务商保持中立:核心(路由器、allowlist、配对、outbox)放在 worker/surface/,每个渠道单独隔离在自己的适配器里:worker/providers/telegram/(唯一加载 grammY 的地方)。启动时通过 DSH_GUARD_PROVIDER 读取当前启用的服务商(config.worker.provider,默认 telegram);Telegram 的 token 是 TELEGRAM_BOT_TOKEN —— secrets.env 保存当前启用服务商的那一行,dsh-guard-setup 知道哪一行属于当前启用的服务商。

新增服务商(WhatsApp、Matrix……)就是实现中立契约 ProviderAdapter,再注册到 registry。完整手册——架构、逐类型契约、各服务商限制对照表,以及新增服务商的分步 checklist——见 docs/PROVIDERS.md,配套技能(skills)在 .agents/skills/dsh-provider-bot 和 .agents/skills/dsh-telegram-provider。

一个请求如何流转(8 行架构)

Telemóvel (navegador)
│ abre o link que o bot enviou: https://<url>/<tunel>/?key=<token> (a chave viaja em query)
▼
❨ Cloudflare edge ❩ TLS → HTTP/2 → WebSocket ; o TLS termina AQUI (texto claro passa pela borda)
▼
cloudflared — quick tunnel (conexão de saída apenas, sem conta, sem SLA) ▼ http://127.0.0.1:3080 (o túnel termina no loopback; o socket local NÃO é alargado)
plugin (proxy dedicado — só o túnel passa por aqui)
│ autentica na borda: sessão (cookie) ou ?key= válida → troca por sessão (302 p/ URL limpa)
│ 403 origem/ Host fora da allowlist · 401 SEM desafio (sem login) · 200 com sessão ▼ DeepSeek Harness Web UI — bind travado em 127.0.0.1 (nunca 0.0.0.0)

图里藏着的关键点:本地访问直接打开(没有屏障)——认证只存在于隧道代理里。链接里的 key 由本机生成(CSPRNG,256 位),落盘只存 digest,轮换前可重复使用,?key= 走 query(中间人可见)——这是 expose-port 模型接受的取舍。绑定到 loopback 之外会被拒绝,拒绝发生在加载时,报错很响——模块地图见 docs/ARCHITECTURE.md。

快速开始(5 条命令)

1. instala o plugin (uma linha; ativa o manifesto de Bundle automaticamente)
dsh plugin --profile web add dsh-guard-messenger

2. corre o DSH — o acesso local abre direto (sem login)
dsh web

3. (opcional) liga o bot: dsh-guard-setup + /parear (docs/ONBOARDING-TELEGRAM.md);
depois, no bot,
/ligar envia o link com a chave ?key=

4. confirma que o acesso local abre (o DSH responde direto); a borda sem chave
dá 401 — ver docs/INSTALL.md Passo 3
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3080/

5. para desligar/desinstalar
dsh plugin remove dsh-guard-messenger
examples/minimal 是可安装的最小示例,验收标准已写进文档:本地直接打开 / 边缘不带 ?key= 返回 401 / 带链接或会话返回 200 / 结束时不留任何进程。

什么情况下不要用

- 团队 / 多用户。 这是单所有者插件:一份 Telegram 的 userKey/chatKey 允许列表(数字 id,绝不用用户名),外加链接密钥。没有 RBAC,也没有多租户审计。
- 生产 / 可用性。 quick tunnel 按 Cloudflare 自己的说法是 "intended for testing and development only" 和 "We don't guarantee any SLA or uptime"。
- 需要合规的人。 TLS 在 Cloudflare 边缘终止,明文会经过那里。不是 E2E。
- 想要「不用想就默认安全」的人。 这里没有这种东西。你暴露的是一个带 shell 的智能体;插件缩小了攻击面,提供了 kill switch,但没有消除这一类风险。
- 公司机器。 trycloudflare.com 有记录在案的恶意软件声誉,很多网络 / EDR 会拦截或标记 cloudflared。

替代方案(诚实对比)

这里不把任何替代方案说成差;好几个在多个维度上更好。价值在于组合,而不在于逐项胜出。

| 替代方案 | 更擅长什么 | 代价 | 什么时候选它而不是这个插件 |
| Tailscale(+ Funnel) | 真正的私有网络(WireGuard),按设备识别身份 | 手机要装客户端,还要账号 | 几乎总是,只要你愿意装客户端 |
| ngrok | 上手即用;免费版就有边缘鉴权(quick tunnel 没有) | 免费版有限制;URL 会变 | 今天就要边缘鉴权时 |
| SSH + tmux | 不新增 HTTP 攻击面,真正的 E2E | 不是 Web UI;在手机上敲代码是受罪 | 只需要看日志、杀进程时 |
| code-server / VS Code tunnels | 浏览器里的完整编辑器 | 不是 DSH;并行运行 | 目标是编辑文件,而不是驱动智能体时 |
| dsh-webui-auth | 在传输层给 WebUI 做鉴权 | 只做鉴权;没有隧道、没有 bot、没有开关 | 只要鉴权就用它 |
| Named tunnel + Cloudflare Access | 鉴权在到达你机器之前 | 需要域名,且 DNS 在 Cloudflare | 只要有域名就用(更优路径) |
| 什么都不用(纯 loopback) | 零暴露风险 | 手机上用不了 | 确实不需要时 |

总之:这不是「又一个隧道」——它是单所有者的完整流程(onboarding → 链接里的密钥 → 临时隧道 → 手机上的链接 → 关闭按钮),bind 锁在 loopback,本地访问直接打开,不用登录。

兼容性

上游 @deepseek-ai/dsh 支持范围:0.1.0-rc.7 .. 0.1.1-rc.1(N/N-1 策略)。完整表格——插件版本 × rc 范围 × 状态——在 docs/COMPATIBILITY.md,该文件由 dsh-compat.yml 生成(绝不手改)。
注意 registry:@deepseek-ai/dsh- 子包的 latest 标签指向最旧的发布版本,不是最新的。请显式固定版本。

端到端验证(测试套件真正证明了什么)

下表从仓库里真实的测试套件整理而来 —— test/e2e/、test/security/、test/integration/ —— 每项都标注了对应文件。这些是 CI 实际跑的检查,不是空头承诺;每一层的细节和运行方式见 docs/TESTING.md。

| 检查项 | 预期结果 | 验证位置 |
| 本地(127.0.0.1)访问 /、/api/state、升级 | 直接打开,200/101,无挑战(DSH 在 loopback 上不受保护) | test/integration/http/barreira.test.ts(第 1 波) |
| 通过隧道 URL 发起请求,无会话且无 ?key= | 401 纯文本,无挑战(不弹登录框) | test/e2e/tunnel-cycle.test.ts、test/security/desafio-401.test.ts |
| 通过隧道 URL 携带有效 ?key=<token> | 302 跳转到干净 URL(+ 会话 Set-Cookie);token 不出现在日志里 | test/unit/http/gate.test.ts、test/unit/tunnel/proxy.test.ts |
| 通过隧道 URL 携带无效 ?key= | 401,无挑战,无 302 | test/unit/http/gate.test.ts、test/security/desafio-401.test.ts |
| 通过隧道 URL 携带有效会话(cookie) | 200,响应来自原始分发 | test/e2e/tunnel-cycle.test.ts |
| 来源异常的 WebSocket 握手(如 https://evil.com) | 拒绝 —— 精确匹配 Origin 白名单(CWE-1385) | test/security/websocket-origin.test.ts |
| Host 不是已发布的那个 / DNS 重绑定 | 在边界处返回 403,逐字节校验 | test/security/host-header.test.ts、test/e2e/tunnel-cycle.test.ts |
| 绕过路由(/..//api、%2e、重复斜杠、/__guard/API/login) | 统一返回 401/403/404,绝不透传 | test/security/path-bypass.test.ts(ADV-001..020) |
| 第 100 次暴力破解失败 | 受限模式开启,重启后仍然生效,隧道上的密钥/会话被拒,loopback 放行 | test/security/nist-ceiling.test.ts |
| 密钥/机密泄漏(日志、响应、IPC 帧、Telegram 载荷、env、state) | 按值埋的 canary 一旦泄漏就失败 | test/security/secret-leak-canary.test.ts(ADV-050..059) |
| 密钥比较会泄漏时间吗? | 恒定时间的统计证明 | test/security/timing-constante.test.ts |
| 用真实进程跑完整隧道周期 | start → READY → 通过 URL 得到 401 → 带会话得到 200 → stop,无孤儿进程 | test/e2e/tunnel-cycle.test.ts(T6.1) |

关于这张表不是什么,得说实话:e2e 用的是 fake-cloudflared* 和假边界—— 按设计,CI 里没有任何字节离开 127.0.0.1。穿过真实边界和真实网络的再次确认,是 test/live/** 套件(DSH_GUARD_LIVE_TESTS=1),需要手动开启,不在门禁内 ——见 docs/TESTING.md §5。

卸载与回滚

dsh plugin remove dsh-guard-messenger
清掉所有残留进程,Web UI 就回到原来的行为。想连链接密钥、配对和本地状态一起删掉:移除 ~/.dsh/guarded-bot。细节见 docs/INSTALL.md。

文档

- docs/INSTALL.md — 分步安装
- docs/ONBOARDING-TELEGRAM.md — 连接 Telegram 机器人并配对
- docs/AGENTS.md — 智能体调度器:配置、命令、安全与限制
- docs/EXPOSURE.md — 隧道起来后有哪些变化
- docs/TUNNEL.md — quick 与 named、TTL、传输层威胁模型
- docs/THREAT-MODEL.md — 攻击者、缓解措施、哪些不缓解
- docs/TROUBLESHOOTING.md — 症状 → 原因 → 怎么办
- docs/ARCHITECTURE.md — Cordis 接缝与模块地图
- docs/TESTING.md — 怎么跑每一级测试

贡献与报告

- 发现漏洞?不要开公开 issue。 读 SECURITY.md,用里面写的私密渠道(Private Vulnerability Reporting 或邮件)。
- 想贡献?CONTRIBUTING.md 里有四条命令搭好环境、测试分级、PR 里永远不接受的东西,以及怎么添加消息服务商。
- 行为准则:CODE_OF_CONDUCT.md。

MIT 协议 — LICENSE。上游的 DeepSeek Harness 同样是 MIT。
预览分享卡片长什么样 ↓frederico-kluser/dsh-guard-messenger
分享 · DeepSeek Harness Hub
frederico-kluser/dsh-guard-messenger

用密钥链接在手机上安全访问本地编码界面

回到顶部