单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。 Co-authored-by: Cursor <cursoragent@cursor.com>
137 lines
No EOL
12 KiB
Markdown
137 lines
No EOL
12 KiB
Markdown
# Contract: onesvm-browser-server 控制面首版实现(到「部署 mgr1 测试」就绪)
|
||
|
||
- **contract_id**: contract-20260901-browser-impl
|
||
- **run_id**: run-20260901-browser-impl
|
||
- **创建**: 2026-09-01(Asia/Shanghai)
|
||
- **状态**: approved
|
||
- **复杂度**: core_framework(触发 Architecture Review)
|
||
- **用户授权**: 2026-09-01 用户原话确认「按预设(D7=Go、D8=进程内适配器、D9=灰度、D10=附加 casa-net、D1=L0→L2 渐进、D3=:8640//bs-api)开实现 Contract」——D1–D10 全部按预设锁定,不再询问。
|
||
|
||
## 1. Goal
|
||
|
||
实现 docs 全部设计所定义的**控制面三角色 Go 单仓**(gateway / scheduler / proxymanager,`cmd/browser-server` 单二进制 role 分流),**到部署 mgr1 测试的就绪态**:代码 + 单测全绿 + 本机 docker compose 冒烟通过 + amd64 镜像构建 + stack.yml + 传输/部署脚本。**本 Contract 不执行任何对 mgr1 的实际部署/SSH 操作**(部署轮另走 🟡 确认)。
|
||
|
||
## 2. 权威输入(Read,全部必读)
|
||
|
||
| 输入 | 路径 | 用途 |
|
||
|---|---|---|
|
||
| 设计权威 | `docs/design-arch-20260901.md` | 机制细节(§2 认证/配额、§3 Dock 五方法/信封、§4 队列/背压、§5 ProxyManager/合规、§7 可观测) |
|
||
| 决策权威 | `docs/plan-final-20260901.md` | 结论与档位(§2 排队/内存、§3 Dock/输出、§5 评分) |
|
||
| 部署预设 | `docs/deploy-prod-preset-20260901.md` | 服务清单/端口/资源/灰度步骤(D7/D8/D9/D10 已锁) |
|
||
| MCP 契约 | `docs/mcp-usage-20260901.md` | 工具参数/响应形状/错误码对消费者承诺(T1 契约面) |
|
||
| 组织规范 | `onesvm-dev-md/base/development-standards.md` | §5 urlapi、§6 镜像 tag、§7 东八区 |
|
||
| 组织规范 | `onesvm-dev-md/base/deployment-rules.md` | stack 必备字段/secret/命名 |
|
||
| 实测凭据 | `bench/searxng-{cn,global}/settings.yml`、`bench/{lightpanda,chrome-headless-shell}/compose.yml`、`bench/trafilatura-http/app.py`、`bench/site-matrix/cdp_fetch.mjs`、`bench/proxy/up.sh` | 引擎配置/CDP 最小调用面/适配器接口形状(复用声明见 §6) |
|
||
|
||
## 3. 决策锁定(不再开放)
|
||
|
||
| # | 决策 | 值 |
|
||
|---|---|---|
|
||
| D3 | 端口 | gateway host `:8640`;反代前缀 `/bs-api`;scheduler `:8641`、proxymanager `:8642`、mihomo mixed `:17890` 仅 overlay |
|
||
| D7 | 技术栈 | Go 单仓单二进制多角色(`go run ./cmd/browser-server -role=…`) |
|
||
| D8 | 适配器形态 | scheduler 进程内 Go 模块(引擎经 overlay HTTP/CDP 调用;Dock 五方法 = 内部 interface) |
|
||
| D9 | 上线 | mgr1 单节点起步,灰度扩展三节点另议 |
|
||
| D10 | 联调网络 | stack 附加 `vlepontas-casa-net`(external,alias `browser-server`) |
|
||
| D2 | 国外搜索 | Bing-only 姿态 |
|
||
| O9/O10 | MCP 2025 腿 / 管理面 | 首版仅 2026 无状态 POST /mcp;管理面 CLI + SQL 先行(首版:CLI 子命令写 SQLite) |
|
||
| 依赖 | 第三方库 | 禁重型依赖;准许 `modernc.org/sqlite`(纯 Go 无 CGO)、`gopkg.in/yaml.v3`;其余 stdlib + 手写(MCP JSON-RPC/CDP WebSocket 自实现,面窄);网络依赖须经 Go proxy 镜像拉取 |
|
||
|
||
## 4. scope(写边界)
|
||
|
||
**in-scope(唯一写区)**:`server/`(新建,全部 Go 源码)、`stacks/browser-server.yml`、`scripts/deploy-mgr1.sh`、`scripts/smoke-local.sh`、`docs/impl-20260901.md`(交付记录)、`.dsh/contracts/**`、`.dsh/artifacts/run-20260901-browser-impl/**`。`docs/deploy-prod-preset-20260901.md` 仅允许追加「镜像 tag 约定(development-standards §6)」小节(O13 落地)。
|
||
|
||
**out-of-scope**:`docs/` 其余文档、`bench/`、`onesvm-dev-md/`(submodule)、`casa-commander/`(submodule)、`.dsh/casa-runtime/`、`README.md`、`AGENTS.md`、任何 remote/生产操作。**本仓不做 git commit**(除非用户另行指示)。
|
||
|
||
## 5. 逐模块 Acceptance(工程级硬门槛)
|
||
|
||
### A1 公共包(`server/internal/`)
|
||
|
||
- `contract/`:统一信封/JobEnvelope/RawResult/五方法接口/错误码——与 design-arch §3.2/§3.3、mcp-usage §2/§3 字段**一致**;时间字段一律 `+08:00`。
|
||
- `store/`:SQLite(modernc 纯 Go)open(WAL/busy_timeout=2s/foreign_keys=on)+ migration(schema v1:consumers/api_keys/audit/quota/jobs/dead_letters/rules)+ repository 方法面。SQL 源码内 `?` 占位符、行级错误处理、无 `SELECT *` 糊装。
|
||
- `auth/`:静态 key 校验 `hmac.Equal`(constant-time);密钥注入 `${VAR:?required}` fail-closed;**禁 `os.Getenv` 默认值兜底**。
|
||
- `policy/`:SSRF fail-closed(协议 80/443、私网/元数据 CIDR、CNAME 银行卡号段不重定向重验——**重定向重验按 design §5.4 每跳重验**)、域名规则 trie(direct/pool/deny)、robots(普通 key,TTL 缓存 SQLite)。
|
||
- `safetyscan/`:词表文件 + 编译正则(NFKC 归一)+ PII redact + 注入包裹 delimiter;命中高危 block;`wordlist_version` 字段。
|
||
- `httpx/`:共享 HTTP client(大小/类型守卫、超时、重定向重验 hook)。
|
||
|
||
### A2 gateway
|
||
|
||
- MCP JSON-RPC 2.0 单 handler(`tools/list`、`tools/call`,MCP 头校验/协议版本),HTTP `/v1/search` `/v1/read`,`/healthz` `/readyz`。
|
||
- 认证 → scope → 429/402/403 → **overlay `POST scheduler:8641/enqueue`**(落盘成功才 200,scheduler 不可达 → 503+Retry-After,gateway 零落盘)。
|
||
- 认证头仅 `X-Service-Token`(401 时响应头提示禁 Bearer);admin 面:`X-Service-Token` = admin token,`POST /admin/keys`(签发)、`DELETE /admin/keys/{id}`(吊销)。
|
||
- 搜索响应走 gateway 进程内短 TTL 缓存(query+region 键,默认 300s,≤50 条 LRU)——消化上游 CAPTCHA(plan-final §2.4 设计结论)。
|
||
|
||
### A3 scheduler
|
||
|
||
- 队列(SQLite 单写者 + 进程内 bounded queue):`enqueue` 落 WAL 才 ACK;`ADMIT_MAX=60`(running+queued,env 可调);429/503 携带 `Retry-After` + `running/queued` 现状。
|
||
- 派发:`UPDATE … WHERE id=(SELECT …) RETURNING`;租约 `lease_until` + reaper 收割 + `attempts≤2` 仅瞬时错误重试 + 死信表。
|
||
- Dock 适配器进程内注册表:searxng-cn / searxng-global / trafilatura / lightpanda / headless-shell 五个,各自实现 `contract.DockAdapter` 五方法;capabilities 如 §4.4 标签。
|
||
- 能力路由:region/render 标签匹配(`region=overseas` 强制走 mihomo 出口;`shell_active ⇒ panda 停新` 互斥,入队侧执行)。
|
||
- 模版层(模版层整合器,S7 归属):RawResult → Size/Type guard → fit markdown(trafilatura 引擎侧已产 markdown,CDP 引擎给 text→go 差分算法转 markdown 或直接 bodyText 作正文)→ safetyscan → 信封封装;score 归一 0–1。
|
||
- 降级链:C 满试 B、B 失败回 A(HTTP+fit),全败 `blocked/upstream` + warnings,不静默降质。
|
||
- `/pressure` + `/metrics`(文本格式,design §7.2 指标面)。
|
||
- headless-shell 按需槽:Swarm API(`DOCKER_HOST=tcp://…` 或 unix socket 挂载)scale 0→1,空闲 10min 回收(D8 预设;不可用时适配器返回 unhealthy,不阻塞其它通道)。
|
||
|
||
### A4 proxymanager
|
||
|
||
- 订阅解析:Clash YAML(复用 bench `lib.py` 逻辑的 Go 版)→ 剔除占位节点 → vless/hysteria2 分池(hy2 不调度)→ 写 mihomo provider 配置 + mihomo 热载。
|
||
- 探活:`https://www.google.com/generate_204`(禁 cp.cloudflare.com HEAD);活跃 30s / 全池 5min;连续 2 失败摘除、1 成功回候选;EWMA 延迟按域名分组。
|
||
- 轮换 P2C + sticky session(TTL map);域名路由表(direct/deny/pool)与 gateway policy 联动(共享 SQLite rules 表 + 热载)。
|
||
- HTTP API(仅 overlay):`/healthz`、`/api/proxies`(探活状态)、`/api/exit?domain=`(sticky 选出口)。
|
||
- `PROXY_SUB_URLS` 多订阅容灾;订阅 URL 不落盘不入 git(内存传递给 mihomo config 生成)。
|
||
|
||
### A5 部署产物
|
||
|
||
- `stacks/browser-server.yml`:9 服务(gateway/scheduler/proxymanager/mihomo + searxng-cn/global + trafilatura + lightpanda + backup sidecar)+ shell 按需(`replicas=0`,Swarm 拉起);deployment-rules §1.3 必备字段全(TZ/placement/healthcheck/limits);镜像 digest/tag 锁定;`vlepontas-casa-net` external 附加 alias `browser-server`;SQLite named volume 钉 mgr1。
|
||
- Dockerfile 多阶段(builder golang:1.27-alpine → distroless/base 或 alpine);Makefile(build/test/lint/fmt/cross-amd64/image);`scripts/deploy-mgr1.sh`(save|load + stack deploy --resolve-image never + prune dangling 尾步骤)与 `scripts/smoke-local.sh`(compose 冒烟:起本地 compose,签发 key,curl 全链路断言)。
|
||
- 镜像 tag 约定落地:`onesvm/browser-server:dev` 日常复用(O13)。
|
||
|
||
### A6 质量门槛(verify/reverify 共用)
|
||
|
||
1. `go build ./...`、`go vet ./...`、`gofmt -l` 空、`go test ./...` **全绿**;关键路径单测覆盖:auth(比对常数时间/过期/吊销)、SSRF 私网判定、队列抢单原子性、词表命中、信封字段与 mcp-usage 一致性(golden test)、路由标签匹配、探活摘挂状态机。
|
||
2. 行数:`server/` 与脚本每文件 **≤600 行**硬上限(组织 600 行纪律);>600 须拆分后再验收。
|
||
3. T 系复核:T1 契约(mock 形状=真实响应形状:searxng json/trafilatura/CDP 均以 bench 实测样本为准)、T2 fail-closed(SSRF/密钥/订阅缺省)、T3 密钥(`${VAR:?}` fail-closed + `hmac` 比对 + 无默认值)、T6(租约 reaper 退避)、T7 入参边界(Pydantic→Go 等价物:入参长度/范围校验)。
|
||
4. 本机冒烟(smoke-local.sh,可选依赖 Docker 在线):gateway/scheduler 起 + stub 引擎 → 签发 key → `/v1/search` `/v1/read` MCP+HTTP 全链路 200 + 错误路径(401/403/429/503)断言。
|
||
5. amd64 交叉构建成功(`GOOS=linux GOARCH=amd64 go build`),镜像 ≤60MB 目标(distroless)。
|
||
|
||
## 6. 复用声明(组织纪律:能复用须标明)
|
||
|
||
| 复用物 | 来源 | 去向 |
|
||
|---|---|--- A2 |
|
||
| searxng 引擎配置(cn 四引擎 / global Bing-only 姿态) | `bench/searxng-{cn,global}/settings.yml` | stack 内嵌同参数 settings(按 Swarm 语法适配) |
|
||
| trafilatura 服务(`POST /v1/read`、SSRF 守卫、semaphore=8) | `bench/trafilatura-http/app.py`(镜像已构建 `bench-s3b-trafilatura:local`) | stack 的 trafilatura 服务 = **同一代码构建**,镜像 tag `onesvm/trafilatura-http:dev` |
|
||
| CDP 最小调用面(json/version → ws → Target.create/attach → Page/Runtime/Network enable → navigate → evaluate 提取) | `bench/site-matrix/cdp_fetch.mjs` | lightpanda/headless-shell 适配器 CDP 客户端(Go 重写,面窄) |
|
||
| Clash 订阅解析/占位节点剔除/区域归类逻辑 | `bench/proxy/lib.py` | proxymanager 订阅解析(Go 移植) |
|
||
| 反爬特征检测正则(cloudflare/waf/captcha 判定) | `bench/site-matrix/cdp_fetch.mjs detectVendor` | 模版层 blocked 判定 |
|
||
| compose 参数(镜像 digest pin、shm、proxy env) | `bench/*/compose.yml` | stack yml 引擎服务 |
|
||
|
||
## 7. 工件流(artifact_io)
|
||
|
||
Worker 交付物一律落 `.dsh/artifacts/run-20260901-browser-impl/`:
|
||
|
||
| 产物 | 路径 |
|
||
|---|---|
|
||
| 实现回执(builder 自验收) | `impl-receipt.md` |
|
||
| verify(首轮独立验收) | `verify.md` |
|
||
| reverify(复核 + 迭代修复记录) | `reverify.md` |
|
||
| 架构审查(core_framework 强制) | `arch-review.md` |
|
||
| 冒烟原始输出 | `smoke/` |
|
||
| 行数检查输出 | `wc.txt` |
|
||
|
||
## 7bis. 通信纪律(CASA 铁律)
|
||
|
||
- Worker 之间/Worker→指挥官:只传**产物路径 + diff 摘要**,禁「如上所述」传话。
|
||
- Worker 不得最终自评 PASS——**builder ≠ judge**;verify/reverify 由独立只读 Worker 执行。
|
||
- 指挥官主会话禁写业务文件(`server/`、`stacks/`、`scripts/`);仅契约/工件/收口文档。
|
||
|
||
## 8. 失败与迭代
|
||
|
||
任一 acceptance 不满足 → verify Worker 出 `fail-*.md`(列证据 file:line)→ 指挥官派迭代 Worker **只注入 fail artifact + 本 Contract** 修复;最多 3 轮(policy.max_iterations=3),仍败则升级用户。
|
||
|
||
## 9. 部署边界(红线)
|
||
|
||
本轮**不 SSH mgr1、不 stack deploy、不 push remote**。`scripts/deploy-mgr1.sh` 只写脚本不执行。部署轮另走 🟡 前置确认(mgr1 free -g / ss -tln / 影响面报告)。
|
||
|
||
## 10. 时间与语言
|
||
|
||
- 全部时间字段 Asia/Shanghai `+08:00`(development-standards §7);容器 `TZ=Asia/Shanghai`。
|
||
- 代码注释/交付文档简体中文,标识符英文。 |