jo-inc/camofox-browserPublic

Stealth headless browser for AI agents — bypass Cloudflare, bot detection, and anti-scraping. Drop-in Puppeteer/Playwright replacement.

AI summary: An anti-detection browser server that provides fingerprint spoofing at the C++ level for AI agents.

Stars
11.3K
+80 today
Forks
1.1K
Watchers
30
Open issues
114
Open PRs
31
Contributors
~57
Commits
581
Branches
27

JavaScriptMITCreated Jan 26, 2026Last push 2d agoLatest release v1.18.0+164 stars this week+1.7K this month

Quick answers

What is camofox-browser?
An anti-detection browser server that provides fingerprint spoofing at the C++ level for AI agents.
What does camofox-browser do?
camofox-browser is a specialized server component built upon Camoufox, a Firefox fork engineered for deep anti-detection capabilities. It provides a robust browsing environment specifically designed for autonomous AI agents that need to navigate the web without being blocked by anti-bot measures. The core engine spoofs browser fingerprints at the underlying C++ level, making automated traffic indistinguishable from human browsing. This allows agents to reliably scrape data or interact with modern web applications that employ stringent security checks. The project is maintained by the team behind the Jo personal AI agent, ensuring it meets real-world demands for autonomous web interaction.
Who is camofox-browser for?
Developers building autonomous AI agents, web scrapers, and privacy researchers requiring advanced anti-detection capabilities. Users should be familiar with managing server-side browser automation.
How do I get started with camofox-browser?
git clone https://github.com/jo-inc/camofox-browser && cd camo
How popular is camofox-browser on GitHub?
jo-inc/camofox-browser has 11,343 stars and 1,127 forks on GitHub, and gained 164 stars in the last 7 days.
What license does camofox-browser use?
jo-inc/camofox-browser is released under the MIT license.

Star history

since Sep 7, 2026
05K10KSep 2026Sep 2026Sep 2026Oct 2026
11.3K stars as of Oct 2, 2026. Measured daily since Sep 7, 2026; GitHub no longer exposes earlier star timestamps.

Contribution activity

commits per day, last 52 weeks
OctNovDecJanFebMarAprMayJunJulAugSepMonWedFri2025-10-04: 0 commits2025-10-05: 0 commits2025-10-06: 0 commits2025-10-07: 0 commits2025-10-08: 0 commits2025-10-09: 0 commits2025-10-10: 0 commits2025-10-11: 0 commits2025-10-12: 0 commits2025-10-13: 0 commits2025-10-14: 0 commits2025-10-15: 0 commits2025-10-16: 0 commits2025-10-17: 0 commits2025-10-18: 0 commits2025-10-19: 0 commits2025-10-20: 0 commits2025-10-21: 0 commits2025-10-22: 0 commits2025-10-23: 0 commits2025-10-24: 0 commits2025-10-25: 0 commits2025-10-26: 0 commits2025-10-27: 0 commits2025-10-28: 0 commits2025-10-29: 0 commits2025-10-30: 0 commits2025-10-31: 0 commits2025-11-01: 0 commits2025-11-02: 0 commits2025-11-03: 0 commits2025-11-04: 0 commits2025-11-05: 0 commits2025-11-06: 0 commits2025-11-07: 0 commits2025-11-09: 0 commits2025-11-10: 0 commits2025-11-11: 0 commits2025-11-12: 0 commits2025-11-13: 0 commits2025-11-14: 0 commits2025-11-15: 0 commits2025-11-16: 0 commits2025-11-17: 0 commits2025-11-18: 0 commits2025-11-19: 0 commits2025-11-20: 0 commits2025-11-21: 0 commits2025-11-22: 0 commits2025-11-23: 0 commits2025-11-24: 0 commits2025-11-25: 0 commits2025-11-26: 0 commits2025-11-27: 0 commits2025-11-28: 0 commits2025-11-29: 0 commits2025-11-30: 0 commits2025-12-01: 0 commits2025-12-02: 0 commits2025-12-03: 0 commits2025-12-04: 0 commits2025-12-05: 0 commits2025-12-06: 0 commits2025-12-07: 0 commits2025-12-08: 0 commits2025-12-09: 0 commits2025-12-10: 0 commits2025-12-11: 0 commits2025-12-12: 0 commits2025-12-13: 0 commits2025-12-14: 0 commits2025-12-15: 0 commits2025-12-16: 0 commits2025-12-17: 0 commits2025-12-18: 0 commits2025-12-19: 0 commits2025-12-20: 0 commits2025-12-21: 0 commits2025-12-22: 0 commits2025-12-23: 0 commits2025-12-24: 0 commits2025-12-25: 0 commits2025-12-26: 0 commits2025-12-27: 0 commits2025-12-28: 0 commits2025-12-29: 0 commits2025-12-30: 0 commits2025-12-31: 0 commits2026-01-01: 0 commits2026-01-02: 0 commits2026-01-03: 0 commits2026-01-04: 0 commits2026-01-05: 0 commits2026-01-06: 0 commits2026-01-07: 0 commits2026-01-08: 0 commits2026-01-09: 0 commits2026-01-10: 0 commits2026-01-11: 0 commits2026-01-12: 0 commits2026-01-13: 0 commits2026-01-14: 0 commits2026-01-15: 0 commits2026-01-16: 0 commits2026-01-17: 0 commits2026-01-18: 0 commits2026-01-19: 0 commits2026-01-20: 0 commits2026-01-21: 0 commits2026-01-22: 0 commits2026-01-23: 0 commits2026-01-24: 0 commits2026-01-25: 0 commits2026-01-26: 0 commits2026-01-27: 0 commits2026-01-28: 0 commits2026-01-29: 0 commits2026-01-30: 0 commits2026-01-31: 0 commits2026-02-01: 0 commits2026-02-02: 0 commits2026-02-03: 0 commits2026-02-04: 0 commits2026-02-05: 0 commits2026-02-06: 0 commits2026-02-07: 0 commits2026-02-08: 0 commits2026-02-09: 0 commits2026-02-10: 11 commits2026-02-11: 14 commits2026-02-12: 7 commits2026-02-13: 0 commits2026-02-14: 1 commit2026-02-15: 1 commit2026-02-16: 1 commit2026-02-17: 1 commit2026-02-18: 0 commits2026-02-19: 0 commits2026-02-20: 0 commits2026-02-21: 9 commits2026-02-22: 2 commits2026-02-23: 2 commits2026-02-24: 0 commits2026-02-25: 2 commits2026-02-26: 1 commit2026-02-27: 0 commits2026-02-28: 0 commits2026-03-01: 1 commit2026-03-02: 0 commits2026-03-03: 0 commits2026-03-04: 1 commit2026-03-05: 0 commits2026-03-06: 0 commits2026-03-07: 0 commits2026-03-08: 6 commits2026-03-09: 1 commit2026-03-10: 0 commits2026-03-11: 0 commits2026-03-12: 0 commits2026-03-13: 4 commits2026-03-14: 1 commit2026-03-15: 0 commits2026-03-16: 0 commits2026-03-17: 1 commit2026-03-18: 0 commits2026-03-19: 1 commit2026-03-20: 4 commits2026-03-21: 0 commits2026-03-22: 3 commits2026-03-23: 0 commits2026-03-24: 0 commits2026-03-25: 0 commits2026-03-26: 1 commit2026-03-27: 1 commit2026-03-28: 2 commits2026-03-29: 8 commits2026-03-30: 2 commits2026-03-31: 1 commit2026-04-01: 1 commit2026-04-02: 1 commit2026-04-03: 0 commits2026-04-04: 0 commits2026-04-05: 8 commits2026-04-06: 5 commits2026-04-07: 0 commits2026-04-08: 0 commits2026-04-09: 0 commits2026-04-10: 0 commits2026-04-11: 0 commits2026-04-12: 1 commit2026-04-13: 1 commit2026-04-14: 0 commits2026-04-15: 1 commit2026-04-16: 0 commits2026-04-17: 3 commits2026-04-18: 30 commits2026-04-19: 0 commits2026-04-20: 2 commits2026-04-21: 0 commits2026-04-22: 0 commits2026-04-23: 1 commit2026-04-24: 0 commits2026-04-25: 43 commits2026-04-26: 10 commits2026-04-27: 11 commits2026-04-28: 32 commits2026-04-29: 6 commits2026-04-30: 1 commit2026-05-01: 3 commits2026-05-02: 0 commits2026-05-03: 1 commit2026-05-04: 4 commits2026-05-05: 3 commits2026-05-06: 19 commits2026-05-07: 2 commits2026-05-08: 2 commits2026-05-09: 0 commits2026-05-10: 12 commits2026-05-11: 1 commit2026-05-12: 2 commits2026-05-13: 2 commits2026-05-14: 2 commits2026-05-15: 0 commits2026-05-16: 1 commit2026-05-17: 0 commits2026-05-18: 1 commit2026-05-19: 0 commits2026-05-20: 0 commits2026-05-21: 1 commit2026-05-22: 1 commit2026-05-23: 12 commits2026-05-24: 6 commits2026-05-25: 0 commits2026-05-26: 0 commits2026-05-27: 0 commits2026-05-28: 1 commit2026-05-29: 0 commits2026-05-30: 5 commits2026-05-31: 5 commits2026-06-01: 1 commit2026-06-02: 6 commits2026-06-03: 3 commits2026-06-04: 0 commits2026-06-05: 0 commits2026-06-06: 0 commits2026-06-07: 0 commits2026-06-08: 0 commits2026-06-09: 0 commits2026-06-10: 1 commit2026-06-11: 1 commit2026-06-12: 0 commits2026-06-13: 0 commits2026-06-14: 1 commit2026-06-15: 1 commit2026-06-16: 1 commit2026-06-17: 1 commit2026-06-18: 1 commit2026-06-19: 0 commits2026-06-20: 0 commits2026-06-21: 0 commits2026-06-22: 0 commits2026-06-23: 0 commits2026-06-24: 0 commits2026-06-25: 0 commits2026-06-26: 0 commits2026-06-27: 0 commits2026-06-28: 0 commits2026-06-29: 0 commits2026-06-30: 0 commits2026-07-01: 0 commits2026-07-02: 3 commits2026-07-03: 0 commits2026-07-04: 3 commits2026-07-05: 0 commits2026-07-06: 0 commits2026-07-07: 2 commits2026-07-08: 0 commits2026-07-09: 0 commits2026-07-10: 0 commits2026-07-11: 1 commit2026-07-12: 0 commits2026-07-13: 0 commits2026-07-14: 0 commits2026-07-15: 0 commits2026-07-16: 0 commits2026-07-17: 4 commits2026-07-18: 1 commit2026-07-19: 5 commits2026-07-20: 15 commits2026-07-21: 4 commits2026-07-22: 0 commits2026-07-23: 0 commits2026-07-24: 0 commits2026-07-25: 0 commits2026-07-26: 1 commit2026-07-27: 1 commit2026-07-28: 0 commits2026-07-29: 0 commits2026-07-30: 1 commit2026-07-31: 0 commits2026-08-01: 17 commits2026-08-02: 0 commits2026-08-03: 0 commits2026-08-04: 2 commits2026-08-05: 0 commits2026-08-06: 0 commits2026-08-07: 0 commits2026-08-08: 0 commits2026-08-09: 0 commits2026-08-10: 0 commits2026-08-11: 1 commit2026-08-12: 0 commits2026-08-13: 2 commits2026-08-14: 0 commits2026-08-15: 0 commits2026-08-16: 0 commits2026-08-17: 1 commit2026-08-18: 0 commits2026-08-19: 12 commits2026-08-20: 1 commit2026-08-21: 0 commits2026-08-22: 0 commits2026-08-23: 1 commit2026-08-24: 0 commits2026-08-25: 0 commits2026-08-26: 0 commits2026-08-27: 0 commits2026-08-28: 0 commits2026-08-29: 2 commits2026-08-30: 0 commits2026-08-31: 0 commits2026-09-01: 0 commits2026-09-02: 0 commits2026-09-03: 11 commits2026-09-04: 7 commits2026-09-05: 2 commits2026-09-06: 0 commits2026-09-07: 2 commits2026-09-08: 6 commits2026-09-09: 5 commits2026-09-10: 2 commits2026-09-11: 0 commits2026-09-12: 0 commits2026-09-13: 9 commits2026-09-14: 4 commits2026-09-15: 1 commit2026-09-16: 4 commits2026-09-17: 0 commits2026-09-18: 2 commits2026-09-19: 1 commit2026-09-20: 0 commits2026-09-21: 0 commits2026-09-22: 3 commits2026-09-23: 0 commits2026-09-24: 0 commits2026-09-25: 0 commits2026-09-26: 0 commits2026-09-27: 0 commits2026-09-28: 1 commit2026-09-29: 0 commits2026-09-30: 12 commits2026-10-01: 0 commits2026-10-02: 3 commits2026-10-03: 0 commits
505 commits in the last yearLessMore

