# 威胁模型（v0）

> 与 [`01-mvp-scope.md`](01-mvp-scope.md) 配套。技术名词保留英文。  
> 文档示例一律使用 `example.com` / `127.0.0.1` / `YOUR_TOKEN` 占位，不得写入真实 token、私钥或内网主机名。

## 资产

| 资产 | 说明 |
| --- | --- |
| `deviceToken` | 配对后手机持有的长期秘密。服务端只存 SHA-256 哈希。 |
| 服务端 X25519 私钥 | 数据面 E2EE 的身份锚。文件 0600，目录 0700。 |
| 会话内容 | Agent 输出、用户短回复、当前会话元数据。经 E2EE 后对中继/旁路不可读。 |
| 审批决策 | 批准 / 拒绝 / 提问应答。写操作必须可归因到 `deviceId`。 |

## 攻击者

| 攻击者 | 能力假设 |
| --- | --- |
| LAN 被动嗅探 | 可读同网段明文 HTTP / WS。 |
| LAN 主动 MITM | 可 ARP/DNS 欺骗，在页面加载时注入或替换 JS。 |
| QR 泄露 | 拍照、截图、投屏使 pairing offer 离开操作者控制。 |
| 手机丢失 | 已配对设备落入他人之手，直到吊销。 |
| 恶意插件 | 与本插件同住一个 DSH profile，可读部分宿主服务。 |
| 自建中继运营者 | 控制 Worker 源码与 Cloudflare 账号；可见连接元数据；PIN 路径可见 offer。 |

## 安全不变量

1. **未认证连接只处理握手。** 数据面在完成配对握手之前，不接受任何业务 RPC、不返回会话内容。
2. **秘密落地约束。** `deviceToken` 只存 SHA-256 哈希；私钥、注册表、审计文件权限 0600（所在目录 0700）；密钥永不进入日志、截图或仓库。
3. **RPC 白名单默认拒绝。** 未知方法一律拒绝。写操作（审批、提问应答、短回复）写入审计记录，可归因到 `deviceId`。
4. **管理面 owner 围栏。** 管理面始终只绑回环。本机/SSH 必须是 loopback TCP peer 与匹配的 loopback Host/Origin；HTTPS 反代还必须同时满足精确 peer allowlist、精确 HTTPS Origin/Host、代理鉴权后注入的 owner proof、`Sec-Fetch-Site: same-origin` 与独立 mutation CSRF proof。非受信 peer 的 `X-Forwarded-*` 一律忽略，配置不完整 fail closed。
5. **不触碰宿主围栏。** 不修改、不削弱 `dsh web` `/api` 的绑定与鉴权；不抢 `api-proxy` 的审批 / 提问 provider。
6. **文档占位。** 一切公开示例使用 `example.com` / `127.0.0.1` / `YOUR_TOKEN`，不出现真实地址或凭据。

## 诚实边界（v0）

手机页经 **LAN HTTP 明文下发**。主动 LAN MITM 可在页面加载时注入恶意 JS；页面一旦被替换，后续 E2EE 只保护「恶意脚本与服务器之间」的信道，管不到投递层。

这是**浏览器客户端相对签名原生 App 的固有差距**，不是本里程碑的实现疏漏。`/m` 响应附带 CSP（`script-src 'self'`、`frame-ancestors 'none'`）与 `X-Frame-Options: DENY`，用来收窄**已合法下发页面**被注入后的能力；它**不能**阻止首次 HTTP 被替换。

缓解（按优先级）：

- 推荐把数据面放在 Tailscale / WireGuard 等加密 overlay 内，而不是裸 LAN。
- **Cloudflare Quick Tunnel（方案 1，本里程碑）**，设置页显式开启，只暴露数据面：
  - **`/m` 会公开可达**：任何人持有 `https://<随机>.trycloudflare.com` 即可加载手机页。TLS 在 Cloudflare 边缘终结，回源 `http://127.0.0.1:6879`。
  - **配对仍是 fragment + E2EE**：`/m` 静态页面本身不含会话秘密；没有 fragment 令牌（+ 共享密钥）仍不能完成 `e2ee_auth`。URL 等同临时钥匙，勿转发。
  - **必须随 unload /「停止」杀掉 `cloudflared` 子进程**（`ctx.effect` disposer）。
  - **禁止**把 `3080` / `dsh web` 送进隧道；隧道 `--url` 只指向 `127.0.0.1:<数据面端口>`。
- M4 可选：自签 HTTPS + 二维码钉死证书哈希（TOFU）。
- 未来选项：路线 C 原生 App，用签名包投递客户端，彻底离开「首次 HTTP 下发」模型。
- **方案 2：自建会合中继（M5）**，设置页显式开启，桌面与手机各自出站连操作者自己的 Worker：
  - **不**把本机 `6879` / `3080` 打到公网；中继只做会合与密文转发。
  - **投递层是边缘 TLS**：裸 LAN MITM 换不了 `/m`。被攻破的 Worker 仍可下发恶意 JS——自建 = 信任该 CF 账号与该源码。
  - **QR fragment 不到 Worker**；PIN 兑换（`POST /m/claim`）会让 Worker 运营者在 TTL 内看见完整 offer（含 `deviceToken`）。
  - Worker 可见连接元数据（谁在连、帧长/时间），不可见 `e2ee_auth` 之后的 RPC 明文。
  - **禁止**把 `3080` / `dsh web` 接到 Worker；**禁止**本仓库运营公共会合点。
  - 规格见 [`05-cloud-relay.md`](05-cloud-relay.md)。

**明确出界（v0 不防、不声称防）：**

- 桌面被控（攻击者已在跑 `dsh web` 的机器上）
- 浏览器扩展窃取设置页 / 配对码
- 通用 XSS（宿主或其它插件的 DOM 注入）
- 手机页把 X25519 私钥与 resume offer 放在 `sessionStorage`（非 `localStorage`）：关标签即失效，降低共享设备上的残留；仍无法抵御同页 XSS 读存储

## 禁止事项 / Prohibitions

以下文案可直接展示给用户（中英对照）。

- **不共享他人凭据。** Do not share another person's credentials.
- **不监测未授权账户。** Do not monitor accounts you are not authorized to access.
- **不裸暴露公网端口。** Do not bind the data-plane port on `0.0.0.0` to the public Internet. A user-started Cloudflare Quick Tunnel (HTTPS at the edge, localhost origin) or a **self-hosted** rendezvous Worker (desktop outbound only) is an explicit exception; never tunnel `3080`.
- **不暗示 DeepSeek 官方背书。** This plugin is not affiliated with, and is not endorsed by, DeepSeek.

错误示例（禁止照抄到生产）：`ws://203.0.113.10:6879?token=s3cret`  
正确示例：`ws://127.0.0.1:6879` 或 `wss://example.com`，token 写作 `YOUR_TOKEN`。
