Releases: jo-inc/camofox-browser
Release list
v1.13.0 — Persistence and browser recovery
Highlights
- Recover zombie browser contexts during tab creation with a configurable 10-second
newPageTimeoutMsdeadline, targeted context replacement, and one retry — thanks @Love-JourneY (#5416). - Persist IndexedDB alongside cookies and local storage when enabled — thanks @ianks (#7225).
- Fully reset persisted session storage through the cookie/session reset flow — thanks @PluginsKers (#6972).
- Support a configurable server bind host — thanks @snapss-dev (#8096).
- Increase Docker shared memory to 2 GB for improved browser stability — thanks @lindenau-cedix (#6017).
Contributors
Thank you to @Love-JourneY, @ianks, @PluginsKers, @snapss-dev, and @lindenau-cedix for the contributions included in this release.
Full diff: v1.12.1...v1.13.0
v1.12.1
VNC reliability
- Restore initial VNC attachment for Xvfb launched with
-displayfd. - Preserve per-server display ownership across multi-instance hosts.
- Reattach x11vnc after browser/Xvfb restarts on the same display.
- Add
GET /vnc/statusfor attachment and port discovery. - Add hermetic watcher coverage and a disposable Linux recovery smoke test.
Includes the VNC work proposed in #4781, #5243, #6549, and #8070. Thanks @Doud-FR, @paranoidi, @modanq, and @luxles.
v1.12.0 — big set of changes from jo and reliability/compat fixes
This release upstreams reliability work from running Camofox in production at Jo, alongside community fixes.
What changed
Browser reliability
Substantial changes as a result of broad production-usage on residential proxies from askjo.ai
- Scope process cleanup to browsers owned by the current server.
- Prevent close/relaunch races and clean up orphaned browser processes.
- Improve idle-shutdown health checks and memory-pressure recovery.
- Harden popup, stale-tab, and concurrent tab lifecycle handling.
- Improve multi-instance tab routing, proxy rotation, and Google prewarming.
- Bound click fallback lookups and return an actionable 422 instead of a 30-second 500.
- Skip unstable captcha and bot-detection iframes during ref construction.
- Normalize browser failures into stable status codes and recovery hints.
Linux and Docker
- Await
VirtualDisplay.get()so launches no longer receiveDISPLAY=[object Promise]. - Remove the fixed context viewport that triggered
viewport.isMobileJuggler errors. - Default Xvfb to 1280×720×24.
- Pin
camoufox-jsto0.10.2for reproducible installs. - Improve Docker build and dependency installation.
Fixes #6807 and related reports #8043, #8042, #5916, #5812, #5643, #5408, and #5166.
Persistence and startup
- Await shutdown listeners before closing contexts so storage checkpoints complete. Fixes #7162.
- Add
CAMOFOX_DISABLE_DEFAULT_ADDONS=1to skip UBO when addon downloads or cache state are unreliable. Fixes #5067. - Restore the disabled reporter contract. Fixes #5434.
OpenClaw and dependencies
- Fix factory tool registration so newer OpenClaw versions expose all
camofox_*tools. Fixes #5437. - Restore the generated
plugin.jsruntime artifact. - Fix the E2E test command and test teardown.
- Update
qsto 6.15.2 andua-parser-jsto 2.0.10, including its Client Hints ReDoS fix.
Community PRs
Thank you!
- #7266 — Linux display and viewport compatibility — @mvanhorn
- #5078 — optional UBO exclusion — @mvanhorn
- #5960 — disabled reporter contract — @mrzlab630
- #7163 — shutdown persistence — @joyjit
- #7879 — process ownership cleanup — @leomercer-dev
- #5663 — bounded click fallback — @leoneparise
- #5591 — unstable iframe handling — @leoneparise
- #7534 — OpenClaw tool registration — @xenek
- #5909 — dependency security updates — @dependabot
v1.11.2 — CLI Binary
CLI Binary
The npm package now installs a camofox-browser executable.
Run standalone from npm:
npx @askjo/camofox-browserOr install globally:
npm install -g @askjo/camofox-browser
camofox-browserDefault port is still 9377; configure via the same environment variables as source installs.
v1.11.1
ClawHub Publish Fix
Restores the ClawHub runtime id to camofox-browser so the package can publish as an update to the existing @askjo/camofox-browser listing.
No runtime behavior changes from v1.11.0.
v1.11.0 — Windows Support, OpenClaw Compatibility, Production Reliability
Windows Support
Windows users can now build and run camofox-browser without WSL or make.
This release adds a PowerShell build.ps1 workflow, enforces LF line endings for shell scripts, and fixes Docker builds from Windows checkouts by invoking shell scripts through sh. Plugin loading also works correctly on Windows now: plugins are imported via file:// URLs instead of raw filesystem paths.
OpenClaw Compatibility
OpenClaw 2026.5.x requires packages to declare tool ownership in contracts.tools. camofox-browser now ships that manifest metadata, fixing tool registration failures in newer OpenClaw versions.
Cross-Origin Iframe Interaction
Accessibility snapshots now traverse supported iframes, and refs can resolve back into their owning frames. This makes embedded checkout/payment fields visible and clickable through the normal ref-based flow.
Tracking and analytics iframes are filtered out so snapshots stay focused on actionable page content.
Security Hardening
Several sensitive routes now require configured auth:
POST /tabs/:tabId/evaluateDELETE /sessions/:userIdPOST /pressure/cleanup
Cookie import now uses the shared auth middleware too. If you expose camofox-browser beyond loopback, verify your CAMOFOX_API_KEY / CAMOFOX_ACCESS_KEY setup before upgrading.
Production Reliability
This release pulls in reliability fixes from jo production usage:
- Idle shutdown no longer leaks orphaned Camoufox processes
- Browser close now force-cleans escaped child processes
- Health checks stay green after idle browser shutdown
- Stale tab errors are clearer and more recoverable
- Active tab operations refresh session access time so live sessions are not expired
- Popups from
target=_blankandwindow.openare tracked as managed tabs - Tab operations are better serialized with per-tab locks
- Navigation timeout cleanup avoids poisoning proxy-backed sessions
- YouTube transcript session cleanup handles races more safely
- Crash/stall reporter no longer treats
429relay responses as successful delivery
Fixes & Improvements
- Prometheus metrics are lazy-loaded and disabled by default unless explicitly enabled
- Snapshot size metrics added
- Navigate requests are aborted when their tab is deleted
- Retry-path
constreassignment crashes fixed - VNC plugin config is explicit and disabled by default
- Optional Sentry integration no longer prevents server startup when Sentry is not installed
Thank You
Thanks to:
- @kgarg2468 — OpenClaw contracts and Windows/Electron feedback
- @SirBrenton — reporter 429 handling
- @sebastiondev — auth hardening
- @Chen-538 — Windows plugin loading fix
- @dawsman — VNC config cleanup
- @pasmud — Windows build support
v1.9.1
Memory Leak Reporter Improvements
Reduced noise from auto-reported memory leak issues by ~90%.
Changes
- Raised reporter threshold from 200MB → 400MB — the self-healing restart mechanism already handles growth at 200MB when idle, so we only report when self-healing failed
- Added session gate — skip reports when sessions=0 and browser is already dead (restart handled it). Extreme growth (>600MB) still reports as a safety net.
- Capture browser RSS during growth —
lastSeenBrowserRssMbis now captured while the browser is alive (previously always null by report time). Enables distinguishing Node/Playwright leaks from Firefox shared-memory bleed. - Auto-close bot now handles
memory-leakissues with 0 contexts + 0 tabs (same pattern as stuck-issue handling) - Fixed misleading code comment — native memory metric measures Node/Playwright state (CDP buffers, glibc arenas), not Firefox's jemalloc
Context
Audit of 78 auto-reported memory-leak issues found all were self-healing conditions generating noise. The memory pressure restart (kills browser at 200MB growth when idle) was already handling these, but the reporter fired at the same threshold, creating duplicate signals for resolved conditions.
v1.9.0 — Viewport Testing, Tab Leak Fix, Session Robustness
Responsive Viewport Testing
POST /tabs/:tabId/viewport — set any viewport size on a live tab for responsive testing. Pass { width, height } and the page re-renders at that size. Useful for testing mobile breakpoints, tablet layouts, or unusual screen dimensions without creating a new session.
Tab Leak Fix
Two fixes for pages that survive normal cleanup:
safePageCloseforce-kill: Ifpage.close()hangs (e.g., stuck unload handler), the page is force-closed after timeout instead of leaked. Previously a single stuck page would starve Firefox of DOM threads.- Orphan page reaper: Background interval detects Playwright pages that escaped
tabGroupstracking and force-closes them.
Session Lifecycle Robustness
- Navigation timeout → session destroy: A poisoned proxy (Cloudflare holding a connection for 30s) used to kill all subsequent navigations in that context. Now click/navigate/open_url timeouts destroy the session so the next request gets a fresh proxy.
- Drain locks before close: Queued tab operations get clean
410 Tab destroyedinstead of cascading500 Target page closederrors. - Dead-context → 503: Cascade errors return
session_expiredwith retry semantics instead of generic 500. - Transparent proxy retry: Navigation failures from proxy blocks or timeouts automatically retry once with a fresh session.
- goBack timeout 10s → 20s: Back navigation uses browser cache; 10s was too short for complex SPA re-renders.
Install Hardening
- Ship compiled
plugin.jsso install works even when TypeScript compilation is skipped — by @Yeraze (#2140) - Cross-platform postinstall script with cache validation — by @gustavosmendes (#2149)
- Graceful handling of
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1(warns + skips warm-up instead of crash loop) — by @gustavosmendes (#2149)
Fixes & Improvements
- Native memory pressure: restart browser when idle if heap exceeds threshold
- Memory leak false-positive reduction (3 safeguards for watchdog stall detection)
- Crash reporter renamed to telemetry, credentials moved to Cloudflare Worker relay
- OpenClaw plugin metadata + ClawHub publish workflow
Thank You
- @cunninghambe — co-authored viewport endpoint and tab leak fix (#2470, #2471)
- @gustavosmendes — install hardening and
PLAYWRIGHT_SKIP_BROWSER_DOWNLOADsupport (#2149) - @Yeraze — compiled plugin.js fix (#2140)
- @Xy2002 — Railway deployment configuration (#651)
v1.8.0 — Global Access Key, Memory Leak Fix
Global Access Key
CAMOFOX_ACCESS_KEY env var — by @trader-payne (#586)
If you expose camofox beyond loopback — on a VPS, in a Docker network, behind a reverse proxy — you've needed to rely on network-level controls or CAMOFOX_API_KEY (which only gates cookie import). There was no single switch to require auth on every route.
Set CAMOFOX_ACCESS_KEY and every request must carry Authorization: Bearer <key>. Three routes are conditionally exempt:
GET /health— always open (Docker/Fly healthchecks need it)POST /sessions/:userId/cookies— exempt only whenCAMOFOX_API_KEYis also set (has its own gate)POST /stop— exempt only whenCAMOFOX_ADMIN_KEYis also set (has its own gate)
If the dedicated key for an exempted route is not configured, the access key still gates it — defense-in-depth, no accidentally unprotected endpoints.
The access key also works as a superkey on requireAuth() routes, so you don't need two tokens in a single request. 401 responses include WWW-Authenticate: Bearer realm="camofox" per RFC 7235. Env var values are whitespace-trimmed to prevent copy-paste mistakes.
Fully opt-in. If you don't set the env var, nothing changes.
Native Memory Leak Fix
browser.close() had a race condition that could lose the browser PID, leaving orphaned Firefox child processes alive after the context was torn down. On long-running servers this compounded — ~930MB leaked per orphaned browser tree.
Three fixes: closeBrowserFully() serializes concurrent close calls with a shared promise so the PID is never lost, _forceKillProcessTree() walks /proc to find and SIGKILL orphaned children that escaped the process group, and cleanupStaleFirefoxProfiles() sweeps leftover temp directories from enable_cache: true sessions on startup.
Fixes & Improvements
- Crash reporter noise reduction: stable dedup signatures, active-tab gate, per-type rate limits, sleep/suspend false-positive suppression
- npm publishing via OIDC trusted publishing — provenance attestations on every release, no
NPM_TOKENsecret - Docker images published to GHCR on release:
docker pull ghcr.io/jo-inc/camofox-browser:1.8.0
Thank You
Thanks to @trader-payne for the access key feature.
v1.7.2
Structured Extract
POST /tabs/:tabId/extract — by @mvanhorn (#70)
Every agent that uses camofox-browser eventually writes the same code: take an accessibility snapshot, find the refs you care about, parse the name field out of each line by hand, coerce strings to numbers or booleans, trim whitespace. The extract endpoint collapses that into one request with a JSON Schema.
Pass a schema with x-ref hints pointing at snapshot refs, get back a typed object with coercion handled. No LLM, no inference, no external calls — just "look up these refs and coerce them to these types." Think the deterministic half of Stagehand's extract().
POST /tabs/:tabId/extract
{
"schema": {
"type": "object",
"properties": {
"title": { "type": "string", "x-ref": "e1" },
"price": { "type": "number", "x-ref": "e3" },
"inStock": { "type": "boolean", "x-ref": "e4" }
}
}
}
→ { "ok": true, "data": { "title": "Widget", "price": 29.99, "inStock": true } }Session Tracing
POST /tabs with trace: true — by @mvanhorn (#68)
Every major competitor ships session capture — Steel Browser rebuilt its replay stack around MP4, Browserbase leads its observability docs with video replay, AWS Bedrock AgentCore stores recordings in S3. Camofox shipped structured logs and single-shot screenshots, which wasn't enough to reconstruct what the agent actually saw during a multi-step run.
Playwright's recordVideo doesn't work on Firefox (it uses a Chrome-only CDP method). So this uses context.tracing instead, which actually gives you more than video: screenshots + DOM snapshots + network + JS stacks + console output, all in one zip you open with npx playwright show-trace.
Pass trace: true when creating a tab. When the session closes, the trace is flushed to disk. List and download traces via /sessions/:userId/traces. TTL + size-cap sweep runs on startup. Trace directories are SHA-256-hashed per user. Default off, opt-in per session.
OpenAPI Docs
GET /docs — (#78, inspired by @mvanhorn's #69)
Auto-generated spec from @openapi JSDoc annotations on all 31 routes via swagger-jsdoc. Interactive docs at /docs using swagger-stripey. Drift-proof: tests fail if you add a route without @openapi or remove a route but leave the annotation. Legacy endpoints (/act, /navigate, /snapshot, /start, /stop) marked deprecated.
Crash & Hang Telemetry (on by default)
Browser automation fails in ways that are hard to predict — Cloudflare challenges, site redesigns breaking selectors, redirect loops, renderer crashes. The scope is wide and the failure modes are diverse. Without telemetry, the only signal is "it didn't work."
This is on by default, and that's deliberate. The failure surface of headless browser automation is enormous — every site is different, anti-bot measures change weekly, and edge cases compound across browsers, OSes, and network conditions. We can't make camofox rock-solid without knowing what actually breaks in the wild. We invested heavily in anonymization (see below) so we could make this the default responsibly. If you disagree with the tradeoff, one env var turns it off.
The crash reporter 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 — on this repo — when:
- Uncaught exceptions crash the process
- Event loop stalls exceed 5 seconds (watchdog detection, with sleep/suspend suppression so laptop lids don't trigger false reports)
- Frustration patterns — 3+ consecutive failures (timeout, dead context, navigation abort) on the same tab
Each report is structured into sections designed for fast triage:
Environment — version, Node version, platform, uptime.
Resources — Node RSS + heap, browser process RSS (the one everyone misses — the browser OOMs, not Node), open file descriptors, active libuv handles, browser context count, active tab count. This is the difference between "it crashed" and "it crashed because 47 tabs consumed 4GB."
Hang Details — operation name, duration, time spent waiting for the tab lock vs. time in the actual operation, document.readyState at timeout (instantly tells you which layer is stuck: loading = network, interactive = heavy JS, complete = Playwright waitUntil condition not met, unresponsive = renderer crashed), in-flight request count.
Anti-Bot Detection — automatically classifies whether a hang was caused by bot detection rather than a camofox bug. Identifies the provider (Cloudflare, DataDome, PerimeterX, Distil, Sucuri, Akamai) from response headers, reports the HTTP status, redirect chain length + status codes, and response body size. Bot-detected hangs get a bot-detection label so they're filtered out of real bug triage.
Proxy — whether a proxy was configured, type (HTTP/SOCKS5), whether auth was configured, and classified error codes (ERR_PROXY_CONNECTION_FAILED, ERR_TUNNEL_CONNECTION_FAILED, ERR_PROXY_AUTH_REQUESTED, ERR_PROXY_TLS) — no IPs, hostnames, ports, or credentials.
Stall Details — event-loop stall duration, last Express route handler, active handles/requests, heap delta during the stall (positive = memory pressure, negative = GC was the cause).
What it never captures:
- Page content, DOM, screenshots, cookies, headers, request/response bodies
- User-entered data, form fields, credentials
- Proxy IPs, hostnames, ports, or credentials
- Environment variables or system configuration
- Session traces — even if tracing is enabled, traces stay on disk and are never sent to the reporter
How URLs are anonymized:
- Public infrastructure (Cloudflare, Google, GitHub, npm) preserved verbatim so we can identify which CDNs cause problems
- All other domains → stable HMAC hash (
site-a1b2c3d4) — same hash across reports for correlation, not reversible to the original - Paths → depth only (
•/•/•), query params → count only (?[3]). No keys, values, or path content ever included - Tokens, secrets, API keys →
<token>. IPs, emails → redacted
How it works:
- Reports filed as GitHub Issues via dedicated GitHub App (issues-only permissions, no PAT needed)
- Rate limited to 10/hour, deduplicated by stack signature (same crash = comment on existing issue, not a new one)
- Fully overridable: Set your own GitHub App credentials in the
crashReportersection ofcamofox.config.jsonto route reports to your org's private repo instead - Disable with
CAMOFOX_CRASH_REPORT_ENABLED=false
Fixes & Improvements
session:destroyingevent: New lifecycle event fires beforecontext.close(), while the Playwright context is still alive — use it for persistence checkpoints. The existingsession:destroyedstill fires after cleanup for backward compatibility. By @nobita2041 (#75)- CI overhaul: GitHub Actions with full browser-backed test suite, Node 24 only, parallel unit + e2e jobs. CI initially set up by @hnshah
Discussions
We've turned on Discussions on the repo.
Thank You
Thanks to @mvanhorn for structured extract and session tracing, @nobita2041 for the session lifecycle fix, and @hnshah for getting CI off the ground.