onesvm-browser-server/docs/dev-handoff-20260902.md
chii 9b689b2476 feat: 落地节点指纹与 Cookie 罐,并按现网能力更新消费/接手文档
公开页抓取改为 Chrome 136 自洽身份 + 每节点 SQLite 养罐,Trafilatura 走 curl_cffi;MCP/README/接手说明与 09-02 现网复测对齐,避免消费方继续抄过期的站点三分表。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 17:04:25 +08:00

125 lines
6.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
type: runbook
status: active
created: 2026-09-02
---
# 接手开发说明
> 读者:第一次改这个仓的工程师 / Agent。读完应能:找到权威文档、在本机跑通测试、知道改哪一层、知道什么不能做。
> 决策权威:[`plan-final-20260901.md`](plan-final-20260901.md)。机制权威:[`design-arch-20260901.md`](design-arch-20260901.md)。现网能力:[`mcp-usage-20260901.md`](mcp-usage-20260901.md) §4。仓库地图:根 [`README.md`](../README.md)。
## 0. 十分钟路径
1. 读根 `README.md` §1–§3(问题、分层、入选方案)。
2. 读本文 §1–§4(目录、事件、怎么改、怎么验)。
3. `cd server && go test -race ./...` 必须绿。
4. 再按任务读:路由 → `internal/scheduler/routing.go`;抓取 → `stacks/trafilatura/app.py` + `internal/dock/`;合规 → `internal/policy/`;MCP 契约 → `internal/gateway/mcphandler.go`。
不要从 `.dsh/contracts/` 或 superseded 手稿当现行 acceptance。过程产物在 `.dsh/artifacts/`(gitignore)。
## 1. 进程与包
单二进制三角色,Swarm 里三个服务:
| `-role` | 职责 | 有状态 |
|---|---|---|
| `gateway` | MCP `/mcp`、HTTP `/v1/*`、Auth、策略预检、入队 | 无。可多副本 |
| `scheduler` | SQLite 队列、能力路由、Dock 适配器、模版层、Cookie 罐 | **有。replicas=1,钉节点** |
| `proxymanager` | 订阅、探活、选出口 | replicas=1 |
适配器在 scheduler 进程内,引擎在旁边的容器(SearXNG / Trafilatura / Lightpanda / shell)。
| 要改 | 去哪 | 不要 |
|---|---|---|
| 工具参数 / 信封 | `internal/contract/`、`gateway/mcphandler.go` | 只改文档不改 schema |
| 搜索 junk 过滤 | `scheduler/searx_parse.go` `isJunkSearchHit` | 在适配器里偷偷丢结果 |
| 精读 TLS/UA | `stacks/trafilatura/app.py`(真抓取) | 只改 Go `httpx` 假装 Chrome |
| 指纹模版 | `internal/fingerprint/` | 模版和 impersonate 错代 |
| Cookie 罐 | `internal/session/` + `store/session.go` | 把 Cookie 写进 MCP / jobs JSON |
| robots / SSRF | `internal/policy/` | fail-open 放行内网 |
| 出站代理 | `internal/proxymanager/` | 在适配器里自己拨代理 |
| 部署 | `stacks/browser-server.yml`、`scripts/deploy-mgr1.sh` | 在 Swarm 节点上 `docker pull` |
`JobEnvelope.Session` 与 `RawResult.SetCookies` 是 `json:"-"`。打破这条 = Cookie 进审计/MCP,P0。
## 2. 事件表(本服务已填、热修只改碰到的行)
| 事件 | 怎么活 | 怎么死 | 多快死 |
|---|---|---|---|
| search/read 请求 | 认证过 → 入队 ACK → 适配器出 RawResult → 模版层信封 | 策略 `denied`;上游 `blocked`/`upstream`/`timeout` | 入站跟网关;出站 Trafilatura 15s;队列硬顶 120s |
| 排队位 | running+queued &lt; 60 才接 | 超限 503,只拒新 | 消费者按 `Retry-After` |
| Cookie 罐 | 200 且非投毒则 Sanitize 后覆盖该域 | 403/验证页/换出口 → 整域删 | TTL 2h,硬顶 6h,60s reap |
| 指纹模版 | 节点 HOSTNAME 种子,8 套落库,内存只持 active | 不因单次失败换脸 | 进程重启从库恢复同一张脸 |
| 渲染槽 | Lightpanda 多槽;shell 默认 0 副本 | shell 忙则互斥停接 | 空闲 10min 计数(本版不自动 scale) |
| 密钥 | `X-Service-Token`,库内 hash | 缺省 fail-closed | 吊销缓存 ≤10s |
「用默认超时」不算已填。Go 出站禁止 `http.DefaultClient`。I/O 第一参必须是 `ctx`。
## 3. 改代码时的分层
```
消费者 → gateway(认证/配额/SSRF/robots)
→ scheduler 入队
→ 路由(region + 是否要渲染)
→ dock 适配器(只产 RawResult)
→ 模版层(体积守卫 / fit markdown / 安全扫描 / 信封)
```
新增第 6 个引擎:实现 `init/health/execute/teardown/capabilities`,注册路由标签,**不要**再写一套合规。对照 `docs/overview-integration-20260901.md` §2。
P0 指纹自洽:`impersonate`、UA、`sec-ch-ua`、platform 钉同一 Chrome 大版本(现网 136)。Go `httpx` 保持诚实 UA。
P1 罐硬顶:每域 20 颗、单条 4KiB、总 2MiB、64 域 LRU。登录态名(`session-token` / `sid` / `__secure-*psid` 等)直接拒收。
## 4. 验证
合入前:
```bash
make fmt && make vet
cd server && go test -race ./...
make wc # 单文件 >600 行必须拆
```
改了 MCP 参数或信封:同步 `docs/mcp-usage-20260901.md` 与 `mcphandler.go` 的 schema,并补/改 golden。
改了 Trafilatura:必须重建 `onesvm/trafilatura-http:dev`,只更 Go 镜像是半接线(真抓取在 Python)。
现网复测:用 HTTP `/v1/search` `/v1/read` 即可(与 MCP 同一内核)。不要把消费方 key 写进脚本提交。
## 5. 部署(🟡,须用户确认)
目标是 **primary mgr1 `192.168.1.51`**,不是 dev-swarm。默认 `scripts/deploy-mgr1.sh` 是 dry-run。
已知坑(已踩过,不要再踩):
1. 生产节点禁直连 Docker Hub;本机构建 amd64 → `save|load` → `--resolve-image never`。
2. **同 tag `:dev` 覆盖后必须 `docker service update --force`**,否则还跑旧任务。
3. Lightpanda CDP 握手 `Host` 必须是 `127.0.0.1:9222`,Docker 服务名会被拒。
4. Trafilatura cgroup 现网是 **192m**(64m 会 OOM 137)。`deploy-prod-preset` 里仍写 64m,以 stack 为准。
5. 海外 search 出口键用 `https://www.bing.com/`,不要拿空 URL 去 `/api/exit`。
6. 单次适配器超时不要把整实例健康闩死;只有 `/healthz` 失败或 HTTP 5xx 才摘适配器。
回滚:`docker stack rm browser-server`(命名卷保留)。不要 `git push --force`。
## 6. 文档怎么维护
| 变了什么 | 更新哪 |
|---|---|
| 工具参数 / 错误码 / 现网能力 | `mcp-usage-20260901.md`(消费方权威) |
| 联调步骤 / 验收用例 | `integration-vlepontas-20260901.md` |
| 决策(档位、Bing-only、不做 L4) | `plan-final`;不要只改 overview |
| 队列字段 / Dock 五方法 | `design-arch` |
| 选型理由 | `decision-candidates` |
| 接手路径 / 现网坑 | **本文** |
NFP:规范进组织 `base/`,本仓只写本服务事实。密钥只在 `deploy.env` / 组织 `credentials.md`,禁止写进 docs。
## 7. 不要做的事
- 为「让 Google 出结果」去伪装 Chrome + Go TLS,或给消费方换出口绕 robots。
- 把 Amazon 登录 Cookie / TOTP 账号池搬进本服务(DaaS 的实证:决定变量是出口 IP)。
- 未填事件表就加新中间件(Redis、第二套队列)。
- 未授权 commit / push;用户没说「确认执行」就 `--apply` 到 mgr1。
- 用 2026-09-01 bench 的站点三分表覆盖现网 §4(BBC/百科/Amazon 结论已漂移)。