Signals and awards

derived from tracked data
  • Widely adopted

    11,343 stars

  • Very active

    505 commits in 52 weeks

  • Permissive license

    MIT

  • Continuous integration

    Automated checks passing

  • Repeat trending

    3 trending appearances

What camofox-browser does

camofox-browser is a specialized server component built upon Camoufox, a Firefox fork engineered for deep anti-detection capabilities. It provides a robust browsing environment specifically designed for autonomous AI agents that need to navigate the web without being blocked by anti-bot measures. The core engine spoofs browser fingerprints at the underlying C++ level, making automated traffic indistinguishable from human browsing. This allows agents to reliably scrape data or interact with modern web applications that employ stringent security checks. The project is maintained by the team behind the Jo personal AI agent, ensuring it meets real-world demands for autonomous web interaction.

Developers building autonomous AI agents, web scrapers, and privacy researchers requiring advanced anti-detection capabilities. Users should be familiar with managing server-side browser automation.

  • C++ level spoofing: Modifies core browser rendering and execution engines to fundamentally alter device fingerprints.
  • Anti-detection architecture: Specifically designed to bypass modern web application firewalls and anti-bot systems.
  • AI agent integration: Serves as a reliable, stealthy backend browser instance for automated and autonomous agents.
  • Firefox foundation: Built upon the mature and standard-compliant Camoufox fork of the Mozilla Firefox browser.
  • Server deployment: Operates as a server component to easily integrate with distributed AI architectures.

Where teams use it

Empowering autonomous agents

Provide a reliable browsing backend for AI agents to interact with complex web applications without being blocked.

Advanced web scraping

Extract data from strictly guarded websites by utilizing deep fingerprint spoofing to appear as legitimate human traffic.

Automated testing

Conduct robust end-to-end testing against security-conscious platforms without triggering defensive lockouts.

Privacy research

Investigate web tracking and fingerprinting mechanisms using a fully configurable and modifiable browser stack.

Getting started: git clone https://github.com/jo-inc/camofox-browser && cd camo

README

master branch
camofox-browser

camofox-browser

Anti-detection browser server for AI agents, powered by Camoufox

License: MIT GitHub stars npm version GitHub last commit

Standing on the mighty shoulders of Camoufox - a Firefox fork with fingerprint spoofing at the C++ level.


Jo

Built by the team behind jo, a personal AI agent that runs half on your Mac, half on a dedicated cloud machine just for you -- with zero maintenance needed. Available on macOS, Telegram, WhatsApp, and email. Try the beta free ->

Pradeep Elankumaran

Pradeep Elankumaran (@pradeep24) — co-founder and technical CEO at Jo.



git clone https://github.com/jo-inc/camofox-browser && cd camofox-browser
npm install && npm start
# -> http://localhost:9377

Why

AI agents need to browse the real web. Playwright gets blocked. Headless Chrome gets fingerprinted. Stealth plugins become the fingerprint.

Camoufox patches Firefox at the C++ implementation level - navigator.hardwareConcurrency, WebGL renderers, AudioContext, screen geometry, WebRTC - all spoofed before JavaScript ever sees them. No shims, no wrappers, no tells.

