Skip to content

Releases: jo-inc/camofox-browser

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.

v1.6.0 — Plugin System, Persistence, VNC

Choose a tag to compare

@skyfallsin skyfallsin released this 19 Apr 03:22
6e5926e

Camofox Browser v1.6.0

What's New

🔌 Plugin System

Camofox is now extensible. Features can be built as self-contained plugins with their own dependencies, config, routes, and tests — loaded automatically from plugins/.

The plugin system includes an event bus with async mutating hooks (emitAsync()) so plugins can intercept and transform 29 browser events across 7 categories (session creation, navigation, downloads, and more), shared auth so plugins inherit the server's API key, and per-plugin configuration in camofox.config.json. A plugin manager handles installation:

node scripts/plugin.js install ./my-plugin
node scripts/plugin.js remove my-plugin
node scripts/plugin.js list

Plugins auto-install their own npm dependencies on startup. Want to add custom auth flows, logging, content filtering, or site-specific logic? Write a plugin — no need to fork the server. Three built-in plugins ship with this release to show the pattern.

💾 Persistence Plugin (enabled by default)

Browser sessions now survive restarts. Cookies and localStorage are saved per-user and restored automatically on the next session. No more re-authenticating after every reboot. Profiles are stored at ~/.camofox/profiles/ by default — configure it in camofox.config.json:

{
  "plugins": {
    "persistence": {
      "profileDir": "/your/custom/path"
    }
  }
}

