隐身浏览
面向 AI Agent 的隐身无头浏览器,绕过 Cloudflare 与反爬,可替代 Puppeteer/Playwright
由 jo, 一个个人 AI 代理 的团队构建,jo 一半运行在你的 Mac 上,一半运行在专属于你的专用云机器上——无需任何维护。可在 macOS、Telegram、WhatsApp 和电子邮件上使用。 免费试用测试版 ->
git clone https://github.com/jo-inc/camofox-browser && cd camofox-browser
npm install && npm start
# -> http://localhost:9377
为什么
AI 代理需要浏览真实网络。Playwright 被拦截。Headless Chrome 被指纹识别。隐身插件成为指纹本身。
Camoufox 在 C++ 实现层面 修补 Firefox——navigator.hardwareConcurrency、WebGL 渲染器、AudioContext、屏幕几何信息、WebRTC——所有内容在 JavaScript 触及之前就被伪造。没有 shim、没有包装器、没有痕迹。
该项目将该引擎封装为专为代理构建的 REST API:可访问性快照替代臃肿的 HTML、用于点击的稳定元素引用,以及针对常见网站的搜索宏。
功能
- C++ 反检测 - 绕过 Google、Cloudflare 和大多数机器人检测
- 元素引用 - 稳定的
e1、e2、e3标识符,用于可靠交互 - 令牌高效 - 可访问性快照比原始 HTML 小约 90%
- 在任何地方运行 - 延迟启动浏览器 + 空闲时关闭,空闲时内存占用约 40MB。设计用于与你的其余堆栈共享同一台机器——树莓派、5 美元 VPS、共享基础设施。
- 会话隔离 - 每个用户独立的 cookies/存储
- Cookie 导入 - 注入 Netscape 格式的 cookie 文件,用于认证浏览
- 文件上传 - 从配置的上传目录附加文件,无需原生 OS 对话框
- 代理 + GeoIP - 通过住宅代理路由流量,自动设置区域/时区
- 结构化日志 - 包含请求 ID 的 JSON 日志行,用于生产环境可观测性
- YouTube 转录 - 通过 yt-dlp 提取任何 YouTube 视频的字幕,无需 API 密钥
- 搜索宏 -
@google_search、@youtube_search、@amazon_search、@reddit_subreddit等 10 多个 - 快照截图 - 在可访问性快照旁边包含 base64 PNG 截图
- 大页面处理 - 自动截断快照,支持基于偏移量的分页
- 下载捕获 - 捕获浏览器下载并通过 API 获取(可选内联 base64)
- DOM 图像提取 - 列出
<img>src/alt,并可选择返回内联 data URL - 随处部署 - Docker、Fly.io、Railway
- VNC 交互式登录 - 通过 noVNC 可视化登录网站,导出存储状态供代理重用
- OpenAPI 文档 - 自动生成的规范位于
/openapi.json,交互式文档位于/docs - 结构化提取 -
POST /tabs/:tabId/extract使用 JSON Schema,通过x-ref属性映射到快照引用 - 会话跟踪 - 可选的每会话 Playwright 跟踪捕获(截图 + DOM 快照 + 网络),提供 API 端点用于列出、获取和删除跟踪压缩包
- 遥测 - 通过 GitHub Issues 自动 匿名化崩溃/挂起遥测。识别导致故障的网站和常见故障模式。私有域名经过 HMAC 哈希,路径/参数被剥离,令牌/IP 被擦除。通过
CAMOFOX_CRASH_REPORT_ENABLED=false选择退出。
可选依赖
| 依赖 | 用途 | 安装 |
|---|---|---|
| yt-dlp | YouTube 转录提取(快速路径) | pip install yt-dlp 或 brew install yt-dlp |
Docker 镜像包含 yt-dlp。对于本地开发,安装它以支持 /youtube/transcript 端点。没有它,该端点会回退到较慢的基于浏览器的方法。
快速开始
OpenClaw 插件
openclaw plugins install @askjo/camofox-browser
工具: camofox_create_tab | camofox_snapshot | camofox_click | camofox_type | camofox_navigate | camofox_scroll | camofox_screenshot | camofox_close_tab | camofox_list_tabs | camofox_import_cookies
独立运行
从 npm 运行:
npx @askjo/camofox-browser
或者从源码运行:
git clone https://github.com/jo-inc/camofox-browser
cd camofox-browser
npm install
npm start # 首次运行时下载 Camoufox(约 300MB)
默认端口是 9377。查看环境变量了解所有选项。
注意: postinstall 脚本会在获取 Camoufox 二进制文件之前,为自身取消设置
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD。如果没有这个覆盖,导出的PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1(常见于 Playwright 配置为使用系统 Chrome 时)会静默跳过二进制文件下载,并在运行时导致服务器崩溃。外部 Camoufox 可执行文件: 在
npm install之前设置CAMOUFOX_EXECUTABLE=/path/to/camoufox-bin,并在启动服务器时设置,以跳过捆绑下载并使用该可执行文件。兼容别名有CAMOUFOX_EXECUTABLE_PATH和CAMOFOX_EXECUTABLE_PATH。这对 NixOS 路径(如/nix/store/.../camoufox-bin)很有用;可执行文件必须来自包含properties.json、version.json和fontconfig/的 Camoufox 捆绑包。隔离环境或自定义二进制管理: 如果你已经有 Camoufox 捆绑包,建议使用
CAMOUFOX_EXECUTABLE。否则,可以通过npm install --ignore-scripts(跳过每个依赖的生命周期脚本——最粗暴的选项)禁用自动获取,或者更精确地,使用npm install --omit=optional加上手动npx camoufox-js fetch步骤针对你的镜像。注意,PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install不再跳过 Camoufox 下载(postinstall 会在本地清理环境);请使用--ignore-scripts或CAMOUFOX_EXECUTABLE实现该目的。
Docker
附带的 Makefile 会自动检测你的 CPU 架构,并在 Docker 构建之外预先下载 Camoufox 和 yt-dlp 二进制文件,因此重建速度很快(约 30 秒 vs 约 3 分钟)。
# 构建并启动(自动检测架构:M1/M2 上为 aarch64,Intel 上为 x86_64)
make up
# 停止并移除容器
make down
# 强制重新构建(例如升级 VERSION/RELEASE 后)
make reset
# 仅下载二进制文件(不构建)
make fetch
# 显式覆盖架构或版本
make up ARCH=x86_64
make up VERSION=135.0.1 RELEASE=beta.24
Windows
在 Windows 上,make 不可用。请改用附带的 build.ps1 PowerShell 脚本:
# 构建并启动
.\build.ps1 up
# 停止并移除容器
.\build.ps1 down
# 仅构建镜像
.\build.ps1 build
# 强制重新构建
.\build.ps1 reset
# 仅下载二进制文件(不构建)
.\build.ps1 fetch
# 覆盖架构
.\build.ps1 up -Arch x86_64
.\build.ps1 up -Arch aarch64
注意: 推荐使用 PowerShell 7+(
pwsh),但powershell.exe(Windows PowerShell 5.1)也可用。该脚本需要安装了 WSL2 后端的 Docker Desktop for Windows。行尾: 本项目包含一个
.gitattributes文件,强制.sh文件使用 Unix(LF)行尾。如果你已经克隆了仓库并在docker build过程中遇到sh: not found或set: Illegal option -错误,请运行:Get-ChildItem -Recurse *.sh | ForEach-Object { (Get-Content $_) -join "`n" + "`n" | Set-Content $_ -NoNewline }这会将 shell 脚本转换为 LF 行尾。由于
.gitattributes的存在,未来的克隆会自动处理这个问题。
警告:不要直接运行
docker build。 Dockerfile 使用绑定挂载(bind mounts)从dist/拉取预先下载的二进制文件。请始终使用make up(或先运行make fetch再运行make build)——它会先下载二进制文件。
Fly.io
对于 Fly.io 或其他远程 CI,你需要一个在构建时下载二进制文件的 Dockerfile,而不是使用绑定挂载。
Railway
附带了 railway.toml。它使用 Dockerfile.ci(在构建时下载二进制文件),并自动将 Railway 的 PORT 环境变量映射到 CAMOFOX_PORT。
# 安装 Railway CLI,然后:
railway link
railway up
通过 Railway 面板或 CLI 设置密钥:
railway variables set CAMOFOX_API_KEY="your-generated-key"
使用
Cookie 导入
从浏览器导入 Cookie 到 Camoufox,以跳过 LinkedIn、Amazon 等网站上的交互式登录。
设置
1. 生成一个密钥:
# macOS / Linux
openssl rand -hex 32
2. 启动 OpenClaw 前设置环境变量:
export CAMOFOX_API_KEY="your-generated-key"
openclaw start
插件(用于认证请求)和服务器(用于验证请求)使用相同的密钥。两者在同一个环境中运行——只需设置一次。
为什么使用环境变量? 密钥是一个秘密。
openclaw.json中的插件配置以明文存储,因此秘密不适合放在那里。请在 shell 配置文件、systemd 单元、Docker 环境或 Fly.io 密钥中设置CAMOFOX_API_KEY。
Cookie 导入默认禁用。 如果未设置
CAMOFOX_API_KEY,服务器将以 403 拒绝所有 Cookie 请求。
3. 从浏览器导出 Cookie:
安装一个可以导出 Netscape 格式 Cookie 文件的浏览器扩展(例如 Chrome/Firefox 的 “cookies.txt”)。导出你希望认证的网站的 Cookie。
4. 放置 Cookie 文件:
mkdir -p ~/.camofox/cookies
cp ~/Downloads/linkedin_cookies.txt ~/.camofox/cookies/linkedin.txt
默认目录为 ~/.camofox/cookies/。可通过 CAMOFOX_COOKIES_DIR 覆盖。
5. 让你的代理导入它们:
从 linkedin.txt 导入我的 LinkedIn Cookie
代理调用 camofox_import_cookies -> 读取文件 -> 使用 Bearer 令牌 POST 到服务器 -> Cookie 被注入浏览器会话。后续对 linkedin.com 的 camofox_create_tab 调用将自动携带认证信息。
工作原理
~/.camofox/cookies/linkedin.txt (Netscape 格式,磁盘上)
|
v
camofox_import_cookies 工具 (解析文件,按域名过滤)
|
v POST /sessions/:userId/cookies
| Authorization: Bearer <CAMOFOX_API_KEY>
| 请求体: { cookies: [Playwright cookie 对象] }
v
camofox 服务器 (验证、清理、注入)
|
v context.addCookies(...)
|
Camoufox 浏览器会话 (经过认证的浏览)
cookiesPath相对于 Cookie 目录解析——禁止目录遍历逃逸- 每次请求最多 500 个 Cookie,文件大小限制 5MB
- Cookie 对象会被清理,仅保留 Playwright 允许的字段
会话持久化
默认情况下,camofox 将每个用户的 Cookie 和 localStorage 持久化到 ~/.camofox/profiles/。会话在浏览器重启后仍然存在——只需登录一次(通过 Cookie 或 VNC),后续会话会自动恢复已认证状态。
~/.camofox/
|-- cookies/ # 引导 Cookie 文件(Netscape 格式)
\-- profiles/ # 持久化会话状态(自动管理)
\-- <hashed-userId>/
\-- storage_state.json
通过 CAMOFOX_PROFILE_DIR 覆盖目录,或在持久化插件配置中设置 "profileDir"。要禁用持久化,请在 camofox.config.json 中设置 "persistence": { "enabled": false }。
默认情况下,存储状态仅包含 Cookie 和 localStorage。要同时持久化 IndexedDB,请在持久化插件配置中设置 "indexedDB": true。这将捕获所有可序列化的 IndexedDB 记录——不仅仅是认证数据——并且可能使快照明显变大,检查点变慢。
会话追踪
捕获会话中每个操作的 Playwright 追踪:页面截图、DOM 快照、网络请求和控制台输出。输出是一个 .zip 文件,你可以在 Playwright 内置的 Trace Viewer 中打开。
打开第一个选项卡时通过传递 trace: true 为每个会话选择加入:
curl -X POST http://localhost:9377/tabs \
-H 'Content-Type: application/json' \
-d '{"userId":"agent1","sessionKey":"task1","url":"https://example.com","trace":true}'
当会话关闭时写入追踪。关闭会话以刷新它,然后列出、获取和查看:
# 关闭会话以刷新追踪
curl -X DELETE http://localhost:9377/sessions/agent1
# 列出追踪文件
curl http://localhost:9377/sessions/agent1/traces
# {"traces":[{"filename":"trace-2026-04-18T04-05-00-...zip","sizeBytes":42810,"createdAt":...}]}
# 下载(Content-Type: application/zip)
curl http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip > session.zip
# 在 Playwright 的 Trace Viewer 中查看
npx playwright show-trace session.zip
# 删除
curl -X DELETE http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip
为什么选择追踪而不是视频:Camoufox 基于 Firefox,而 Playwright 的 recordVideo 仅支持 Chromium。追踪在 Firefox 上有效,并且比视频提供更多信息(网络 + DOM + 控制台 + 截图)。
现有会话无法动态切换追踪(Tracing)设置。如需更改标记,请先执行 DELETE /sessions/:userId。
存储默认位于 ~/.camofox/traces/<hashed-userId>/,并在服务器启动时进行清理:
CAMOFOX_TRACES_DIR- 基础目录(默认:~/.camofox/traces)CAMOFOX_TRACES_MAX_BYTES- 每条追踪的最大大小,超过时在下一次启动时删除(默认:50MB)CAMOFOX_TRACES_TTL_HOURS- 超过此时间(以小时计)的追踪将在下一次启动时删除(默认:24)
独立服务器用法
curl -X POST http://localhost:9377/sessions/agent1/cookies \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer YOUR_CAMOFOX_API_KEY' \
-d '{"cookies":[{"name":"foo","value":"bar","domain":"example.com","path":"/","expires":-1,"httpOnly":false,"secure":false}]}'
Docker / Fly.io / Railway
docker run -p 9377:9377 \
-e CAMOFOX_API_KEY="your-generated-key" \
-v ~/.camofox/cookies:/home/node/.camofox/cookies:ro \
camofox-browser
对于 Fly.io:
fly secrets set CAMOFOX_API_KEY="your-generated-key"
对于 Railway:
railway variables set CAMOFOX_API_KEY="your-generated-key"
代理 + GeoIP
通过代理路由所有浏览器流量,并利用 Camoufox 内置的 GeoIP 自动从代理的 IP 地址获取区域设置、时区和地理位置。
简单代理(单一端点):
export PROXY_HOST=166.88.179.132
export PROXY_PORT=46040
export PROXY_USERNAME=myuser
export PROXY_PASSWORD=mypass
npm start
回连代理(轮转粘性会话):
适用于 Decodo、Bright Data 或 Oxylabs 等提供单一网关端点且带有基于会话的粘性 IP 的供应商:
export PROXY_STRATEGY=backconnect
export PROXY_BACKCONNECT_HOST=gate.provider.com
export PROXY_BACKCONNECT_PORT=7000
export PROXY_USERNAME=myuser
export PROXY_PASSWORD=mypass
npm start
每个浏览器上下文会获得一个唯一的粘性会话,因此不同用户会获得不同的 IP 地址。会话会在代理错误或被 Google 拦截时自动轮转。
在 Docker 中:
docker run -p 9377:9377 \
-e PROXY_HOST=166.88.179.132 \
-e PROXY_PORT=46040 \
-e PROXY_USERNAME=myuser \
-e PROXY_PASSWORD=mypass \
camofox-browser
配置代理后:
- 所有流量均通过代理路由
- Camoufox 的 GeoIP 会自动设置
locale、timezone和geolocation,使其与代理的出口 IP 匹配 - 浏览器指纹(语言、时区、坐标)与代理位置保持一致
- 未配置代理时,默认使用
en-US、America/Los_Angeles、旧金山坐标
Telemetry
浏览器自动化会以难以预测的方式失败——Cloudflare 验证、网站改版导致选择器失效、重定向循环、对话框风暴、渲染器崩溃。范围广,失败模式多样。没有遥测数据,唯一的信号就是“没成功”。
遥测为我们提供关于哪些网站失败、如何失败以及失败频率的结构化数据,以便我们优先修复那些真正影响用户的问题模式。当出现以下情况时,它会自动创建 GitHub Issue:
- 未捕获的异常导致进程崩溃
- 事件循环停滞超过 5 秒(看门狗检测)
- 挫败模式——同一标签页连续 3 次或更多失败(超时、上下文失效、导航中止)
每条报告包含失败类型、堆栈跟踪、标签页健康计数器(HTTP 状态码直方图、控制台错误数、请求失败数、重定向深度)以及目标 URL——所有内容均已匿名化。
工作方式
遥测数据被发送至一个轻量级的 Cloudflare Worker 端点 https://camofox-telemetry.askjo.workers.dev。该端点以环境变量形式持有 GitHub App 凭据——本包中不包含任何凭据。
lib/reporter.js (客户端,无凭据)
| 匿名化 -> POST https://camofox-telemetry.askjo.workers.dev/report
v
Cloudflare Worker (持有 GitHub App 密钥)
| 验证 -> 限速 -> 去重 -> 创建 GitHub Issue
v
GitHub Issue 创建完成
端点源代码位于本仓库的 workers/crash-reporter/index.ts。
验证
你无需信任我们——可以自行验证当前运行的端点:
# 1. 询问端点正在运行的代码
curl https://camofox-telemetry.askjo.workers.dev/source
# -> { "commit": "abc1234", "sha256": "e3b0c44...", "source": "https://github.com/..." }
# 2. 将 sha256 与本仓库中的源代码进行比较
sha256sum workers/crash-reporter/index.ts
# 3. 检查提交是否与 CI 部署的版本一致
# https://github.com/jo-inc/camofox-browser/actions/workflows/telemetry-deploy.yml
git log --oneline workers/crash-reporter/index.ts | head -1
如果哈希值不匹配,说明端点运行的代码与本仓库中的不同。部署工作流(.github/workflows/telemetry-deploy.yml)会在部署时注入提交和源代码哈希——每次部署都可以在 GitHub Actions 中审计。
或者完全跳过验证:设置 CAMOFOX_CRASH_REPORT_ENABLED=false 可禁用所有遥测功能,或通过 CAMOFOX_CRASH_REPORT_URL 指向你自己的端点。
隐私
所有上报的数据在离开进程前都会经过严格的匿名化处理(lib/reporter.js L28-290):
- URL —— 著名的公共域名(Google、Amazon、Reddit、Cloudflare 等)会原样显示,以便我们识别哪些网站导致问题。私有/未知域名会被替换为稳定的 HMAC 哈希(
site-a1b2c3d4)—— 不同报告中的相同哈希用于关联,但无法还原为原始域名。路径段变为*/*/*(仅保留深度)。查询参数变为?[3](仅保留参数数量)。从不包含任何键、值或路径内容。 - 文件路径 -> 仅保留文件名(
<path>/server.js) - 令牌、密钥、API 密钥 ->
<token> - IP 地址、电子邮件、环境变量 -> 已脱敏
- Docker/Fly 机器 ID ->
<id> - 标签页健康 —— 纯计数器(崩溃次数、错误次数、状态码直方图)。不包含页面内容、URL 或用户数据。
重复的 issue 通过堆栈签名检测,并会以 +1 评论的形式追加到现有 issue 中,而不会创建新 issue。
# 禁用遥测
export CAMOFOX_CRASH_REPORT_ENABLED=false
# 指向你自己的端点(见下文)
export CAMOFOX_CRASH_REPORT_URL=https://your-endpoint.example.com/report
# 调整限速(默认:每小时 10 次)
export CAMOFOX_CRASH_REPORT_RATE_LIMIT=5
自托管遥测端点
要将遥测报告归档到你自己的 GitHub 仓库中,而非 jo-inc/camofox-browser:
-
创建 GitHub App —— Settings -> Developer settings -> GitHub Apps -> New
- 权限:Repository -> Issues -> Read & Write
- 取消勾选 Webhook -> Active(不需要)
- 点击 Generate a key —— 下载一个
.pem文件 - 将应用安装到你的目标仓库(Install App -> 选择仓库)
- 记下你的 App ID(应用 General 页面的数字)和 Installation ID(安装后 URL 中的数字:
github.com/settings/installations/{id})
-
部署端点 — 克隆此仓库并部署 worker:
cd workers/crash-reporter # 编辑 wrangler.toml:将 account_id 设置为你的 Cloudflare 账户 ID npx wrangler deployworker 是一个零 npm 依赖的单一 TypeScript 文件。它也可以在 Deno、Bun 或任何支持 Web Crypto API 的运行时上运行。
-
设置 worker 密钥:
cd workers/crash-reporter echo "YOUR_APP_ID" | npx wrangler secret put GH_APP_ID echo "YOUR_INSTALL_ID" | npx wrangler secret put GH_INSTALL_ID # 密钥必须是 PKCS#8 DER base64 格式(不是原始 PEM) openssl pkcs8 -topk8 -inform PEM -outform DER -nocrypt -in your-app.pem | \ base64 | tr -d '\n' | npx wrangler secret put GH_PRIVATE_KEY # 在你的仓库中提交 issue echo "your-org/your-repo" | npx wrangler secret put GH_REPO -
将 camofox-browser 指向你的端点:
export CAMOFOX_CRASH_REPORT_URL=https://your-worker.your-subdomain.workers.dev/report -
验证:
curl https://your-worker.your-subdomain.workers.dev/health # -> {"status":"ok"}
结构化日志
所有日志输出为 JSON(每行一个对象),便于日志聚合器解析:
{"ts":"2026-02-11T23:45:01.234Z","level":"info","msg":"req","reqId":"a1b2c3d4","method":"POST","path":"/tabs","userId":"agent1"}
{"ts":"2026-02-11T23:45:01.567Z","level":"info","msg":"res","reqId":"a1b2c3d4","status":200,"ms":333}
健康检查请求(/health)被排除在请求日志之外,以减少干扰。
基本浏览
# 创建标签页
curl -X POST http://localhost:9377/tabs \
-H 'Content-Type: application/json' \
-d '{"userId": "agent1", "sessionKey": "task1", "url": "https://example.com"}'
# 获取无障碍快照(含元素引用)
curl "http://localhost:9377/tabs/TAB_ID/snapshot?userId=agent1"
# -> { "snapshot": "[button e1] Submit [link e2] Learn more", ... }
# 通过引用点击
curl -X POST http://localhost:9377/tabs/TAB_ID/click \
-H 'Content-Type: application/json' \
-d '{"userId": "agent1", "ref": "e1"}'
# 向元素输入文本
curl -X POST http://localhost:9377/tabs/TAB_ID/type \
-H 'Content-Type: application/json' \
-d '{"userId": "agent1", "ref": "e2", "text": "hello", "pressEnter": true}'
# 使用搜索宏导航
curl -X POST http://localhost:9377/tabs/TAB_ID/navigate \
-H 'Content-Type: application/json' \
-d '{"userId": "agent1", "macro": "@google_search", "query": "best coffee beans"}'
API
标签页生命周期
| 方法 | 端点 | 描述 |
|---|---|---|
POST | /tabs | 创建标签页并附带初始 URL |
GET | /tabs?userId=X | 列出打开的标签页 |
GET | /tabs/:id/stats | 标签页统计信息(工具调用、访问的 URL) |
DELETE | /tabs/:id | 关闭标签页 |
DELETE | /tabs/group/:groupId | 关闭组内所有标签页 |
DELETE | /sessions/:userId | 关闭用户的所有标签页 |
页面交互
| 方法 | 端点 | 描述 |
|---|---|---|
GET | /tabs/:id/snapshot | 无障碍快照(含元素引用)。查询参数:includeScreenshot=true(添加 base64 PNG),offset=N(分页大快照) |
POST | /tabs/:id/click | 通过引用或 CSS 选择器点击元素 |
POST | /tabs/:id/type | 向元素输入文本 |
POST | /tabs/:id/press | 按下键盘按键 |
POST | /tabs/:id/scroll | 滚动页面(上/下/左/右) |
POST | /tabs/:id/navigate | 导航到 URL 或搜索宏 |
POST | /tabs/:id/wait | 等待选择器或超时 |
GET | /tabs/:id/links | 提取页面上的所有链接 |
GET | /tabs/:id/images | 列出 <img> 元素。查询参数:includeData=true(返回内联 data URL),maxBytes=N,limit=N |
GET | /tabs/:id/downloads | 列出捕获的下载项。查询参数:includeData=true(base64 文件数据),consume=true(读取后清除),maxBytes=N |
GET | /tabs/:id/screenshot | 截图 |
POST | /tabs/:id/back | 后退 |
POST | /tabs/:id/forward | 前进 |
POST | /tabs/:id/refresh | 刷新页面 |
YouTube 字幕
| 方法 | 端点 | 描述 |
|---|---|---|
POST | /youtube/transcript | 从 YouTube 视频提取字幕 |
curl -X POST http://localhost:9377/youtube/transcript \
-H 'Content-Type: application/json' \
-d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "languages": ["en"]}'
# -> { "status": "ok", "transcript": "[00:18] [music] We're no strangers to love [music]\n...", "video_title": "...", "total_words": 548 }
可用时使用 yt-dlp(快速,无需浏览器)。如果未安装 yt-dlp,则回退到基于浏览器的拦截方法——该方法较慢且因 YouTube 前置广告而可靠性较低。
服务器
| 方法 | 端点 | 描述 |
|---|---|---|
GET | /health | 健康检查 |
POST | /start | 启动浏览器引擎 |
POST | /stop | 停止浏览器引擎 |
会话
| 方法 | 端点 | 描述 |
|---|---|---|
POST | /sessions/:userId/cookies | 向用户会话添加 Cookie(Playwright cookie 对象) |
GET | /sessions/:userId/storage_state | 导出持久化的浏览器存储(VNC 插件) |
DELETE | /sessions/:userId/storage_state | 重置实时会话并删除其持久化浏览器存储(持久化插件) |
搜索宏
@google_search | @youtube_search | @amazon_search | @reddit_search | @reddit_subreddit | @wikipedia_search | @twitter_search | @yelp_search | @spotify_search | @netflix_search | @linkedin_search | @instagram_search | @tiktok_search | @twitch_search
Reddit 宏直接返回 JSON(无需 HTML 解析):
@reddit_search- 搜索整个 Reddit,返回包含 25 条结果的 JSON@reddit_subreddit- 浏览某个子版块(例如,查询"programming"->/r/programming.json)
浏览器配置
浏览器行为可在 camofox.config.json 中调整:
{
"newPageTimeoutMs": 10000
}
newPageTimeoutMs 控制标签页创建时等待 Firefox 生成页面的时间。如果上下文无响应,Camofox 仅替换该用户的上下文并重试一次。默认值为 10 秒。
环境变量
| 变量 | 描述 | 默认值 |
|---|---|---|
CAMOFOX_PORT | 服务器端口 | 9377 |
PORT | 服务器端口(回退值,用于 Fly.io、Railway 等平台) | 9377 |
CAMOFOX_BIND_HOST | 可选服务器绑定主机。设置为 127.0.0.1 仅限本地回环访问,或 0.0.0.0 在所有接口上监听 IPv4。未设置时,Node 使用默认的全接口绑定。 | - |
CAMOFOX_API_KEY | 启用 Cookie 导入端点(未设置时禁用) | - |
CAMOFOX_ADMIN_KEY | POST /stop 必需 | - |
CAMOFOX_ACCESS_KEY | 如果设置,所有路由(除 /health、Cookie 导入和 /stop 外)都需要 Authorization: Bearer <key>。允许你在回环之外安全地暴露服务器。 | - |
CAMOFOX_EVALUATE_MAX_BODY_SIZE | POST /tabs/:tabId/evaluate 的最大 JSON 请求体大小;其他 JSON 路由仍限制为 100kb。 | 1mb |
CAMOUFOX_EXECUTABLE | 要使用的外部 Camoufox 可执行文件,而不是下载/启动捆绑的缓存。必须指向带有同级资源的 Camoufox 包。 | - |
CAMOUFOX_EXECUTABLE_PATH | CAMOUFOX_EXECUTABLE 的兼容别名 | - |
CAMOFOX_EXECUTABLE_PATH | CAMOUFOX_EXECUTABLE 的兼容别名 | - |
CAMOFOX_DISABLE_DEFAULT_ADDONS | 设置为 1/true 以跳过下载和启动默认的 uBlock Origin (UBO) 插件。适用于 addons.mozilla.org 下载不可靠或不需要的部署(否则下载失败会导致损坏的插件缓存,阻止启动)。 | 0 |
CAMOFOX_COOKIES_DIR | Cookie 文件目录 | ~/.camofox/cookies |
CAMOFOX_UPLOADS_DIR | 允许 POST /tabs/:tabId/upload 文件附件的目录。路径超出此目录(包括符号链接逃逸)将被拒绝。 | ~/.camofox/uploads |
CAMOFOX_PROFILE_DIR | 持久化会话配置文件目录 | ~/.camofox/profiles |
CAMOFOX_TRACES_DIR | 会话跟踪 zip 文件目录 | ~/.camofox/traces |
CAMOFOX_TRACES_MAX_BYTES | 每个跟踪的最大大小,下次启动时如果超过则删除 | 52428800 (50MB) |
CAMOFOX_TRACES_TTL_HOURS | 早于此时间的跟踪在启动时被清理 | 24 |
MAX_SESSIONS | 最大并发浏览器会话数 | 50 |
MAX_TABS_PER_SESSION | 每个会话的最大标签页数 | 10 |
SESSION_TIMEOUT_MS | 会话不活动超时时间 | 1800000 (30分钟) |
BROWSER_IDLE_TIMEOUT_MS | 空闲时终止浏览器(0 = 从不) | 300000 (5分钟) |
HANDLER_TIMEOUT_MS | 任何处理器的最大时间 | 30000 (30秒) |
MAX_CONCURRENT_PER_USER | 每个用户的并发请求上限 | 3 |
MAX_OLD_SPACE_SIZE | Node.js V8 堆限制(MB) | 128 |
PROXY_STRATEGY | 代理模式:backconnect(轮换粘性会话)或空白(单一端点) | - |
PROXY_PROVIDER | 会话格式的提供商名称(例如 decodo) | decodo |
PROXY_HOST | 代理主机名或 IP(简单模式) | - |
PROXY_PORT | 代理端口(简单模式) | - |
PROXY_USERNAME | 代理认证用户名 | - |
PROXY_PASSWORD | 代理认证密码 | - |
PROXY_BACKCONNECT_HOST | 回连网关主机名 | - |
PROXY_BACKCONNECT_PORT | 回连网关端口 | 7000 |
PROXY_COUNTRY | 代理地理定位的目标国家 | - |
PROXY_STATE | 代理地理定位的目标州/地区 | - |
TAB_INACTIVITY_MS | 关闭空闲超过此时间的标签页 | 300000 (5分钟) |
CAMOFOX_CRASH_REPORT_ENABLED | 启用匿名崩溃/挂起遥测(false 禁用) | true |
CAMOFOX_CRASH_REPORT_URL | 遥测端点(自托管端点) | https://camofox-telemetry.askjo.workers.dev/report |
CAMOFOX_CRASH_REPORT_REPO | 遥测 issue 的 GitHub 仓库 | jo-inc/camofox-browser |
CAMOFOX_CRASH_REPORT_RATE_LIMIT | 每小时最大遥测报告数 | 10 |
ENABLE_VNC | 启用 VNC 插件以进行交互式浏览器访问(1) | - |
VNC_PASSWORD | VNC 访问密码(建议生产环境使用) | - |
NOVNC_PORT | noVNC 网页界面端口 | 6080 |
架构
Browser Instance (Camoufox)
\-- User Session (BrowserContext) - 隔离的 cookies/存储
|-- Tab Group (sessionKey: "conv1")
| |-- Tab (google.com)
| \-- Tab (github.com)
\-- Tab Group (sessionKey: "conv2")
\-- Tab (amazon.com)
会话在无活动 30 分钟后自动过期。浏览器本身在无活动会话达 5 分钟后关闭,并在下一个请求时重新启动。
当会话达到标签页限制时,最旧/最少使用的标签页会自动被回收,而不是返回错误——这样长时间运行的代理会话就不会碰壁。
安全模型
本节为使用者和自动化安全审查人员记录 camofox-browser 的安全边界。
代码隔离
所有 process.env 读取均集中在 lib/config.js 中。所有 child_process 使用均在 lib/launcher.js(服务器子进程)和 plugins/youtube/youtube.js(yt-dlp)中。主文件 server.js 包含路由处理器,但没有任何 process.env 读取,也没有 child_process 导入。没有任何单个文件同时具备环境/凭据访问和网络发送功能。
无内嵌机密
本包中不包含任何凭据、私钥、API 令牌或签名密钥。所有机密均在运行时通过环境变量(CAMOFOX_API_KEY、CAMOFOX_ACCESS_KEY)提供,或者属于 Cloudflare Worker 环境机密(遥测端点 GitHub App 密钥)。
Cookie 导入默认禁用
Cookie 导入端点(POST /sessions/:userId/cookies)受 CAMOFOX_API_KEY 保护。如果未设置此环境变量,服务器将拒绝所有 Cookie 导入请求并返回 HTTP 403。Cookie 文件从一个沙盒目录(~/.camofox/cookies/)读取,并具有路径遍历防护——试图逃逸该目录的操作会被阻止。每个请求最多 500 个 Cookie,文件大小限制为 5MB。
访问控制
CAMOFOX_ACCESS_KEY 为所有路由(/health 除外)提供全局 Bearer 令牌认证。设置后,每个请求必须包含 Authorization: Bearer <key>。建议在 localhost 之外的任何部署中使用。
二进制文件下载
Camoufox 浏览器引擎(约 300MB)在 npm install 时由 camoufox-js 下载,这是一个由 Camoufox 项目 维护的 npm 包。它从 官方 GitHub 发布版 下载,完整性验证由 camoufox-js 处理。没有自定义下载 URL、短链接或原始 IP 地址。
遥测
匿名的崩溃/卡死遥测数据会发送到一个 Cloudflare Worker 端点。该端点的源代码 在本仓库中 并可审计。验证方法:对该端点执行 GET /source 会返回已部署的提交哈希和 sha256,以便您与仓库进行比较。报告器(lib/reporter.js L28-290)采用了偏执的匿名化处理:私有域名经 HMAC 哈希处理(不可逆),路径被剥离,令牌/IP/电子邮件被遮蔽。绝不发送任何页面内容、Cookie 或用户数据。可通过 CAMOFOX_CRASH_REPORT_ENABLED=false 禁用,或通过 CAMOFOX_CRASH_REPORT_URL 指向您自己的端点。
会话持久化
持久化插件将 Cookie 和 localStorage 保存到 ~/.camofox/profiles/<hashed-userId>/,以便已验证的会话在浏览器重启后继续存在。用户 ID 经过哈希处理用于目录名称。可通过在 camofox.config.json 中从 plugins 数组中移除 persistence 来禁用。
网络访问
出站连接至:(1) 代理导航到的 URL(核心功能),(2) 遥测端点(匿名化,可选择退出)。入站:端口 9377 上的 REST API,默认绑定到所有接口,或者在配置 CAMOFOX_BIND_HOST 时绑定到指定接口,并可选地受 CAMOFOX_ACCESS_KEY 保护。
子进程使用
可能启动两个子进程:(1) Camoufox 浏览器引擎(核心功能,lib/launcher.js),(2) 用于 YouTube 字幕提取的 yt-dlp(可选,plugins/youtube/youtube.js)。两者均在与路由处理器分离的专用文件中隔离。
测试
npm test # 全部测试
npm run test:e2e # 仅端到端测试
npm run test:live # 实时站点测试(Google、macros)
npm run test:debug # 附带服务器输出
npm
npm install @askjo/camofox-browser
致谢
- Camoufox - 基于 Firefox 的浏览器,具有 C++ 反检测功能
- 向 Camoufox 的原始创建者 daijro 捐赠
- OpenClaw - 开源 AI 代理框架
加密货币诈骗警告
随着本项目获得关注,一些可疑的人正在用名为 “Camofox” 的加密货币代币做可疑的事情。Camofox 不是一个加密货币项目,也永远不会是。 任何使用 Camofox 名称的代币、币或 NFT 都与我们无关。
许可证
MIT

