单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。 Co-authored-by: Cursor <cursoragent@cursor.com>
12 KiB
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-netexternal 附加 aliasbrowser-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 共用)
go build ./...、go vet ./...、gofmt -l空、go test ./...全绿;关键路径单测覆盖:auth(比对常数时间/过期/吊销)、SSRF 私网判定、队列抢单原子性、词表命中、信封字段与 mcp-usage 一致性(golden test)、路由标签匹配、探活摘挂状态机。- 行数:
server/与脚本每文件 ≤600 行硬上限(组织 600 行纪律);>600 须拆分后再验收。 - T 系复核:T1 契约(mock 形状=真实响应形状:searxng json/trafilatura/CDP 均以 bench 实测样本为准)、T2 fail-closed(SSRF/密钥/订阅缺省)、T3 密钥(
${VAR:?}fail-closed +hmac比对 + 无默认值)、T6(租约 reaper 退避)、T7 入参边界(Pydantic→Go 等价物:入参长度/范围校验)。 - 本机冒烟(smoke-local.sh,可选依赖 Docker 在线):gateway/scheduler 起 + stub 引擎 → 签发 key →
/v1/search/v1/readMCP+HTTP 全链路 200 + 错误路径(401/403/429/503)断言。 - 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。 - 代码注释/交付文档简体中文,标识符英文。