Skip to content

Commit 46967e5

Browse files
committed
docs: 规则成文与文档同步(阶段5)
- AGENTS.md:pragma 规则与实际用法对齐(D1=C)——业务分支禁用, 两类「按构造不可达」例外成文(src/ 防御兜底、tests/ 测试替身宽接口) - README:TRUST_PROXY 行补登录限流/审计共用同一 IP 解析; QUOTA_PROBE_MINUTES 标注下限 - TECHNICAL:流内错误分类改为 Event.error_kind 单一来源的说明; /authorize pending TTL 与回调 URL 不驻留内存;trust_proxy 小节 补限流/审计同源语义
1 parent d0d6d99 commit 46967e5

3 files changed

Lines changed: 9 additions & 5 deletions

File tree

‎AGENTS.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66

77
- 后端:`uv run ruff check src tests scripts` + `uv run pytest -q --cov=src --cov-report=term --cov-fail-under=100`
88
- 行/分支覆盖率 100% 是硬门槛;新增/修改的代码必须带测试,覆盖率缺口一律补测试解决,
9-
禁止用 pragma/排除来"达标"
9+
禁止用 pragma/排除给「该测没测」的业务分支达标。允许的例外(必须带注释说明为何不可达):
10+
- src/:「按构造不可达」的防御兜底——`__main__` 入口、协议演进防御分支、前置 return
11+
已排除的死分支
12+
- tests/:测试替身实现了宽于本用例所需的接口方法(注释「由 XX 调用 / 本用例不调用」)
13+
- 平台差异分支不用 pragma,必须 monkeypatch 测全(见下条)
1014
- 前端(web/):`pnpm exec tsc --noEmit` + `pnpm exec vitest run` + `pnpm build`
1115
- 注意平台差异:本地 macOS 通过不代表 CI(ubuntu-latest)通过;平台相关分支
1216
(platform.system/machine 等)必须用 monkeypatch 测全所有分支

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ CodeBuddy 成长中心的「连登天数 / 活跃地图」按日统计客户端
232232
| `DEFAULT_MODEL` | `glm-5.2` | 模型为空/`auto` 时的默认 |
233233
| `USERS_FILE` | `secrets/users.txt` | **仅引导期**:老式用户文件路径,启动时一次性导入 SQLite(已存在的用户名不覆盖,幂等)。账号唯一源是 `users` 表 |
234234
| `DATA_DIR` | `./data` | SQLite 与运行数据目录 |
235-
| `QUOTA_PROBE_MINUTES` | `60` | 额度探测周期 |
235+
| `QUOTA_PROBE_MINUTES` | `60` | 额度探测周期(下限 1 分钟) |
236236
| `GROWTH_INTERVAL_MINUTES` | `60` | 成长中心(仅 CodeBuddy)一轮领取的周期;下限 5 分钟 |
237237
| `GROWTH_IRREVERSIBLE_ACTIONS` | `true` | 是否允许成长中心的不可逆动作:抽奖、连登兑换、开 Buddy 盲盒、消耗补登卡。`false` 时仍会领取旅行礼物与任务奖励 |
238238
| `ACTIVITY_REPORT_ENABLED` | `false` | 活跃上报(仅 CodeBuddy):每天为账号补发一条对话事件续连登。**默认关闭**——官方条款禁止脚本篡改活动数据(处罚为取消资格并追回礼品),上游改版即失效,不作为可靠性功能(见上文「活跃上报」) |
@@ -242,7 +242,7 @@ CodeBuddy 成长中心的「连登天数 / 活跃地图」按日统计客户端
242242
| `CONVERSATION_STICKY_SECONDS` | `3600` | 会话粘性 TTL:优先按请求体显式会话标识(`conversation_id`/`conversationId`/`prompt_cache_key`,metadata 或顶层),无则回落消息前缀指纹,多轮请求固定用同一凭证(手动 pin 的凭证优先,粘性让位);带 `user_id` 时不派生前缀兜底键(避免并行对话误钉同一号);凭证出错仍会轮换,成功后重新粘定;`≤0` 关闭 |
243243
| `MODEL_BLOCKLIST` | `custom_model_*,*sub*agent*,summary,browser_use_*,file_search_agent,default,hunyuan-image-*` | 模型列表黑名单(fnmatch,仅影响列表展示,直连指定不受影响);默认值按两边上游实测清单补入内部/不可用模型(`default` 零内容、`hunyuan-image-*` 400 11103),刻意不含 `*-volc` 与 `aquila`/`sagitta`/`seed-code-pro-0430`(实测可正常 chat)(见 TECHNICAL.md §3.5) |
244244
| `ALLOWED_HOSTS` | 空 | Host 白名单,防 DNS rebinding |
245-
| `TRUST_PROXY` | `false` | 是否采信 `X-Forwarded-For` 判定 API Key 的来源 IP(`allowed_ips` 白名单用)。默认关闭——该头由客户端可写;仅在「本服务前恰好一层受信反代」时开启,届时取 XFF 最后一个条目(见上文「API Key 的渠道绑定与来源 IP 白名单」) |
245+
| `TRUST_PROXY` | `false` | 是否采信 `X-Forwarded-For` 判定来源 IP(API Key 的 `allowed_ips` 白名单、登录限流与审计共用同一解析)。默认关闭——该头由客户端可写;仅在「本服务前恰好一层受信反代」时开启,届时取 XFF 最后一个条目(见上文「API Key 的渠道绑定与来源 IP 白名单」) |
246246
| `CODEBUDDY_API_ENDPOINT` | `https://copilot.tencent.com` | CodeBuddy 上游地址;改动时必须同时把它加入 `CODEBUDDY_ALLOWED_ENDPOINTS` |
247247
| `CODEBUDDY_ALLOWED_ENDPOINTS` | 官方两站(见 compose) | 上游端点白名单,真实 Token 只发往白名单内地址 |
248248
| `CODEBUDDY_CHAT_MIN_INTERVAL` | `5` | CodeBuddy 聊天最小间隔(秒),与 TRAE 共享节流;`0` 关闭 |