This project wraps that engine in a REST API built for agents: accessibility snapshots instead of bloated HTML, stable element refs for clicking, and search macros for common sites.

Features

  • C++ Anti-Detection - bypasses Google, Cloudflare, and most bot detection
  • Element Refs - stable e1, e2, e3 identifiers for reliable interaction
  • Token-Efficient - accessibility snapshots are ~90% smaller than raw HTML
  • Runs on Anything - lazy browser launch + idle shutdown keeps memory at ~40MB when idle. Designed to share a box with the rest of your stack -- Raspberry Pi, $5 VPS, shared infra.
  • Session Isolation - separate cookies/storage per user
  • Cookie Import - inject Netscape-format cookie files for authenticated browsing
  • File Upload - attach files from a configured upload directory without a native OS dialog
  • Proxy + GeoIP - route traffic through residential proxies with automatic locale/timezone
  • Structured Logging - JSON log lines with request IDs for production observability
  • YouTube Transcripts - extract captions from any YouTube video via yt-dlp, no API key needed
  • Search Macros - @google_search, @youtube_search, @amazon_search, @reddit_subreddit, and 10 more
  • Snapshot Screenshots - include a base64 PNG screenshot alongside the accessibility snapshot
  • Large Page Handling - automatic snapshot truncation with offset-based pagination
  • Download Capture - capture browser downloads and fetch them via API (optional inline base64)
  • DOM Image Extraction - list <img> src/alt and optionally return inline data URLs
  • Deploy Anywhere - Docker, Fly.io, Railway
  • VNC Interactive Login - log into sites visually via noVNC, export storage state for agent reuse
  • OpenAPI Docs - auto-generated spec at /openapi.json and interactive docs at /docs
  • Structured Extract - POST /tabs/:tabId/extract with a JSON Schema that maps properties to snapshot refs via x-ref
  • Session Tracing - opt-in per-session Playwright trace capture (screenshots + DOM snapshots + network) with API endpoints to list, fetch, and delete trace zips
  • Telemetry - automatic anonymized crash/hang telemetry via GitHub Issues. Identifies which sites cause failures and common failure patterns. Private domains are HMAC-hashed, paths/params stripped, tokens/IPs redacted. Opt-out with CAMOFOX_CRASH_REPORT_ENABLED=false.

Optional Dependencies

Dependency Purpose Install
yt-dlp YouTube transcript extraction (fast path) pip install yt-dlp or brew install yt-dlp

The Docker image includes yt-dlp. For local dev, install it for the /youtube/transcript endpoint. Without it, the endpoint falls back to a slower browser-based method.

Quick Start

OpenClaw Plugin

openclaw plugins install @askjo/camofox-browser

Tools: camofox_create_tab | camofox_snapshot | camofox_click | camofox_type | camofox_navigate | camofox_scroll | camofox_screenshot | camofox_close_tab | camofox_list_tabs | camofox_import_cookies

Standalone

Run from npm:

npx @askjo/camofox-browser

Or from source:

git clone https://github.com/jo-inc/camofox-browser
cd camofox-browser
npm install
npm start  # downloads Camoufox on first run (~300MB)

Default port is 9377. See Environment Variables for all options.

Note: the postinstall script unsets PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD for itself before fetching the Camoufox binary. Without that override, an exported PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 (common when Playwright is configured to use system Chrome) would silently skip the binary download and crash the server at runtime.

External Camoufox executable: set CAMOUFOX_EXECUTABLE=/path/to/camoufox-bin before npm install and when starting the server to skip the bundled download and launch that executable. Compatibility aliases are CAMOUFOX_EXECUTABLE_PATH and CAMOFOX_EXECUTABLE_PATH. This is useful for NixOS paths such as /nix/store/.../camoufox-bin; the executable must come from a Camoufox bundle that includes properties.json, version.json, and fontconfig/.

Air-gapped or custom binary management: prefer CAMOUFOX_EXECUTABLE when you already have a Camoufox bundle. Otherwise disable the auto-fetch with npm install --ignore-scripts (skips lifecycle scripts for every dependency -- bluntest option) or, more surgically, npm install --omit=optional plus a manual npx camoufox-js fetch step against your mirror. Note that PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install no longer skips the Camoufox download (the postinstall sanitizes the env locally); use --ignore-scripts or CAMOUFOX_EXECUTABLE for that.

Docker

The included Makefile auto-detects your CPU architecture and pre-downloads Camoufox + yt-dlp binaries outside the Docker build, so rebuilds are fast (~30s vs ~3min).

# Build and start (auto-detects arch: aarch64 on M1/M2, x86_64 on Intel)
make up

# Stop and remove the container
make down

# Force a clean rebuild (e.g. after upgrading VERSION/RELEASE)
make reset

# Just download binaries (without building)
make fetch

# Override arch or version explicitly
make up ARCH=x86_64
make up VERSION=135.0.1 RELEASE=beta.24
Windows

On Windows, make is not available. Use the included build.ps1 PowerShell script instead:

# Build and start
.\build.ps1 up

# Stop and remove the container
.\build.ps1 down

# Build image only
.\build.ps1 build

# Force a clean rebuild
.\build.ps1 reset

# Download binaries only (without building)
.\build.ps1 fetch

# Override architecture
.\build.ps1 up -Arch x86_64
.\build.ps1 up -Arch aarch64

Note: PowerShell 7+ (pwsh) is recommended but powershell.exe (Windows PowerShell 5.1) also works. The script requires Docker Desktop for Windows with the WSL2 backend.

Line endings: This project includes a .gitattributes file that forces Unix (LF) line endings for .sh files. If you've already cloned the repo and get sh: not found or set: Illegal option - errors during docker build, run:

Get-ChildItem -Recurse *.sh | ForEach-Object { (Get-Content $_) -join "`n" + "`n" | Set-Content $_ -NoNewline }

This converts shell scripts to LF line endings. Future clones will handle this automatically thanks to .gitattributes.

WARNING: Do not run docker build directly. The Dockerfile uses bind mounts to pull pre-downloaded binaries from dist/. Always use make up (or make fetch then make build) -- it downloads the binaries first.

Fly.io

For Fly.io or other remote CI, you'll need a Dockerfile that downloads binaries at build time instead of using bind mounts.

Railway

A railway.toml is included. It uses Dockerfile.ci (which downloads binaries at build time) and maps Railway's PORT env var to CAMOFOX_PORT automatically.

# Install Railway CLI, then:
railway link
railway up

Set secrets via the Railway dashboard or CLI:

railway variables set CAMOFOX_API_KEY="your-generated-key"

Usage

Cookie Import

Import cookies from your browser into Camoufox to skip interactive login on sites like LinkedIn, Amazon, etc.

Setup

1. Generate a secret key:

# macOS / Linux
openssl rand -hex 32

2. Set the environment variable before starting OpenClaw:

export CAMOFOX_API_KEY="your-generated-key"
openclaw start

The same key is used by both the plugin (to authenticate requests) and the server (to verify them). Both run from the same environment -- set it once.

Why an env var? The key is a secret. Plugin config in openclaw.json is stored in plaintext, so secrets don't belong there. Set CAMOFOX_API_KEY in your shell profile, systemd unit, Docker env, or Fly.io secrets.

Cookie import is disabled by default. If CAMOFOX_API_KEY is not set, the server rejects all cookie requests with 403.

3. Export cookies from your browser:

Install a browser extension that exports Netscape-format cookie files (e.g., "cookies.txt" for Chrome/Firefox). Export the cookies for the site you want to authenticate.

4. Place the cookie file:

mkdir -p ~/.camofox/cookies
cp ~/Downloads/linkedin_cookies.txt ~/.camofox/cookies/linkedin.txt

The default directory is ~/.camofox/cookies/. Override with CAMOFOX_COOKIES_DIR.

5. Ask your agent to import them:

Import my LinkedIn cookies from linkedin.txt

The agent calls camofox_import_cookies -> reads the file -> POSTs to the server with the Bearer token -> cookies are injected into the browser session. Subsequent camofox_create_tab calls to linkedin.com will be authenticated.

