服务端部署

项目支持的生产部署主线是 Linux 主机上的 Docker Compose。完整栈包含 gateway、Camofox Browser、拒绝私网目标的 Squid 出口代理,以及可选的 Caddy 自动 HTTPS。

前置条件

不要向公网暴露 gateway 的 8080、Camofox 的 9377 或 Squid 的 3128 端口。远程 Agent 只能使用 HTTPS endpoint。

生成固定版本的部署命令

源码 Git tag 和 gateway 镜像 tag 必须一致。下面的命令默认使用构建当前文档时的项目版本。

Linux / Docker Compose

不填写域名时,gateway 只监听 127.0.0.1:8080;填写域名后,bootstrap 会加载 Caddy overlay 并自动申请 HTTPS 证书。脚本以 0600 权限创建 .env,生成两把独立的 64 字符密钥,拉取所有镜像并等待服务就绪。

配置搜索与抓取就绪等待

默认使用不需要额外凭据的稳定优先顺序:

WEB_SEARCH_PROVIDERS=duckduckgo,brave,bing,google
WEB_SEARCH_PROVIDER_TIMEOUT_MS=15000
WEB_SEARCH_PROVIDER_COOLDOWN_MS=300000
WEB_FETCH_READY_TIMEOUT_MS=5000

列表同时决定启用的 Provider 和故障切换顺序,不能为空、不能重复,也不能包含未知名称。只填写 google 可保留单引擎行为;将其放在第一位可使用 Google-first。无论 gateway 浏览器并发设为多少,Google 都固定为单并发。

遇到 search_blocked 后,该 Provider 会立即进入冷却,同一个请求继续尝试下一 Provider。冷却期间会直接跳过,不再创建浏览器 Tab;冷却结束后只允许一个半开探测请求。明确的零结果不会触发切换。Provider 超时、不可用或解析契约变化也会触发本次降级,但只有 blocked 会打开冷却熔断。

WEB_FETCH_READY_TIMEOUT_MS 用于限制 web_fetch 首次只得到空白或 iframe 占位时的一次就绪等待。部分微信公众号分享链接会先进入自动验证中间页,几秒后再返回正文;gateway 会等待一次并重新 snapshot,但不会点击或解决 CAPTCHA。

选择暴露方式

仅回环地址

不要设置 WEB_SEARCH_DOMAIN。适用于 Agent 与服务位于同一主机、SSH Tunnel、私有 VPN,或者已经存在本机反向代理的场景。在服务端检查:

curl --fail http://127.0.0.1:8080/readyz

使用 Caddy 公网 HTTPS

先完成域名解析,再设置 WEB_SEARCH_DOMAIN。Caddy 会监听 80/443、自动申请和续期证书,并且只反向代理 gateway。如果服务器没有可工作的 IPv6 路由,不要配置 AAAA 记录。

curl --fail https://search.example.com/readyz

使用已有反向代理

按回环模式部署,把 HTTPS 请求转发到 http://127.0.0.1:8080。保持回环监听,转发请求体与 Authorization Header,并允许 MCP 流式响应。不要把 /metrics 暴露到公网。

验证认证与 MCP

/healthz 只表示 gateway 进程存活;/readyz 还会确认 Camofox 已连接且浏览器正在运行。这两个端点无需认证,搜索、抓取、MCP 和 metrics 都需要公开 API Key。

set -a
. ./.env
set +a

curl --fail http://127.0.0.1:8080/readyz
curl --fail http://127.0.0.1:8080/v1/search \
  -H "Authorization: Bearer $WEB_SEARCH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query":"Camofox Browser","count":3}'

在 Agent 主机安装 CLI,先运行普通检查。--live 会执行真实 Provider 链搜索,并显示最终 Provider 或明确的上游错误。

Agent 验证

日常运维

回环部署只使用基础 Compose 文件;Caddy 部署的每条命令都要追加 -f deploy/compose.public.yaml

docker compose --env-file .env -f deploy/compose.yaml ps
docker compose --env-file .env -f deploy/compose.yaml logs --tail=200 gateway camofox egress-guard
docker compose --env-file .env -f deploy/compose.yaml restart gateway
docker compose --env-file .env -f deploy/compose.yaml down

down 会保留命名卷。除非明确要删除 Caddy 证书和缓存的 GeoLite 数据,否则不要添加 -v

带认证的 /metrics 可以通过回环 gateway 访问,其中包含 Provider 尝试结果、fallback、耗时、熔断状态和 Fetch 就绪恢复指标。内置的公网 Caddy 配置会按设计对该路径返回 404。

升级与回滚

必须保留现有 .env,其中的公开 Key 已经安装到各 Agent。升级时不要运行 bootstrap.sh --force,否则两把密钥都会被替换。

  1. 拉取并切换到目标 Release tag。
  2. 只修改 .env 中的 WEB_SEARCH_IMAGE,让其指向相同版本的 GHCR 镜像。
  3. 使用与首次部署相同的 Compose 文件组合执行 pull 和 up。
  4. 确认 /readyz,再运行 camofox-web-search doctor
git fetch --tags
git checkout v0.0.2
# 编辑 .env:WEB_SEARCH_IMAGE=ghcr.io/idefav/web-search:0.0.2
docker compose --env-file .env -f deploy/compose.yaml pull
docker compose --env-file .env -f deploy/compose.yaml up -d --no-build --wait --wait-timeout 180

回滚使用同样流程,只需切回上一版本的源码 tag 和镜像 tag。应通过 Secret Manager 或加密渠道备份 .env;一旦丢失,就必须在所有 Agent 上轮换公开 Key。

常见问题

现象 检查或处理方式
/healthz 正常但 /readyz 返回 503 检查 camofoxgeolite-init 日志以及服务器的外网 HTTPS。
Caddy 无法申请证书 检查 A/AAAA 记录、80/443 端口,以及是否有其他进程占用端口。
401 unauthorized 确认 Agent 进程读取到的 WEB_SEARCH_API_KEY 与服务端 .env 一致。
429 或 busy 降低调用并发,或者有意识地调整 gateway 限制。
search_blocked 所有已启用 Provider 都被拦截或正在冷却。按 Retry-After 稍后重试,或在 Squid 后配置合规的上游代理;不得绕过出口防护。
微信文章返回 fetch_blocked 验证中间页未在等待时间内自动恢复。按 Retry-After 稍后重试;立即重复请求可能加重基于 IP 的风控。合规的上游代理可能改善成功率,但服务不会解 CAPTCHA 或导入登录态。
unsafe_url URL 解析到了私网、保留、本地或其他禁止地址,这是预期的安全保护。
upstream_timeout 检查浏览器和出口日志,并且只在响应标记为可重试时重试。

项目不会自动解决 CAPTCHA,也不绕过站点访问控制。搜索和抓取可用性仍取决于部署服务器公网出口 IP 的信誉与策略。