camofox-browser
为AI智能体打造的反检测浏览器服务器,基于Camoufox构建
站在Camoufox的坚实基础之上——这是一个Firefox分支,在C++层面实现指纹欺骗。
由jo背后的团队打造,jo是一款个人AI智能体,一半运行在您的Mac上,一半运行在专为您配置的专属云服务器上——无需任何维护。支持macOS、***、***和电子邮件。免费试用测试版 ->
git clone https://github.com/jo-inc/camofox-browser && cd camofox-browser
npm install && npm start
# -> http://localhost:9377
[!NOTE] 建议使用 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,未来的克隆会自动处理此问题。
[!WARNING] 不要直接运行
docker build。Dockerfile 使用绑定挂载从dist/拉取预下载的二进制文件。请始终使用make up(或make fetch然后make build)—— 这会先下载二进制文件。
对于 Fly.io 或其他远程 CI,您需要一个在构建时下载二进制文件而非使用绑定挂载的 Dockerfile。
已包含 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 到 Camoufox,以跳过 LinkedIn、Amazon 等网站的交互式登录。
1. 生成密钥:
# macOS / Linux
openssl rand -hex 32
2. 在启动 OpenClaw 前设置环境变量:
export CAMOFOX_API_KEY="your-generated-key"
openclaw start
插件(用于验证请求)和服务器(用于验证请求)使用相同的密钥。两者在同一环境中运行——只需设置一次。
[!NOTE] 为什么使用环境变量?密钥是敏感信息。
openclaw.json中的插件配置以明文存储,因此不应在其中存放密钥。请在 shell 配置文件、systemd 单元、Docker 环境或 Fly.io 密钥中设置CAMOFOX_API_KEY。
[!NOTE] 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. 让代理导入 Cookie:
从 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
| Body: { cookies: [Playwright cookie objects] }
v
camofox 服务器 (验证、清理、注入)
|
v context.addCookies(...)
|
Camoufox 浏览器会话 (已认证浏览)
cookiesPath 相对于 Cookie 目录解析——阻止目录外的路径遍历默认情况下,camofox 将每个用户的 Cookie 和 localStorage 持久化到 ~/.camofox/profiles/。会话在浏览器重启后仍然保留——登录一次(通过 Cookie 或 VNC),后续会话会自动恢复已认证状态。
~/.camofox/
|-- cookies/ # Cookie 引导文件(Netscape 格式)
\-- profiles/ # 持久化会话状态(自动管理)
\-- /
\-- storage_state.json
可通过 CAMOFOX_PROFILE_DIR 覆盖目录,或在持久化插件配置中设置 "profileDir"。要禁用持久化,请在 camofox.config.json 中设置 "persistence": { "enabled": false }。
捕获会话中每个操作的 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 + 控制台 + 截图)。
无法在现有会话上切换追踪。如果需要更改标志,请先执行 DELETE /sessions/:userId。
存储默认位于 ~/.camofox/traces/,并在服务器启动时清理:
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 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"
通过代理路由所有浏览器流量,并通过 Camoufox 的内置 GeoIP 根据代理的 IP 地址自动获取区域设置、时区和地理位置。
简单代理(单个端点):
浏览器自动化的失败方式难以预测——Cloudflare验证、网站重新设计导致选择器失效、重定向循环、弹窗风暴、渲染器崩溃等。其影响范围广泛,失败模式多样。没有遥测功能时,唯一的信号就是“它不工作了”。
遥测为我们提供结构化数据,包括哪些网站失败、如何失败以及失败频率,以便我们优先修复那些实际影响用户的问题模式。当出现以下情况时,它会自动创建GitHub Issue:
每份报告包含失败类型、堆栈跟踪、标签页健康计数器(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)会在部署时注入提交记录和源代码哈希——每次部署都可在https://github.com/jo-inc/camofox-browser/actions/workflows/telemetry-deploy.yml%E4%B8%AD%E5%AE%A1%E8%AE%A1%E3%80%82
或者完全跳过验证:CAMOFOX_CRASH_REPORT_ENABLED=false 可禁用所有遥测,或通过 CAMOFOX_CRASH_REPORT_URL 指向您自己的端点。
所有报告数据在离开进程前均经过严格的***化处理(lib/reporter.js 第28-290行):
site-a1b2c3d4)——不同报告中同一域名的哈希值相同,便于关联分析,但无法反推原始域名。路径段会变为*/*/*(仅保留深度)。查询参数会变为?[3](仅保留数量)。绝不会包含任何键、值或路径内容。 /server.js) 重复问题通过堆栈签名检测,会添加 +1 评论而非创建新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——https://github.com/settings/apps/new
.pem 文件github.com/settings/installations/{id})部署端点——克隆本仓库并部署worker:
cd workers/crash-reporter
# 编辑wrangler.toml:将account_id设置为您的Cloudflare账户ID
npx wrangler deploy
该worker是单个TypeScript文件,无npm依赖。它也可在Deno、Bun或任何支持Web *** API的运行时环境中运行。
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
export CAMOFOX_CRASH_REPORT_URL=https://your-worker.your-subdomain.workers.dev/report
curl https://your-worker.your-subdomain.workers.dev/health
# -> {"status":"ok"}
{"ts":"2026-02-11T23:45:01.234Z","level":"info","msg":"req","reqId":"a1b2c3d4","method":"POST","path":"/tabs","userId":"agent1"}
{"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"}'
| 方法 | 端点 | 描述 |
|---|---|---|
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 | 列出``元素。查询参数:includeData=true(返回内联数据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 | 刷新页面 |
| 方法 | 端点 | 描述 |
|---|---|---|
POST | /youtube/transcript | 从***视频提取字幕 |
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 }
如果已安装https://github.com/***/***%EF%BC%8C%E5%88%99%E4%BD%BF%E7%94%A8%E8%AF%A5%E5%B7%A5%E5%85%B7%EF%BC%88%E9%80%9F%E5%BA%A6%E5%BF%AB%EF%BC%8C%E6%97%A0%E9%9C%80%E6%B5%8F%E8%A7%88%E5%99%A8%EF%BC%89%E3%80%82%E5%A6%82%E6%9E%9C%E6%9C%AA%E5%AE%89%E8%A3%85***%EF%BC%8C%E5%88%99%E5%9B%9E%E9%80%80%E5%88%B0%E5%9F%BA%E4%BA%8E%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E6%8B%A6%E6%88%AA%E6%96%B9%E6%B3%95%E2%80%94%E2%80%94%E7%94%B1%E4%BA%8E***%E5%B9%BF%E5%91%8A%E5%89%8D%E8%B4%B4%E7%89%87%E7%9A%84%E5%AD%98%E5%9C%A8%EF%BC%8C%E6%AD%A4%E6%96%B9%E6%B3%95%E9%80%9F%E5%BA%A6%E8%BE%83%E6%85%A2%E4%B8%94%E5%8F%AF%E9%9D%A0%E6%80%A7%E8%BE%83%E4%BD%8E%E3%80%82
| 方法 | 端点 | 描述 |
|---|---|---|
GET | /health | 健康检查 |
POST | /start | 启动浏览器引擎 |
POST | /stop | 停止浏览器引擎 |
| 方法 | 端点 | 描述 |
|---|---|---|
POST | /sessions/:userId/cookies | 向用户会话添加Cookie(Playwright Cookie对象) |
GET | /sessions/:userId/storage_state | 导出Cookie + localStorage(VNC插件) |
@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)npm install @askjo/camofox-browser
随着本项目受到关注,不良分子开始利用名为“Camoufox”的加密货币从事不良行为。Camofox不是加密货币项目,也永远不会成为加密货币项目。 任何使用Camofox名称的、硬币或NFT均与我们无关。
MIT
探索更多轩辕镜像的使用方法,找到最适合您系统的配置方式
通过 Docker 登录认证访问私有仓库
无需登录使用专属域名
Kubernetes 集群配置 Containerd
K3s 轻量级 Kubernetes 镜像加速
VS Code Dev Containers 配置
Podman 容器引擎配置
HPC 科学计算容器配置
ghcr、Quay、nvcr 等镜像仓库
Harbor Proxy Repository 对接专属域名
Portainer Registries 加速拉取
Nexus3 Docker Proxy 内网缓存
需要其他帮助?请查看我们的 常见问题Docker 镜像访问常见问题解答 或 提交工单
docker search 限制
站内搜不到镜像
离线 save/load
插件要用 plugin install
WSL 拉取慢
安全与 digest
新手拉取配置
镜像合规机制
不支持 push
manifest unknown
no matching manifest(架构)
invalid tar header(解压)
TLS 证书失败
DNS 超时
域名连通性排查
410 Gone 排查
402 与流量用尽
401 认证失败
429 限流
D-Bus 凭证提示
413 与超大单层
来自真实用户的反馈,见证轩辕镜像的优质服务