How it works
~/.camofox/cookies/linkedin.txt          (Netscape format, on disk)
        |
        v
camofox_import_cookies tool              (parses file, filters by domain)
        |
        v  POST /sessions/:userId/cookies
        |  Authorization: Bearer <CAMOFOX_API_KEY>
        |  Body: { cookies: [Playwright cookie objects] }
        v
camofox server                           (validates, sanitizes, injects)
        |
        v  context.addCookies(...)
        |
Camoufox browser session                 (authenticated browsing)
  • cookiesPath is resolved relative to the cookies directory -- path traversal outside it is blocked
  • Max 500 cookies per request, 5MB file size limit
  • Cookie objects are sanitized to an allowlist of Playwright fields

Session Persistence

By default, camofox persists each user's cookies and localStorage to ~/.camofox/profiles/. Sessions survive browser restarts -- log in once (via cookies or VNC), and subsequent sessions restore the authenticated state automatically.

~/.camofox/
|-- cookies/          # Bootstrap cookie files (Netscape format)
\-- profiles/         # Persisted session state (auto-managed)
    \-- <hashed-userId>/
        \-- storage_state.json

Override the directory with CAMOFOX_PROFILE_DIR or set "profileDir" in the persistence plugin config. To disable persistence, set "persistence": { "enabled": false } in camofox.config.json.

By default, storage state contains cookies and localStorage only. To also persist IndexedDB, set "indexedDB": true in the persistence plugin config. This captures all serializable IndexedDB records—not only authentication data—and may make snapshots significantly larger and checkpoints slower.

Session Tracing

Capture a Playwright trace of every action in a session: page screenshots, DOM snapshots, network requests, and console output. Output is a single .zip file you can open in Playwright's built-in Trace Viewer.

Opt-in per session by passing trace: true when opening the first tab:

curl -X POST http://localhost:9377/tabs \
  -H 'Content-Type: application/json' \
  -d '{"userId":"agent1","sessionKey":"task1","url":"https://example.com","trace":true}'

The trace is written when the session closes. Close the session to flush it, then list, fetch, and view:

# Close the session to flush the trace
curl -X DELETE http://localhost:9377/sessions/agent1

# List trace files
curl http://localhost:9377/sessions/agent1/traces
# {"traces":[{"filename":"trace-2026-04-18T04-05-00-...zip","sizeBytes":42810,"createdAt":...}]}

# Download (Content-Type: application/zip)
curl http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip > session.zip

# View it in Playwright's Trace Viewer
npx playwright show-trace session.zip

# Delete
curl -X DELETE http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip

Why traces instead of video: Camoufox is Firefox-based, and Playwright's recordVideo is Chromium-only. Traces work on Firefox and give you more than video (network + DOM + console + screenshots).

Tracing cannot be toggled on an existing session. DELETE /sessions/:userId first if you need to change the flag.

Storage defaults to ~/.camofox/traces/<hashed-userId>/ and is swept on server startup:

  • CAMOFOX_TRACES_DIR - base directory (default: ~/.camofox/traces)
  • CAMOFOX_TRACES_MAX_BYTES - max size per trace, removed at next startup if exceeded (default: 50MB)
  • CAMOFOX_TRACES_TTL_HOURS - traces older than this are removed at next startup (default: 24)
Standalone server usage
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

For Fly.io:

fly secrets set CAMOFOX_API_KEY="your-generated-key"

For Railway:

railway variables set CAMOFOX_API_KEY="your-generated-key"

Proxy + GeoIP

Route all browser traffic through a proxy with automatic locale, timezone, and geolocation derived from the proxy's IP address via Camoufox's built-in GeoIP.

Simple proxy (single endpoint):

export PROXY_HOST=166.88.179.132
export PROXY_PORT=46040
export PROXY_USERNAME=myuser
export PROXY_PASSWORD=mypass
npm start

Backconnect proxy (rotating sticky sessions):

For providers like Decodo, Bright Data, or Oxylabs that offer a single gateway endpoint with session-based sticky IPs:

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

Each browser context gets a unique sticky session, so different users get different IP addresses. Sessions rotate automatically on proxy errors or Google blocks.

Or in 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

When a proxy is configured:

  • All traffic routes through the proxy
  • Camoufox's GeoIP automatically sets locale, timezone, and geolocation to match the proxy's exit IP
  • Browser fingerprint (language, timezone, coordinates) is consistent with the proxy location

Without a proxy, Camofox does not claim a geolocation or infer one from the host IP. To use a fixed direct-session identity, set both CAMOFOX_LOCALE and CAMOFOX_TIMEZONE; otherwise Camoufox keeps its own identity defaults.

Telemetry

Browser automation fails in ways that are hard to predict -- Cloudflare challenges, site redesigns breaking selectors, redirect loops, dialog storms, renderer crashes. The scope is wide and the failure modes are diverse. Without telemetry, the only signal is "it didn't work."

Telemetry gives us structured data on which sites fail, how they fail, and how often, so we can prioritize fixes for the patterns that actually affect users. It files GitHub Issues automatically when:

  • Uncaught exceptions crash the process
  • Event loop stalls exceed 5 seconds (watchdog detection)
  • Frustration patterns -- 3+ consecutive failures (timeout, dead context, navigation abort) on the same tab

Each report includes the failure type, stack trace, tab health counters (HTTP status histogram, console errors, request failures, redirect depth), and the target URL -- all anonymized.

How it works

Telemetry is sent to a lightweight Cloudflare Worker endpoint at https://camofox-telemetry.askjo.workers.dev. The endpoint holds the GitHub App credentials as environment secrets -- no secrets are shipped in this package.

lib/reporter.js (client, no secrets)
    |  anonymize -> POST https://camofox-telemetry.askjo.workers.dev/report
    v
Cloudflare Worker (holds GitHub App key)
    |  validate -> rate-limit -> dedup -> create GitHub Issue
    v
GitHub Issue created

The endpoint source code is in this repo at workers/crash-reporter/index.ts.

Verification

You don't have to trust us -- verify what the live endpoint is running:

# 1. Ask the endpoint what code it's running
curl https://camofox-telemetry.askjo.workers.dev/source
# -> { "commit": "abc1234", "sha256": "e3b0c44...", "source": "https://github.com/..." }

# 2. Compare the sha256 against the source in this repo
sha256sum workers/crash-reporter/index.ts

# 3. Check the commit matches what CI deployed
#    https://github.com/jo-inc/camofox-browser/actions/workflows/telemetry-deploy.yml
git log --oneline workers/crash-reporter/index.ts | head -1

If the hashes don't match, the endpoint is running different code than what's in the repo. The deploy workflow (.github/workflows/telemetry-deploy.yml) injects the commit and source hash at deploy time -- every deploy is auditable in GitHub Actions.

Or skip verification entirely: CAMOFOX_CRASH_REPORT_ENABLED=false disables all telemetry, or point to your own endpoint with CAMOFOX_CRASH_REPORT_URL.

Privacy