(Thanks @leoneparise #50 and @eddieoz #55 for the session management groundwork.)

🖥️ VNC Plugin (opt-in) (Thanks @leoneparise! #65)

Some sites just won't let you log in programmatically — CAPTCHAs, MFA flows, or bot detection that blocks headless browsers. The VNC plugin lets you see and control the browser visually in your web browser. Log in by hand like a normal user, and the persistence plugin automatically saves that authenticated session for headless use going forward.

Enable it in camofox.config.json:

{
  "plugins": {
    "vnc": { "enabled": true }
  }
}

Then open http://localhost:6080 to interact with the browser.

🎬 YouTube Plugin (enabled by default)

YouTube transcript extraction moved from the server core into plugins/youtube/. Functionally identical — just better organized, and a good reference for how to build your own plugin.

⌨️ Unified /type Endpoint (Thanks @LolipopJ! #66)

The /type endpoint now supports a keyboard mode for key presses, modifier keys, and shortcuts alongside the existing text input mode.

🐳 Multi-Arch Dockerfile (Thanks @company8! #51)

Docker builds now download Camoufox and yt-dlp at build time with proper architecture detection. ARM and x86 builds just work.

🐛 Fixes

  • Orphaned Camoufox temp files cleaned up on startup
  • CAMOFOX_PORT restored to 9377 (was broken in a previous refactor)
  • Async plugin hooks now properly awaited for mutating events
  • Plugin dependency installer handles both string and object config formats

📦 Dependencies

  • Security bumps via dependabot (#63)

Tests

347 tests passing (+44 new). Auth, plugin loading, persistence, VNC, and keyboard mode all covered.

Upgrading

Drop-in replacement for 1.5.x. No breaking changes, no configuration changes needed.

Thank You

Our biggest community release — 8 PRs from 6 contributors: @leoneparise (#50), @company8 (#51), @eddieoz (#55), @jecruz (#61), @mvanhorn (#62, #65), @LolipopJ (#66).

v1.5.0 — Proxy Providers, Google Bot Detection, Tab Recycling

Choose a tag to compare

@skyfallsin skyfallsin released this 06 Apr 03:41

Camofox Browser v1.5.0

What's New

🔌 Proxy Provider Abstraction

  • Proxy configuration is now provider-agnostic. Supports backconnect (sticky session rotation) and simple (single endpoint) strategies via environment variables — no more hardcoded provider assumptions.
  • Per-context proxy rotation: new browser contexts get fresh proxy sessions without restarting the entire browser.
  • New env vars: PROXY_STRATEGY, PROXY_PROVIDER, PROXY_BACKCONNECT_HOST, PROXY_BACKCONNECT_PORT, PROXY_COUNTRY, PROXY_STATE.

🛡️ Google Bot Detection Fixes

  • WebGL support: Added Mesa OpenGL/EGL libraries + Xvfb virtual display to Docker image. Firefox can now create WebGL contexts — a major bot detection signal that was previously missing.
  • Self-healing Google search: blocked tabs are automatically retried with fresh proxy sessions instead of returning errors.
  • Degraded mode: browser stays available for non-Google tasks even when all proxy sessions are Google-blocked.

♻️ Tab Recycling

  • When the per-session tab limit is reached, the oldest/least-used tab is recycled instead of returning a hard 429 error. Long-running agent sessions no longer hit dead ends.

🚀 Fly.io Horizontal Scaling

  • New lib/fly.js helpers: fly-replay header routing for sticky tab sessions across multiple machines, concurrency-based autoscaling support.

🐳 Makefile for Local Builds (Thanks @mihado! #43)

  • make up auto-detects CPU architecture (x86_64/aarch64), pre-downloads Camoufox + yt-dlp outside the Docker build for faster rebuilds (~30s vs ~3min), and starts the container.
  • make build, make down, make reset, make clean — full lifecycle management.

🐛 Bug Fixes

  • YouTube transcript proxy: yt-dlp routed through residential proxy with lazy retry on startup failure.
  • YouTube transcript session leak: phantom __yt_transcript__ session cleaned up after use. (Thanks @imtylervo! #39)
  • Slow page refs: Element references no longer go stale on pages that take a long time to settle. (#36)
  • DELETE userId: /tabs/:id DELETE endpoints now accept userId from query string — no longer silently fails when HTTP clients strip request bodies. (Thanks @imtylervo! #39)
  • Scroll left/right: Horizontal scroll now works correctly instead of silently scrolling vertically. (Thanks @imtylervo! #39)
  • Plugin orphan process: Server process is now killed on health check failure during startup instead of leaking. (Thanks @imtylervo! #39)
  • Timer leak: buildRefs interval is cleared on tab close. (Thanks @imtylervo! #39)
  • Plugin stringify: Tool parameters are properly JSON-serialized. (#35)

📦 Dependency Bumps

  • brace-expansion 1.1.13, qs 6.14.2, picomatch 2.3.2, path-to-regexp 0.1.13

Upgrading

Update your plugin to v1.5.0. If you use a proxy, the old PROXY_HOST/PROXY_PORT env vars still work for simple single-endpoint proxies. For rotating sticky sessions, set PROXY_STRATEGY=backconnect — see README for details.

v1.4.1 — Metrics, Proxy Fix, Stability

Choose a tag to compare

@skyfallsin skyfallsin released this 22 Mar 19:32
91fc2ec

Camofox Browser v1.4.1

What's New

📊 Prometheus Metrics

  • New /metrics endpoint exposes Prometheus-compatible metrics: active tabs, request latency (by endpoint), browser restarts, tab lifecycle events, and more. Drop it into your existing monitoring stack.

🐛 Bug Fixes

  • Firefox proxy credentials: Fixed base64 auth header decoding for Firefox-style proxy credential format.
  • Macro validation: LLM-generated garbage values (__NO__, none, null) no longer cause 500 errors — they're filtered out and treated as empty. Missing url/macro now returns 400 instead of 500.
  • goBack navigation: Pages that cancel navigation (NS_BINDING_CANCELLED) no longer throw unhandled errors.
  • Idle browser shutdown: The idle timer now starts after browser pre-warm, so unused browsers don't stay alive indefinitely (~735 MB RSS savings). (Thanks @rplakas! #33)
  • Plugin evaluate fix: camofox_evaluate request body is now properly JSON-serialized. (Thanks @kays0x! #35)

Upgrading

Update your plugin to v1.4.1. No configuration changes needed — fully backward compatible.