--- 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 < 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 结论已漂移)。