All reported data goes through paranoid anonymization (lib/reporter.js L28-290) before leaving the process:

  • URLs -- well-known public domains (Google, Amazon, Reddit, Cloudflare, etc.) are shown verbatim so we can identify which sites cause problems. Private/unknown domains are replaced with a stable HMAC hash (site-a1b2c3d4) -- same hash across reports for correlation, but not reversible to the original domain. Path segments become */*/* (depth only). Query params become ?[3] (count only). No keys, values, or path content is ever included.
  • File paths -> stripped to filename only (<path>/server.js)
  • Tokens, secrets, API keys -> <token>
  • IPs, emails, env vars -> redacted
  • Docker/Fly machine IDs -> <id>
  • Tab health -- pure counters (crash count, error count, status code histogram). No page content, no URLs, no user data.

Duplicate issues are detected by stack signature and get a +1 comment instead of a new issue.

# Disable telemetry
export CAMOFOX_CRASH_REPORT_ENABLED=false

# Point to your own endpoint (see below)
export CAMOFOX_CRASH_REPORT_URL=https://your-endpoint.example.com/report

# Adjust rate limit (default: 10 per hour)
export CAMOFOX_CRASH_REPORT_RATE_LIMIT=5
Self-hosted telemetry endpoint

To file telemetry reports in your own GitHub repo instead of jo-inc/camofox-browser:

  1. Create a GitHub App -- Settings -> Developer settings -> GitHub Apps -> New

    • Permissions: Repository -> Issues -> Read & Write
    • Uncheck Webhook -> Active (not needed)
    • Click Generate a key -- downloads a .pem file
    • Install the app on your target repo (Install App -> select repo)
    • Note your App ID (number on the app's General page) and Installation ID (from the URL after installing: github.com/settings/installations/{id})
  2. Deploy the endpoint -- clone this repo and deploy the worker:

    cd workers/crash-reporter
    # Edit wrangler.toml: set account_id to your Cloudflare account ID
    npx wrangler deploy

    The worker is a single TypeScript file with zero npm dependencies. It also runs on Deno, Bun, or any runtime with the Web Crypto API.

  3. Set worker secrets:

    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
    # Key must be PKCS#8 DER base64 (not raw PEM)
    openssl pkcs8 -topk8 -inform PEM -outform DER -nocrypt -in your-app.pem | \
      base64 | tr -d '\n' | npx wrangler secret put GH_PRIVATE_KEY
    # File issues in your repo
    echo "your-org/your-repo" | npx wrangler secret put GH_REPO
  4. Point camofox-browser to your endpoint:

    export CAMOFOX_CRASH_REPORT_URL=https://your-worker.your-subdomain.workers.dev/report
  5. Verify:

    curl https://your-worker.your-subdomain.workers.dev/health
    # -> {"status":"ok"}

Structured Logging

All log output is JSON (one object per line) for easy parsing by log aggregators:

{"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 check requests (/health) are excluded from request logging to reduce noise.

Basic Browsing

# Create a tab
curl -X POST http://localhost:9377/tabs \
  -H 'Content-Type: application/json' \
  -d '{"userId": "agent1", "sessionKey": "task1", "url": "https://example.com"}'

# Get accessibility snapshot with element refs
curl "http://localhost:9377/tabs/TAB_ID/snapshot?userId=agent1"
# -> { "snapshot": "[button e1] Submit  [link e2] Learn more", ... }

# Click by ref
curl -X POST http://localhost:9377/tabs/TAB_ID/click \
  -H 'Content-Type: application/json' \
  -d '{"userId": "agent1", "ref": "e1"}'

# Type into an element
curl -X POST http://localhost:9377/tabs/TAB_ID/type \
  -H 'Content-Type: application/json' \
  -d '{"userId": "agent1", "ref": "e2", "text": "hello", "pressEnter": true}'

# Navigate with a search macro
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

Tab Lifecycle

Method Endpoint Description
POST /tabs Create tab with initial URL
GET /tabs?userId=X List open tabs
GET /tabs/:id/stats Tab stats (tool calls, visited URLs)
DELETE /tabs/:id Close tab
DELETE /tabs/group/:groupId Close all tabs in a group
DELETE /sessions/:userId Close all tabs for a user

Page Interaction

Method Endpoint Description
GET /tabs/:id/snapshot Accessibility snapshot with element refs. Query params: includeScreenshot=true (add base64 PNG), offset=N (paginate large snapshots)
POST /tabs/:id/click Click element by ref or CSS selector
POST /tabs/:id/type Type text into element
POST /tabs/:id/press Press a keyboard key
POST /tabs/:id/scroll Scroll page (up/down/left/right)
POST /tabs/:id/navigate Navigate to URL or search macro
POST /tabs/:id/wait Wait for selector or timeout
GET /tabs/:id/links Extract all links on page
GET /tabs/:id/images List <img> elements. Query params: includeData=true (return inline data URLs), maxBytes=N, limit=N
GET /tabs/:id/downloads List captured downloads. Query params: includeData=true (base64 file data), consume=true (clear after read), maxBytes=N
GET /tabs/:id/screenshot Take screenshot
POST /tabs/:id/back Go back
POST /tabs/:id/forward Go forward
POST /tabs/:id/refresh Refresh page

YouTube Transcript

Method Endpoint Description
POST /youtube/transcript Extract captions from a YouTube video
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 }

Uses yt-dlp when available (fast, no browser needed). Falls back to a browser-based intercept method if yt-dlp is not installed -- this is slower and less reliable due to YouTube ad pre-rolls.

Server

Method Endpoint Description
GET /health Health check
POST /start Start browser engine
POST /stop Stop browser engine

Sessions

Method Endpoint Description
GET /tabs/:tabId/downloads List captured browser downloads and saved current-resource PDFs
POST /tabs/:tabId/fetch-current-resource Save the current inline PDF with its browser-session authentication as a download artifact
GET /sessions/:userId/storage_state Export persisted browser storage (VNC plugin)
DELETE /sessions/:userId/storage_state Reset the live session and delete its persisted browser storage (persistence plugin)

Search Macros

@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 macros return JSON directly (no HTML parsing needed):

  • @reddit_search - search all of Reddit, returns JSON with 25 results
  • @reddit_subreddit - browse a subreddit (e.g., query "programming" -> /r/programming.json)

Browser Configuration

Browser behavior can be tuned in camofox.config.json:

{
  "newPageTimeoutMs": 10000
}

newPageTimeoutMs controls how long tab creation waits for Firefox to create a page. If the context is unresponsive, Camofox replaces only that user's context and retries once. The default is 10 seconds.

Environment Variables

Variable Description Default
CAMOFOX_PORT Server port 9377
PORT Server port (fallback, for platforms like Fly.io, Railway) 9377
CAMOFOX_BIND_HOST Optional server bind host. Set to 127.0.0.1 for loopback-only access or 0.0.0.0 for IPv4 on all interfaces. When unset, Node uses its default all-interface binding. -
CAMOFOX_API_KEY Enable cookie import endpoint (disabled if unset) -
CAMOFOX_ADMIN_KEY Required for POST /stop -
CAMOFOX_ACCESS_KEY If set, all routes (except /health, cookie import, and /stop) require Authorization: Bearer <key>. Lets you safely expose the server beyond loopback. -
CAMOFOX_EVALUATE_MAX_BODY_SIZE Max JSON request body size for POST /tabs/:tabId/evaluate; other JSON routes remain limited to 100kb. 1mb
CAMOFOX_LOCALE Locale for an explicitly configured direct-session identity. Must be set with CAMOFOX_TIMEZONE. -
CAMOFOX_TIMEZONE IANA timezone for an explicitly configured direct-session identity. Must be set with CAMOFOX_LOCALE. -
CAMOUFOX_EXECUTABLE External Camoufox executable to use instead of downloading/launching the bundled cache. Must point to a Camoufox bundle with sibling resources. -
CAMOUFOX_EXECUTABLE_PATH Compatibility alias for CAMOUFOX_EXECUTABLE -
CAMOFOX_EXECUTABLE_PATH Compatibility alias for CAMOUFOX_EXECUTABLE -
CAMOFOX_DISABLE_DEFAULT_ADDONS Set to 1/true to skip downloading and launching the default uBlock Origin (UBO) addon. Useful for deployments where the addons.mozilla.org download is unreliable or unwanted (a failed download otherwise leaves a broken addon cache that blocks startup). 0
CAMOFOX_COOKIES_DIR Directory for cookie files ~/.camofox/cookies
CAMOFOX_UPLOADS_DIR Directory allowed for POST /tabs/:tabId/upload file attachments. Paths outside it, including symlink escapes, are rejected. ~/.camofox/uploads
CAMOFOX_PROFILE_DIR Directory for persisted session profiles ~/.camofox/profiles
CAMOFOX_TRACES_DIR Directory for session trace zips ~/.camofox/traces
CAMOFOX_TRACES_MAX_BYTES Max size per trace, removed on next startup if exceeded 52428800 (50MB)
CAMOFOX_TRACES_TTL_HOURS Traces older than this are swept on startup 24
MAX_SESSIONS Max concurrent browser sessions 50
MAX_TABS_PER_SESSION Max tabs per session 10
SESSION_TIMEOUT_MS Session inactivity timeout (0 = never) 600000 (10min)
BROWSER_IDLE_TIMEOUT_MS Kill browser when idle (0 = never) 300000 (5min)
CAMOFOX_INTERACTIVE Interactive browser mode: desktop opens a real local Camoufox window; off keeps normal headless behavior off
HANDLER_TIMEOUT_MS Max time for any handler 30000 (30s)
MAX_CONCURRENT_PER_USER Concurrent request cap per user 3
MAX_OLD_SPACE_SIZE Node.js V8 heap limit (MB) 128
PROXY_STRATEGY Proxy mode: backconnect (rotating sticky sessions) or blank (single endpoint) -
PROXY_PROTOCOL Proxy protocol: http, https, socks4, or socks5. http
PROXY_PROVIDER Provider name for session format (e.g. decodo) decodo
PROXY_HOST Proxy hostname or IP (simple mode) -
PROXY_PORT Proxy port (simple mode) -
PROXY_USERNAME Proxy auth username -
PROXY_PASSWORD Proxy auth password -
PROXY_BACKCONNECT_HOST Backconnect gateway hostname -
PROXY_BACKCONNECT_PORT Backconnect gateway port 7000
PROXY_COUNTRY Target country for proxy geo-targeting -
PROXY_STATE Target state/region for proxy geo-targeting -
TAB_INACTIVITY_MS Close tabs idle longer than this 300000 (5min)
CAMOFOX_CRASH_REPORT_ENABLED Enable anonymized crash/hang telemetry (false to disable) true
CAMOFOX_CRASH_REPORT_URL Telemetry endpoint (self-hosted endpoint) https://camofox-telemetry.askjo.workers.dev/report
CAMOFOX_CRASH_REPORT_REPO GitHub repo for telemetry issues jo-inc/camofox-browser
CAMOFOX_CRASH_REPORT_RATE_LIMIT Max telemetry reports per hour 10
ENABLE_VNC Enable VNC plugin for interactive browser access (1) -
VNC_PASSWORD Password for VNC access (recommended in production) -
VNC_BIND noVNC listen address; set 0.0.0.0 to reach noVNC through a published container port 127.0.0.1
VNC_RFB_BIND Native VNC listen address; non-loopback requires VNC_PASSWORD and does not change with VNC_BIND 127.0.0.1
NOVNC_PORT noVNC web UI port 6080

Interactive desktop browser

Camofox is headless by default. On a machine with a local graphical desktop, opt in to a visible Camoufox window:

CAMOFOX_INTERACTIVE=desktop npm start

Or set the same preference in camofox.config.json and restart the server:

{
  "interactive": { "mode": "desktop" }
}

The environment variable overrides the file setting. Set CAMOFOX_INTERACTIVE=off to return to normal headless behavior. This mode is intended for a person using the same machine; it does not expose a remote browser-control service.

Architecture

Browser Instance (Camoufox)
\-- User Session (BrowserContext) - isolated cookies/storage
    |-- Tab Group (sessionKey: "conv1")
    |   |-- Tab (google.com)
    |   \-- Tab (github.com)
    \-- Tab Group (sessionKey: "conv2")
        \-- Tab (amazon.com)

Sessions auto-expire after 30 minutes of inactivity. The browser itself shuts down after 5 minutes with no active sessions, and relaunches on the next request.

When a session's tab limit is reached, the oldest/least-used tab is automatically recycled instead of returning an error -- so long-running agent sessions don't hit dead ends.

Security Model

This section documents the security boundaries of camofox-browser for users and automated security reviewers.

Code isolation

All process.env reads are centralized in lib/config.js. All child_process usage is in lib/launcher.js (server subprocess) and plugins/youtube/youtube.js (yt-dlp). The main server.js has route handlers but zero process.env reads and zero child_process imports. No single file combines environment/credential access with network sends.

No embedded secrets

Zero credentials, private keys, API tokens, or signing keys ship in this package. All secrets are provided at runtime via environment variables (CAMOFOX_API_KEY, CAMOFOX_ACCESS_KEY) or are Cloudflare Worker environment secrets (telemetry endpoint GitHub App key).

Cookie import is disabled by default

The cookie import endpoint (POST /sessions/:userId/cookies) is gated behind CAMOFOX_API_KEY. If this env var is not set, the server rejects all cookie import requests with HTTP 403. Cookie files are read from a sandboxed directory (~/.camofox/cookies/) with path traversal protection -- attempts to escape the directory are blocked. Max 500 cookies per request, 5MB file size limit.

Access control

CAMOFOX_ACCESS_KEY provides global bearer token authentication for all routes (except /health). When set, every request must include Authorization: Bearer <key>. Recommended for any deployment beyond localhost.

Binary download

The Camoufox browser engine (~300MB) is downloaded at npm install time by camoufox-js, an npm package maintained by the Camoufox project. It downloads from official GitHub releases with integrity verification handled by camoufox-js. No custom download URLs, no URL shorteners, no raw IP addresses.

Telemetry

Anonymized crash/hang telemetry is sent to a Cloudflare Worker endpoint. The endpoint source is in this repo and auditable. Verification: GET /source on the endpoint returns the deployed commit hash and sha256 so you can compare against the repo. The reporter (lib/reporter.js L28-290) applies paranoid anonymization: private domains are HMAC-hashed (not reversible), paths are stripped, tokens/IPs/emails are redacted. No page content, cookies, or user data is ever sent. Disable with CAMOFOX_CRASH_REPORT_ENABLED=false or point to your own endpoint with CAMOFOX_CRASH_REPORT_URL.

Session persistence

The persistence plugin saves cookies and localStorage to ~/.camofox/profiles/<hashed-userId>/ so authenticated sessions survive browser restarts. UserIds are hashed for directory names. Disable via camofox.config.json by removing persistence from the plugins array.

Network access

Outbound connections are made to: (1) URLs the agent navigates to (core functionality), (2) the telemetry endpoint (anonymized, opt-out available). Inbound: the REST API on port 9377, bound to all interfaces by default or to CAMOFOX_BIND_HOST when configured, optionally protected by CAMOFOX_ACCESS_KEY.

Subprocess usage

Two subprocesses may be spawned: (1) the Camoufox browser engine (core functionality, lib/launcher.js), (2) yt-dlp for YouTube transcript extraction (optional, plugins/youtube/youtube.js). Both are isolated in dedicated files separate from route handlers.

Testing

npm test              # all tests
npm run test:e2e      # e2e tests only
npm run test:live     # live site tests (Google, macros)
npm run test:debug    # with server output

npm

npm install @askjo/camofox-browser

Credits

Crypto Scam Warning

Sketchy people are doing sketchy things with crypto tokens named "Camofox" now that this project is getting attention. Camofox is not a crypto project and will never be one. Any token, coin, or NFT using the Camofox name has nothing to do with us.

License

MIT

View on GitHub

Recent activity

commits and pull requests

Releases and announcements

26 total
  1. ## v1.18.0 ### More reliable browser interactions Tab creation cleans up after failures and timeouts ([#11613](https://github.com/jo-inc/camofox-browser/pull/11613)). Proxy-rotation retries return the replacement page ([#11625](https://github.com/jo-inc/camofox-browser/pull/11625)), and concurrent cleanup preserves newly created tab groups ([#11624](https://github.com/jo-inc/camofox-browser/pull/11624)). Click recovery and named navigation-key handling are also improved. ### Better platform support Docker installs include the files needed by postinstall ([#11062](https://github.com/jo-inc/camofox-browser/pull/11062)), and builds select the Camoufox binary for the target architecture, including ARM64. External Camoufox app bundles work more reliably on macOS. ### Diagnostics and dependencies Native-memory reporting ignores invalid samples ([#11612](https://github.com/jo-inc/camofox-browser/pull/11612)). This release includes Dependabot’s `adm-zip` security update ([#10916](https://github.com/jo-inc/camofox-browser/pull/10916)) and targeted transitive-dependency updates. Thanks to [Ryan Duguid](https://github.com/ryanduguid) for the tab-lifecycle and memory-reporting fixes, [D

  2. ## v1.17.0 A browser tab can clear a site’s challenge, follow its redirect, and render a PDF. Then a second request for that same PDF can still get blocked. That was the remaining hole in inline-PDF retrieval. Thanks to [Anand Patel](https://github.com/acpatelmd) for reporting [#9334](https://github.com/jo-inc/camofox-browser/issues/9334), reproducing this with PubMed Central’s challenge flow, and contributing [#10784](https://github.com/jo-inc/camofox-browser/pull/10784). ### Inline PDFs now use what the browser actually received `POST /tabs/:tabId/fetch-current-resource` now first uses the matching main-frame PDF response already received by the managed browser tab. This matters for sites where the tab can render the PDF after a JavaScript or bot-check redirect, but a separate Node-side request gets HTML or a 403. The route still stays scoped to the current managed tab and its existing download-artifact flow. It does not add arbitrary authenticated fetches. Known oversized PDFs are rejected from their `Content-Length` before their body is read. The existing 50 MiB limit remains in place. ### More reliable recovery - A create-tab request now has enough time to rebuild on

  3. A direct browser connection should not pretend it is somewhere else. A Windows shutdown should not leave a browser tree behind. And if an agent is already looking at an authenticated PDF, it should be able to save that document without starting an unrelated fetch flow. `v1.16.0` makes those paths more honest and dependable. It also updates the bundled OpenClaw plugin for the current OpenClaw plugin API. ## Direct connections use honest identity defaults Direct sessions no longer default to a San Francisco location, `en-US` locale, or `America/Los_Angeles` timezone. Proxy-backed sessions keep Camoufox GeoIP identity behavior. If you need a specific direct-session identity, set `CAMOFOX_LOCALE` and `CAMOFOX_TIMEZONE` together. Camofox validates that pair rather than applying a partial identity. ## Windows cleanup reaches the browser tree On Windows, server-owned Camoufox descendants are now cleaned up when the server shuts down. Cleanup checks the captured process tree and process start time before using a scoped `taskkill /T /F`, so it does not target unrelated browser processes. The release is validated both with a focused Windows cleanup test and the complete browser E2E su

  4. A browser task can fail in annoying ways: a dropdown that is not a normal HTML control, a search box hidden behind a mobile layout, a page that takes too long, or one stuck tab taking down other work with it. `v1.15.0` makes those cases less disruptive. Camofox is better at finding the control a person can actually see, handling native selects and keyboard shortcuts, and keeping a bad tab from unnecessarily interrupting a whole active session. `v1.15.0` brings a batch of hardening work extracted from [Jo](https://askjo.ai?ref=camofox), our personal AI agent. Jo runs these browser paths against real sites every day, so the fixes here come from the awkward cases that show up after the happy path: duplicate controls, native dropdowns, stuck pages, search flows that change underneath you, and a single failed tab disrupting unrelated work. ## Better at the awkward parts of browser work - Native dropdowns and comboboxes are available to agents as real controls. - Clicks prefer visible matches when a page has duplicate selectors. - Keyboard chords and Enter are normalized more consistently. - Mouse fallback errors are handled more usefully when an element is technically pre

  5. ## See the browser when you need to Camofox usually works best out of sight. But when a login needs attention, a page looks wrong, or you want to understand what an agent is doing, a hidden browser is hard to help with. `v1.14.0` adds an opt-in local browser window: ```bash CAMOFOX_INTERACTIVE=desktop ``` Start Camofox with that setting and it opens a real local Camoufox window. You can watch the task, inspect the page, and step in when needed. This is for direct local installs, including Hermes running on your own computer. It is off by default. Existing installs remain headless, and desktop mode does not expose VNC, noVNC, or a remote browser-control port. If you need a browser-based remote view, use the existing VNC plugin in a supported Linux or Docker setup; it is a separate interactive path, not a second view of the same browser session. You can also set it in `camofox.config.json`: ```json { "interactive": { "mode": "desktop" } } ``` Set `CAMOFOX_INTERACTIVE=off`, or remove the setting, to return to normal headless behavior. ## Other improvements - Browser recovery stays with the affected user instead of disrupting other active users. - Timed-out actions retire t

Code frequency

additions and deletions
+13.3K-13.3KWeek of 2026-02-08: +13,011 linesWeek of 2026-02-08: -496 linesWeek of 2026-02-15: +1,588 linesWeek of 2026-02-15: -306 linesWeek of 2026-02-22: +1,259 linesWeek of 2026-02-22: -290 linesWeek of 2026-03-01: +113 linesWeek of 2026-03-01: -8 linesWeek of 2026-03-08: +1,081 linesWeek of 2026-03-08: -502 linesWeek of 2026-03-15: +709 linesWeek of 2026-03-15: -31 linesWeek of 2026-03-22: +853 linesWeek of 2026-03-22: -102 linesWeek of 2026-03-29: +1,233 linesWeek of 2026-03-29: -222 linesWeek of 2026-04-05: +1,168 linesWeek of 2026-04-05: -542 linesWeek of 2026-04-12: +7,083 linesWeek of 2026-04-12: -5,472 linesWeek of 2026-04-19: +13,347 linesWeek of 2026-04-19: -448 linesWeek of 2026-04-26: +6,334 linesWeek of 2026-04-26: -2,097 linesWeek of 2026-05-03: +3,334 linesWeek of 2026-05-03: -104 linesWeek of 2026-05-10: +5,209 linesWeek of 2026-05-10: -1,462 linesWeek of 2026-05-17: +1,152 linesWeek of 2026-05-17: -429 linesWeek of 2026-05-24: +442 linesWeek of 2026-05-24: -82 linesWeek of 2026-05-31: +1,524 linesWeek of 2026-05-31: -232 linesWeek of 2026-06-07: +116 linesWeek of 2026-06-07: -1 linesWeek of 2026-06-14: +131 linesWeek of 2026-06-14: -40 linesWeek of 2026-06-21: +0 linesWeek of 2026-06-21: -0 linesWeek of 2026-06-28: +214 linesWeek of 2026-06-28: -12 linesWeek of 2026-07-05: +154 linesWeek of 2026-07-05: -86 linesWeek of 2026-07-12: +692 linesWeek of 2026-07-12: -1,737 linesWeek of 2026-07-19: +5,544 linesWeek of 2026-07-19: -1,151 linesWeek of 2026-07-26: +3,261 linesWeek of 2026-07-26: -2,520 linesWeek of 2026-08-02: +222 linesWeek of 2026-08-02: -10 linesWeek of 2026-08-09: +283 linesWeek of 2026-08-09: -15 linesWeek of 2026-08-16: +996 linesWeek of 2026-08-16: -831 linesWeek of 2026-08-23: +128 linesWeek of 2026-08-23: -23 linesWeek of 2026-08-30: +619 linesWeek of 2026-08-30: -132 linesWeek of 2026-09-06: +800 linesWeek of 2026-09-06: -252 linesWeek of 2026-09-13: +892 linesWeek of 2026-09-13: -381 linesFeb 8, 2026Sep 13, 2026
+73.5K lines added, -20K removed over the last year.

Commits per week

last 52 weeks
630Week of 2025-10-04: 0 commitsWeek of 2025-10-11: 0 commitsWeek of 2025-10-18: 0 commitsWeek of 2025-10-25: 0 commitsWeek of 2025-11-01: 0 commitsWeek of 2025-11-09: 0 commitsWeek of 2025-11-16: 0 commitsWeek of 2025-11-23: 0 commitsWeek of 2025-11-30: 0 commitsWeek of 2025-12-07: 0 commitsWeek of 2025-12-14: 0 commitsWeek of 2025-12-21: 0 commitsWeek of 2025-12-28: 0 commitsWeek of 2026-01-04: 0 commitsWeek of 2026-01-11: 0 commitsWeek of 2026-01-18: 0 commitsWeek of 2026-01-25: 0 commitsWeek of 2026-02-01: 0 commitsWeek of 2026-02-08: 33 commitsWeek of 2026-02-15: 12 commitsWeek of 2026-02-22: 7 commitsWeek of 2026-03-01: 2 commitsWeek of 2026-03-08: 12 commitsWeek of 2026-03-15: 6 commitsWeek of 2026-03-22: 7 commitsWeek of 2026-03-29: 13 commitsWeek of 2026-04-05: 13 commitsWeek of 2026-04-12: 36 commitsWeek of 2026-04-19: 46 commitsWeek of 2026-04-26: 63 commitsWeek of 2026-05-03: 31 commitsWeek of 2026-05-10: 20 commitsWeek of 2026-05-17: 15 commitsWeek of 2026-05-24: 12 commitsWeek of 2026-05-31: 15 commitsWeek of 2026-06-07: 2 commitsWeek of 2026-06-14: 5 commitsWeek of 2026-06-21: 0 commitsWeek of 2026-06-28: 6 commitsWeek of 2026-07-05: 3 commitsWeek of 2026-07-12: 5 commitsWeek of 2026-07-19: 24 commitsWeek of 2026-07-26: 20 commitsWeek of 2026-08-02: 2 commitsWeek of 2026-08-09: 3 commitsWeek of 2026-08-16: 14 commitsWeek of 2026-08-23: 3 commitsWeek of 2026-08-30: 20 commitsWeek of 2026-09-06: 15 commitsWeek of 2026-09-13: 21 commitsWeek of 2026-09-20: 3 commitsWeek of 2026-09-27: 16 commitsOct 4, 2025Sep 27, 2026
505 commits in the last 52 weeks.

When work happens

weekday and hour
SunMonTueWedThuFriSat036912151821Sun 0:00 — 2 commitsSun 1:00 — 5 commitsSun 2:00 — 1 commitsSun 3:00 — 0 commitsSun 4:00 — 0 commitsSun 5:00 — 0 commitsSun 6:00 — 0 commitsSun 7:00 — 0 commitsSun 8:00 — 0 commitsSun 9:00 — 11 commitsSun 10:00 — 14 commitsSun 11:00 — 7 commitsSun 12:00 — 6 commitsSun 13:00 — 1 commitsSun 14:00 — 2 commitsSun 15:00 — 5 commitsSun 16:00 — 7 commitsSun 17:00 — 2 commitsSun 18:00 — 3 commitsSun 19:00 — 0 commitsSun 20:00 — 4 commitsSun 21:00 — 8 commitsSun 22:00 — 1 commitsSun 23:00 — 2 commitsMon 0:00 — 2 commitsMon 1:00 — 1 commitsMon 2:00 — 0 commitsMon 3:00 — 0 commitsMon 4:00 — 1 commitsMon 5:00 — 0 commitsMon 6:00 — 1 commitsMon 7:00 — 1 commitsMon 8:00 — 3 commitsMon 9:00 — 3 commitsMon 10:00 — 2 commitsMon 11:00 — 0 commitsMon 12:00 — 13 commitsMon 13:00 — 9 commitsMon 14:00 — 1 commitsMon 15:00 — 3 commitsMon 16:00 — 4 commitsMon 17:00 — 1 commitsMon 18:00 — 1 commitsMon 19:00 — 1 commitsMon 20:00 — 3 commitsMon 21:00 — 0 commitsMon 22:00 — 5 commitsMon 23:00 — 2 commitsTue 0:00 — 0 commitsTue 1:00 — 0 commitsTue 2:00 — 0 commitsTue 3:00 — 0 commitsTue 4:00 — 0 commitsTue 5:00 — 0 commitsTue 6:00 — 0 commitsTue 7:00 — 0 commitsTue 8:00 — 6 commitsTue 9:00 — 2 commitsTue 10:00 — 9 commitsTue 11:00 — 2 commitsTue 12:00 — 6 commitsTue 13:00 — 3 commitsTue 14:00 — 0 commitsTue 15:00 — 12 commitsTue 16:00 — 11 commitsTue 17:00 — 4 commitsTue 18:00 — 0 commitsTue 19:00 — 0 commitsTue 20:00 — 3 commitsTue 21:00 — 6 commitsTue 22:00 — 10 commitsTue 23:00 — 3 commitsWed 0:00 — 5 commitsWed 1:00 — 1 commitsWed 2:00 — 1 commitsWed 3:00 — 1 commitsWed 4:00 — 1 commitsWed 5:00 — 0 commitsWed 6:00 — 0 commitsWed 7:00 — 1 commitsWed 8:00 — 9 commitsWed 9:00 — 12 commitsWed 10:00 — 2 commitsWed 11:00 — 5 commitsWed 12:00 — 1 commitsWed 13:00 — 3 commitsWed 14:00 — 3 commitsWed 15:00 — 6 commitsWed 16:00 — 13 commitsWed 17:00 — 7 commitsWed 18:00 — 2 commitsWed 19:00 — 2 commitsWed 20:00 — 2 commitsWed 21:00 — 0 commitsWed 22:00 — 0 commitsWed 23:00 — 7 commitsThu 0:00 — 4 commitsThu 1:00 — 2 commitsThu 2:00 — 0 commitsThu 3:00 — 2 commitsThu 4:00 — 1 commitsThu 5:00 — 0 commitsThu 6:00 — 0 commitsThu 7:00 — 1 commitsThu 8:00 — 1 commitsThu 9:00 — 6 commitsThu 10:00 — 4 commitsThu 11:00 — 3 commitsThu 12:00 — 5 commitsThu 13:00 — 2 commitsThu 14:00 — 7 commitsThu 15:00 — 1 commitsThu 16:00 — 2 commitsThu 17:00 — 0 commitsThu 18:00 — 1 commitsThu 19:00 — 0 commitsThu 20:00 — 0 commitsThu 21:00 — 0 commitsThu 22:00 — 0 commitsThu 23:00 — 0 commitsFri 0:00 — 0 commitsFri 1:00 — 0 commitsFri 2:00 — 0 commitsFri 3:00 — 0 commitsFri 4:00 — 0 commitsFri 5:00 — 1 commitsFri 6:00 — 0 commitsFri 7:00 — 0 commitsFri 8:00 — 3 commitsFri 9:00 — 6 commitsFri 10:00 — 1 commitsFri 11:00 — 5 commitsFri 12:00 — 3 commitsFri 13:00 — 0 commitsFri 14:00 — 7 commitsFri 15:00 — 3 commitsFri 16:00 — 0 commitsFri 17:00 — 0 commitsFri 18:00 — 0 commitsFri 19:00 — 0 commitsFri 20:00 — 0 commitsFri 21:00 — 1 commitsFri 22:00 — 0 commitsFri 23:00 — 4 commitsSat 0:00 — 3 commitsSat 1:00 — 0 commitsSat 2:00 — 0 commitsSat 3:00 — 0 commitsSat 4:00 — 0 commitsSat 5:00 — 0 commitsSat 6:00 — 0 commitsSat 7:00 — 1 commitsSat 8:00 — 9 commitsSat 9:00 — 11 commitsSat 10:00 — 4 commitsSat 11:00 — 8 commitsSat 12:00 — 2 commitsSat 13:00 — 3 commitsSat 14:00 — 8 commitsSat 15:00 — 22 commitsSat 16:00 — 5 commitsSat 17:00 — 15 commitsSat 18:00 — 10 commitsSat 19:00 — 9 commitsSat 20:00 — 8 commitsSat 21:00 — 4 commitsSat 22:00 — 5 commitsSat 23:00 — 4 commits
Commit volume by weekday and hour (UTC). Larger dots mean more commits.
DateListRankStars gained
Sep 9, 2026daily#7+135
Sep 8, 2026daily#7+135
Sep 7, 2026daily#3+117
  • freeCodeCamp/freeCodeCamp

    freeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.

    456.7K stars · TypeScript

  • practical-tutorials/project-based-learning

    Curated list of project-based tutorials

    285.8K stars · Python

  • affaan-m/ECC

    The agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.

    272.9K stars · JavaScript

  • NousResearch/hermes-agent

    The agent that grows with you

    251.2K stars · Python

  • react/react

    The library for web and native user interfaces.

    250.9K stars · JavaScript

  • n8n-io/n8n

    Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.

    206.7K stars · TypeScript