‎TECHNICAL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ response.completed | response.incomplete
475475

476476
来源 IP 判定在 `deps.api_key_user`(鉴权**当场**判掉,不往上传递):
477477

478-
- **默认不采信 `X-Forwarded-For`**:该头由客户端可写,信它等于白名单形同虚设。只有 `TRUST_PROXY=true` 才采信,且取**最后一个**条目——`$proxy_add_x_forwarded_for` 语义下那是紧邻的受信代理实际看到的地址,第一个条目是客户端自己写的。故该开关只适用于「本服务前恰好一层受信反代」,多层或直连必须保持关闭。
478+
- **默认不采信 `X-Forwarded-For`**:该头由客户端可写,信它等于白名单形同虚设。只有 `TRUST_PROXY=true` 才采信,且取**最后一个**条目——`$proxy_add_x_forwarded_for` 语义下那是紧邻的受信代理实际看到的地址,第一个条目是客户端自己写的。故该开关只适用于「本服务前恰好一层受信反代」,多层或直连必须保持关闭。登录限流/审计与 API Key 的 IP 白名单共用同一解析(`deps.request_ip`):反代部署下若限流拿裸对端地址,全站请求会共享同一个限流桶、审计里全是代理 IP。
479479
- 策略是纯函数(`auth/access.py`,不依赖框架):写入时用 `normalize_allowed_ips` 校验并规范化(`10.0.0.1` → `10.0.0.1/32`),非法值 400;读取路径宽松解析,脏条目丢弃,整份白名单一条都解析不出则**拒绝**(fail closed,不因脏数据敞开)。
480480
- 白名单命中失败返回 **403**(`forbidden`)——Key 本身有效,是来源不被允许;与 401「凭证无效」区分,便于调用方排查。
481481
- `api_key_user` 由「返回用户名」升级为返回 `ApiKeyPrincipal(username, key_id, provider_binding)`(4 处出口调用同步调整)。`ApiKeyRepository.verify` 保留为只回用户名的薄封装。
@@ -832,7 +832,7 @@ fixture 存于 `src/provider/fixtures/`(真实 SSE/JSON 样本,覆盖正文
832832
- **同步 sqlite3 而非 aiosqlite**(T-Q2):本地微秒级操作,asyncio 封装开销大于收益
833833
- **双 httpx 客户端**(T-Q4):聊天流 `read=None` 防长流截断;短请求总超时 30s 防悬挂;共享 `trust_env=False`。非流式路径由引擎聚合同一流式上游(无独立 HTTP),悬挂兜底是引擎层的聚合整体超时(`UPSTREAM_COMPLETE_TIMEOUT_SECONDS`,默认 600s,超时按瞬态错误换号重试)
834834
- **手写 SQL 而非 ORM**:10 张表规模下 ORM 收益为负
835-
- **polling OAuth 不转回调**(Q17=C):上游协议决定;TRAE 回调走主端口 + `PUBLIC_BASE_URL`
835+
- **polling OAuth 不转回调**(Q17=C):上游协议决定;TRAE 回调走主端口 + `PUBLIC_BASE_URL`。`/authorize` 无鉴权(浏览器 302 不带 key),防滥用靠两条:无进行中登录一律拒绝;待完成登录有 600s TTL(长期挂着的 pending 会被同网络任何人用自己的 refreshToken 完成兑换——凭证入池、归属记为发起登录的管理员)。`app.state` 只保留 pending 的 state,不驻留含 refreshToken 的完整回调 URL
836836
- **v1 无 Anthropic**(Q8=A):Event 层已预留,v1.1 只加 `compat/anthropic/` 适配器
837837
- **Responses 出口只做 Codex CLI 用到的子集**(Q32,详见 §3.7):不做 `store=true` / `previous_response_id`(服务端无状态,不假装支持);`include=["reasoning.encrypted_content"]` 按实测接受并忽略——Codex CLI 每轮必带,400 会直接打死主客户端;流式终止用 `response.completed` / `response.incomplete` / `response.failed`,**不发 `[DONE]`**(Responses 协议无该哨兵)。形状取自官方 `openai` SDK 类型并用其作客户端验证,对真实 CB 上游冒烟过;**未经真实 Codex CLI 端到端验证**(开发环境无 CLI)
838838
- **不做 reasoning 注入 / effort 档位映射**(原 B1.2,实测后取消):原计划对「强制推理模型族」注入 `thinking` + `reasoning_effort` 并回填历史 `reasoning_content`,实测前提不成立——(1)客户端已自带 `reasoning_effort`(仅 `low`/`medium`)且上游直接接受;(2)客户端已回传历史 `reasoning_content` 且上游接受;(3)原计划的默认模型清单与实际在用命名无关,且 `glm-5.1` 在 `MODEL_BLOCKLIST` 里,硬编码白名单会空转;(4)真要做「客户端丢弃时回填」必须服务端存对话内容,与脱敏纪律冲突。参考实现 IceeAn/codebuddy2api 走相反取向(对白名单模型强制 `reasoning_effort=max` 覆盖客户端),属单来源且会改写客户端意图,不采纳

0 commit comments

Comments
 (0)