Skip to content

Releases: jo-inc/camofox-browser

v1.13.0 — Persistence and browser recovery

Choose a tag to compare

@skyfallsin skyfallsin released this 20 Jul 16:46

Highlights

  • Recover zombie browser contexts during tab creation with a configurable 10-second newPageTimeoutMs deadline, 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

Choose a tag to compare

@skyfallsin skyfallsin released this 19 Jul 23:29

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/status for 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

Choose a tag to compare

@skyfallsin skyfallsin released this 19 Jul 02:38

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 receive DISPLAY=[object Promise].
  • Remove the fixed context viewport that triggered viewport.isMobile Juggler errors.
  • Default Xvfb to 1280×720×24.
  • Pin camoufox-js to 0.10.2 for 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=1 to 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.js runtime artifact.
  • Fix the E2E test command and test teardown.
  • Update qs to 6.15.2 and ua-parser-js to 2.0.10, including its Client Hints ReDoS fix.

Community PRs

Thank you!

v1.11.2 — CLI Binary

Choose a tag to compare

@skyfallsin skyfallsin released this 24 May 07:03

CLI Binary

The npm package now installs a camofox-browser executable.

Run standalone from npm:

npx @askjo/camofox-browser

Or install globally:

npm install -g @askjo/camofox-browser
camofox-browser

Default port is still 9377; configure via the same environment variables as source installs.

v1.11.1

Choose a tag to compare

@skyfallsin skyfallsin released this 24 May 06:07

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

Choose a tag to compare

@skyfallsin skyfallsin released this 24 May 06:00

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.

Fixes #3141 and #3392.

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/evaluate
  • DELETE /sessions/:userId
  • POST /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=_blank and window.open are 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 429 relay 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 const reassignment 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:

v1.9.1

Choose a tag to compare

@skyfallsin skyfallsin released this 07 May 01:46

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 growthlastSeenBrowserRssMb is 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-leak issues 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

Choose a tag to compare

@skyfallsin skyfallsin released this 07 May 01:56

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:

  • safePageClose force-kill: If page.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 tabGroups tracking 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 destroyed instead of cascading 500 Target page closed errors.
  • Dead-context → 503: Cascade errors return session_expired with 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.js so 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

v1.8.0 — Global Access Key, Memory Leak Fix

Choose a tag to compare

@skyfallsin skyfallsin released this 27 Apr 20:33

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 when CAMOFOX_API_KEY is also set (has its own gate)
  • POST /stop — exempt only when CAMOFOX_ADMIN_KEY is 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_TOKEN secret
  • 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

Choose a tag to compare

@skyfallsin skyfallsin released this 26 Apr 04:15

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 crashReporter section of camofox.config.json to route reports to your org's private repo instead
  • Disable with CAMOFOX_CRASH_REPORT_ENABLED=false

Fixes & Improvements

  • session:destroying event: New lifecycle event fires before context.close(), while the Playwright context is still alive — use it for persistence checkpoints. The existing session:destroyed still 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.