Skip to content
dsh.fish
Bundle

dsh-postapi-bridge

DeepSeek Harness (DSH) 统一网关与鉴权桥梁插件:为外部系统与机器人提供开箱即用的 HTTP POST / RESTful API 网关,同时提供 Web 多账号安全认证与用户管理。

Source
ptrel1
License
MIT
Updated
Updated 8 days ago

Readme

# dsh-postapi-bridge 🚪🔐

> **DeepSeek Harness (DSH) 双半侧统一网关与鉴权桥梁插件**
> 融合 **人类端多用户安全登录** + **机器端轻量 HTTP POST / RESTful API 调度网关**。

> ⚠️ **文档时效说明(20260923 核对)**:本文部分章节写于 **DSH 0.1.2 时代**,
> 其中「需修改 DSH 源码加 `requireSession` 扩展点」的路径**已不适用于 0.1.5+**——
> 官方已在 `client/connection` 内**原生提供** `browserAuth.isAuthenticated()` 门禁,
> 无需再打源码补丁。**改动细节见下方「🔐 公网账号系统」章节的现状标注。**
> 阅读时以带 ✅/⚠️ 标注的现状说明为准。

---

## 📦 npm 发布状态

> ⚠️ **npm 上的版本已滞后**:registry 上最新为 **`0.1.0`**,而本仓库当前已是 **`0.6.6`**。
> **请勿以 npm 版本为准**——`0.1.0` 缺少 `sessionAuth` 服务接线,且缺少其后的全部安全加固
> (会话指纹绑定、step-up 提权、伪回环绕过修复、机器通道 fail-closed 等)。
> **以本仓库为准(当前 `0.6.7`)。**
>
> ✅ **推荐安装方式:从本仓库源码安装**
> ```bash
> dsh plugin --profile web add git+https://github.com/ptrel1/dsh-postapi-bridge.git
> ```
> 🔗 npm 页面(版本较旧,仅供参考):[https://www.npmjs.com/package/dsh-postapi-bridge](https://www.npmjs.com/package/dsh-postapi-bridge)

---

## 版本记录 v0.6.4(20260902,机器通道安全加固·PLAN-SEC02 P1-急)

- **机器通道 fail-closed**:/api/dsh/v1/* 非本地(严格双回环之外)请求一律要求 Bearer/X-Gateway-Token;
  apiToken 为空也拒绝——杜绝「token 未配 → 全网可 RCE」的 fail-open。
- **严格回环判定 isStrictLoopback**:socket+Host 双回环,绝不经 X-Forwarded-For
  —— 伪造 XFF:127.0.0.1 与 frp 直连(socket=127,Host=公网)均无法蒙混免 token;
  verifyFingerprint / handleApiBridge / handleAdminUsers 三处统一。
- **/health 白名单免 token**:健康探测不要求凭据,其它机器通道方法一律 401。
- package.json 与 AUDIT_VERSION 对齐 0.6.4。

## 版本记录 v0.6.3(20260902,网关鉴权体验/误杀缓解)

- 会话指纹放宽:UA 只绑定设备类别(desktop/mobile/tablet/other)而非逐字节哈希,桌面/手机切换不再误杀;旧会话回退 UA 哈希硬匹配。ipPrefixBits 默认 24→16。
- 未登录自动跳登录页:浏览器半侧包装全局 fetch 捕获 /api 401/403,仅无会话 cookie 且在登录页外时跳 /login;stepup 场景不狂跳。
- package.json 与 AUDIT_VERSION 对齐 0.6.3。

---

## 🏗️ 前身与历史(承接 `mai_study_code`)

本项目的缘起,与 `maibot_dsh_bridge` 一脉相承,同样承接自早期架构探索原型 —— **`mai_study_code`(麦麦学代码)**。

- **前身定位**:为麦麦设计"目录式 + Web 可视化 + 自进化"的代码学习/编辑工作台,后演变为独立 Web 应用(AgentLoop + WebServer + Sandbox + SSE 事件总线),其架构与 DSH 相似度约 **70%~75%**。
- **中断原因**:自造整套轮子(Agent 循环、Web 编辑器、沙盒、权限、持久化、事件流)维护负担过高,遂暂停。
- **迁移验证**:经研究确认 **DSH** 的架构正是最初构想的那套,转而采用 DSH 作为承载底座。
- **本项目角色**:`dsh-postapi-bridge` 是承接该构想、为外部系统/机器人开放 **DSH HTTP POST / RESTful API 调度网关** 的落地插件(客户端一侧配套 `maibot_dsh_bridge` 负责麦麦接入)。

> 🔗 **前身仓库(已归档)**:https://github.com/ptrel1/mai_study_code.git  
> 📖 该仓库 README 内含完整的架构对比、探索历史与归档说明。

---

## 🧠 DSH 上下文清理与压缩机制(网关长任务背景)

当外部系统通过本网关驱动 DSH 执行长任务时,DSH 通过 **`compaction`(压缩)体系** 控制上下文增长,其**二级漏斗**设计对理解长任务稳定性很有帮助:

| 级别 | 机制 | 是否调 LLM |
|---|---|---|
| **第一级 `dsh-compaction-tool-result-pruner`** | 超预算的 `tool/result`(如 `read`/`bash` 长输出)改写为「保留开头 + 省略标记 + 保留尾部」,**纯语法级剪枝** | ❌ **不调 LLM** |
| **第二级 `dsh-compaction-basic`** | 剪枝仍不足以缓解上下文压力时,调用 LLM 生成**语义摘要**(`retainRatio: 0.08` 保留最新 8%、`maxTokens: 8192`、`thresholdRatio: 0.8`) | ✅ 仅在必要时 |

> **理解要点**:DSH 先通过**非 LLM 剪枝**低成本地把工具输出"压形",只有仍超预算才让 LLM "压义"。这对网关侧的长任务(跨多次 POST push/pull)至关重要——它避免了上下文无限膨胀,同时保住工具结果细节与对话语义,让长流程稳定可控。

---

## ✨ 核心特性

1. **插件本体零侵入(0-Diff)**:登录页、多账号管理、机器 POST API 均基于 DSH 标准扩展点实现,**无需修改官方核心代码**。
   > ✅ **20260923 核对**:DSH **0.1.5+ 起已完全零侵入**——官方原生提供 `/api` 前置登录门禁
   > (`client/connection` 的 `browserAuth.isAuthenticated()`),本插件无需任何源码补丁。
   > 早期(0.1.2)曾需加 `requireSession` 扩展点,该路径已废弃。
2. **人类 Web 通道**:
   - 优美自适应主题登录页(`/login` 与 `/logout`);
   - 多账号独立权限与 Web 账号管理面板;
   - 本机回环(`127.0.0.1`)免密直通。
3. **机器 POST API 通道**:
   - 为 **MaiBot(麦麦)/ 微信机器人 / 飞书 / CI/CD** 等外部系统提供免 Cookie 的纯 POST API;
   - 携带 `Authorization: Bearer <Token>` 或 `X-Gateway-Token` 即可跨域直通,无论是在容器内、公网域名还是内网反向代理,**永不受 127/Cookie 重定向限制**。

---

## 🔐 公网账号系统与 DSH 源码扩展点(重要)

> ⚠️ **现状核对(20260923,针对 DSH 0.1.5-rc.2 实测)**:
>
> 本节以下原文描述的是 **0.1.2 时代**的实现路径。**实测结论**:
>
> | 原文所述 | 0.1.5 现状 |
> |---|---|
> | 需改源码加 `requireSession` 扩展点 | ❌ **源码中已不存在该扩展点**(全仓库 grep 无命中) |
> | 插件 `provide('sessionAuth')` 供 DSH 调用 | ⚠️ **仍在 provide(`lib/index.js:710`),但源码中零消费者** —— 属**预留/历史接口** |
> | 鉴权由 DSH 侧统一完成 | ✅ **成立**,但走的是**官方原生实现**:`packages/client/connection/src/browser-auth.ts` 的 `isAuthenticated()`,由 `rpc-host.ts:99` 调用(`requestRejection`) |
>
> **✅ 现状结论**:DSH 0.1.5 已把「`/api` 前置登录门禁」**吸收进官方源码**,
> 公网未登录访问 `/api/*`(含 `remote.mux` websocket)**一律 401**,loopback 恒放行。
> **不再需要修改 DSH 源码**,本插件负责的是登录页 + 多账号 + 机器通道。
>
> 因此,若你在 0.1.5+ 上看到 `/api/*` 返回 401 —— **这是正常的门禁行为,不是故障**。
> (实测:dsh1 与 dsh2 的无 cookie 请求表现完全一致。)

---

本插件保护的是「登录页 + 管理 API + 机器通道」,**无法保护官方 `/api/*` 核心 RPC**。DSH 官方把 `/api/*` 的信任边界定义在"谁能连到服务"(Host 头)而非"是否登录",且 webserver **无中间件、路由防重复、RPC interceptor 拿不到 request**——纯插件无法在 `/api` 前置登录校验。

因此,公网场景要实现「未登录禁止调用任何 DSH 核心功能」(发消息、执行命令、读写配置等),必须:
1. **修改 DSH 源码**:在 `packages/client/connection/src/index.ts` 增加**默认关闭**的 `requireSession` 扩展点(不开启时与官方单用户行为完全一致;配套 `api-request-trust.ts` 导出两个内部函数);
   > ⚠️ **0.1.5+ 已不需要此步骤**——官方已原生实现,见上方现状核对。
2. **本插件提供实现**:`ctx.provide('sessionAuth', { isAuthenticated })` 返回鉴权判定。
   > ⚠️ **0.1.5+ 此接口已无消费者**,保留仅为兼容旧版 DSH。

> **配置面(settings.* / credentials.*)公网可用**:原生门禁放行登录用户后,原本公网一律 403 的配置面(读配置、改配置、凭据管理、原生对话框、agent preset 管理、模型发现)在公网可访问与修改——登录校验由 DSH 侧统一完成,未登录仍被 401 拦截。
> ✅ **0.1.5+ 实测确认此行为成立**(门禁由官方 `browserAuth.isAuthenticated()` 承担)。

> 完整决策流程(仅本地 → 不改源码;公网 → 需改源码)、源码改动方案、git 维护与升级指导,见:
> 🔒 `skill/public-network-auth-guide.md`

---

## 📡 API 接口速查

| 接口 | 方法 | 鉴权方式 | 说明 |
| :--- | :--- | :--- | :--- |
| `/login` | `GET / POST` | 账号/密码表单 | Web 用户登录与会话颁发 |
| `/logout` | `GET / POST` | Cookie | 安全登出 |
| `/api/dsh/v1/health` | `GET` | 免密 / Token | 健康检查与状态探测 |
| `/api/dsh/v1/task` | `POST` | Bearer Token | 调度 DSH 核心引擎派发 Agent 任务 |
| `/api/dsh/v1/mcp/tool`| `POST` | Bearer Token | 直接调用 DSH 工具(如 `bash`, `read`) |

### 溯源审计(v0.3.0)

**功能**:为网关的每一笔请求(`/api/dsh/v1/*`,含 `/task`)固化成一条溯源记录,用于回答「谁、从哪、走什么通道、调用哪个接口/插件下发了任务」。

**落册两条**:
- `ctx.logger`:进程日志中以 `[postapi-audit]` 前缀输出(DSH supervisor 的 `dsh-web.log` / `dsh-rescue.log` 可见);
- `data/access-log.jsonl`:插件数据目录下的 JSON Lines 文件(默认 `插件目录/data/`,超 5MB 自动轮转保留 3 份 `.1/.2/.3`)。

**记录四要素**:
| 要素 | 字段 | 来源 |
| :--- | :--- | :--- |
| 来源 IP | `ip` / `xff` / `loopback` | 直连 IP + `X-Forwarded-For` 全链 + 是否回环 |
| 域名入口 | `host` / `xForwardedHost` / `xForwardedProto` | `Host` 头与转发头 |
| 通道 | `channel` / `channelVia` | `X-DSH-Channel` 头 → `config.channels` / `DSH_GATEWAY_CHANNELS` 的 token↔通道映射 → UA 推断 → `unknown` |
| 调用方 | `caller` / `ua` | token 指纹(SHA256 前 8 位,明文 token 不落盘)+ User-Agent |

**配置**:
- `config.channels`:`{ "<token>": "通道名", ... }`(把 maibot 的网关 token 映射为 `maibot`,通道一眼可辨);
- `DSH_GATEWAY_CHANNELS`:环境变量 JSON,格式同上(默认层,低于 `config.channels`);
- `config.accessLogDir`:自定义审计日志目录(默认插件 `data/`)。

**生效**:改完重启 DSH(`supervisorctl restart dsh-web`),`/api/dsh/v1/health` 返回 `"audited": true` 作为启用标记。

---

## 🚀 安装与挂载

### 方式一:从 GitHub 源码安装(推荐)

```bash
# 一行命令挂载到 DSH web profile
dsh plugin --profile web add git+https://github.com/ptrel1/dsh-postapi-bridge.git

# 重启 DSH 服务生效
supervisorctl restart dsh-web
```

> 📦 本仓库为当前维护源(最新 `0.6.6`)。
> ⚠️ npm registry 上的 `dsh-postapi-bridge` 停留在 `0.1.0`(缺少 `sessionAuth` 接线,会导致公网 API 403/401),
> **不建议**用 `dsh plugin add dsh-postapi-bridge` 安装。

### 方式二:本地源码 Link(开发 / 定制)

```bash
cd ~/.dsh/profiles/web
dsh plugin --profile web add link:/main/app/github/dsh-postapi-bridge

# 重启 DSH 服务生效
supervisorctl restart dsh-web
```

---

## 🔧 为什么需要修改 DSH 源码(而非纯插件)—— 好处

> ⚠️ **现状(20260923)**:本节论述的是**为什么当初选择"源码门禁"而不是"纯插件方案"**——
> 这个**设计判断本身仍然成立**(能力边界对比见下表)。
> 但**实现路径已变**:官方在 **0.1.5** 把该门禁**吸收进了源码本体**
> (`client/connection` 的 `browserAuth.isAuthenticated()`),
> 因此**使用者不再需要自己打补丁**——那是上游替我们做了这件事。
> 下表左列「纯插件」的局限,仍是当前生态里第三方远程控制插件的真实局限。

本插件的**登录鉴权**依赖一个源码级扩展点(`client-connection` 的 `requireSession`)。为什么不做成"零侵入"纯插件?因为两种方案的**能力边界完全不同**:

| 维度 | 纯插件(零侵入) | 源码扩展点(官方原生门禁) |
| :--- | :--- | :--- |
| 能否在 `/api` 前置鉴权 | **不能**(官方无中间件、路由防重复、interceptor 无 request) | **能**:connection 路由 handler 内统一校验 |
| 覆盖范围 | 只能 gate **自己新开的通道** | 整个 `/api/*`:HTTP RPC + SSE + WebSocket,**fail-closed** |
| 对直连 `/api`(curl 绕过) | 管不住 | 一律 401/403 |
| 与官方升级的兼容性 | 天然无冲突 | 由官方维护(0.1.5+ 起**无需自行 merge**) |

**源码门禁的好处(对比纯插件远程控制方案)**:
1. **守住官方唯一的门**:鉴权发生在 `/api` 入口本身,任何客户端(浏览器、curl、脚本、第三方前端)都被同一道门拦截;
2. **不被通道绕过**:纯插件"远程控制"方案只能把流量引到自己通道再以本机身份代理回 `/api`,本质是可绕过的通道门禁;源码门禁没有这个缝;
3. **行为不漂移**:loopback 恒放行、非回环未登录 401 —— 由官方语义保证;
4. **配置面随用户登录开放**:登录用户可在公网读写 `settings.*`/`credentials.*`(官方默认 pin loopback)。

## 🆚 与第三方远程控制插件(@linxin666/dsh-remote-web-ui)的区别

- **设备配对 ≠ 用户登录**:`dsh-remote-web-ui` 的"设备配对"只是给**设备**发 cookie,管不住直连官方 `/api` 的请求(官方源码原话:"没有插件能做到;`/api` 的围栏是 SDK 自己的接缝")。
- **配对会绕过登录门禁**:它把远程流量经 `/remote` 通道用 loopback 反向代理(伪造 `Host:127.0.0.1`)转回本机,而门禁对 loopback 恒放行 → **配对成功即免登录全权限**。
- **本部署取舍**:公网登录鉴权的唯一入口是源码级门禁(**0.1.5+ 为官方原生**,fail-closed);已在 profile patch 禁用 `web-ui-remote-web-ui`:

```yaml
# ~/.dsh/profiles/web/cordis.patch.yml
- id: web-ui-remote-web-ui
  disabled: true
```

- 详细架构对比见 🔒 `skill/public-network-auth-guide.md` §七。

Install

dsh plugin --profile web add github:ptrel1/dsh-postapi-bridge

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source