# dsh-local-403-fix

修复 `dsh web` 反向代理访问时特权 `/api` 方法返回 **403** 的问题。

## 背景

官方 `@deepseek-ai/dsh-client-connection` 把 15 个特权方法（`settings.*`、`credentials.*`、
`agentPreset.*`、`host.pickDirectory/openPath`、`llm.discoverModels`）钉死在 **loopback**：
前缀路由先用配置的 `trustedHosts` 校验，但进入 fetch handler 后，这些方法会**用空列表
再校验一次**：

```js
// dsh-client-connection@0.1.0-rc.7 lib/index.js 第 538 行
if (method !== void 0 && PRIVILEGED_METHODS.has(method) && !isTrustedApiRequest(request, []))
  return new Response("forbidden", { status: 403 });
```

因此即使 `--trusted-host` 配置了公网域名/IP，任何经过反向代理（本环境为
Xray 隧道 + socat）到达的请求，只要 `Host` 头不是 loopback，特权方法一律 403。
这是官方刻意设计（配置/凭据平面在"真正的认证层出现之前"保持 loopback 同源），
上游 master 至今未改（`packages/client/connection/src/index.ts` 第 147 行）。

## 方案（本目录采用一行补丁，替代旧的 260 行插件）

对运行时实际加载的全局包打一行补丁，把特权检查的空列表换成配置的 `trustedHosts`：

```diff
-		if (method !== void 0 && PRIVILEGED_METHODS.has(method) && !isTrustedApiRequest(request, [])) return new Response("forbidden", { status: 403 });
+		if (method !== void 0 && PRIVILEGED_METHODS.has(method) && !isTrustedApiRequest(request, trustedHosts)) return new Response("forbidden", { status: 403 });
```

语义与上游一致：`trustedHosts` 仍是 DNS-rebinding 围栏，不是认证层。
`trustedHosts` 变量在 `apply()` 作用域内（第 531 行），补丁无需额外改动。

## 文件

| 文件 | 作用 |
|---|---|
| `patches/@deepseek-ai+dsh-client-connection@0.1.0-rc.7.patch` | 标准 pnpm 补丁（进 git，可复现） |
| `apply-403-fix.sh` | 幂等脚本：定位全局包 → 备份 → `patch -p1` 应用/回滚/检查 |
| `package.json` | `pnpm.patchedDependencies` 声明（工程内 pnpm install 时自动应用） |
| `cordis.patch.yml` + `src/index.js` | ⚠️ 旧方案（260 行插件 bundle），已由本补丁替代，保留仅供回退参考 |

## 用法

```bash
# 应用补丁（幂等；my_deepseek.sh 启动前会自动调用）
./apply-403-fix.sh

# 检查状态
./apply-403-fix.sh --check

# 回滚
./apply-403-fix.sh --revert
```

应用后重启 dsh 生效：

```bash
my_deepseek.sh stop && my_deepseek.sh
```

## 为什么不用 pnpm patchedDependencies 直接生效？

`pnpm.patchedDependencies` 只对**当前 pnpm 工程的依赖树**生效；而本环境的 dsh 是
**全局 npm 安装**（`/home/salmonstill/.nvm/.../lib/node_modules/@deepseek-ai/dsh`），
运行时加载的是全局那份 `dsh-client-connection`。所以工程内保留标准补丁文件 +
`apply-403-fix.sh` 幂等打到全局包，两者兼顾可复现与运行时生效。
将来若把 dsh 改为工程内安装，`patchedDependencies` 声明可直接复用。

## 升级 dsh 后

- `apply-403-fix.sh --check` 会显示 `unknown`（特权检查行已变），此时需对照新版
  源码更新补丁文件；
- 补丁文件内的 blob hash 需用 `git hash-object` 重新计算（见 git 历史或脚本注释）。
