<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Camofox-Browser on Producthunt daily</title>
        <link>https://producthunt.programnotes.cn/en/tags/camofox-browser/</link>
        <description>Recent content in Camofox-Browser on Producthunt daily</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Mon, 07 Sep 2026 21:37:54 +0800</lastBuildDate><atom:link href="https://producthunt.programnotes.cn/en/tags/camofox-browser/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>camofox-browser</title>
        <link>https://producthunt.programnotes.cn/en/p/camofox-browser/</link>
        <pubDate>Mon, 07 Sep 2026 21:37:54 +0800</pubDate>
        
        <guid>https://producthunt.programnotes.cn/en/p/camofox-browser/</guid>
        <description>&lt;img src="https://images.unsplash.com/photo-1761998066466-86bd7a1c00ba?ixid=M3w0NjAwMjJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3ODg3ODgwOTl8&amp;ixlib=rb-4.1.0" alt="Featured image of post camofox-browser" /&gt;&lt;h1 id=&#34;jo-inccamofox-browser&#34;&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/jo-inc/camofox-browser&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;jo-inc/camofox-browser&lt;/a&gt;
&lt;/h1&gt;&lt;div align=&#34;center&#34;&gt;
  &lt;img src=&#34;fox.png&#34; alt=&#34;camofox-browser&#34; width=&#34;200&#34; /&gt;
  &lt;h1&gt;camofox-browser&lt;/h1&gt;
  &lt;p&gt;&lt;strong&gt;Anti-detection browser server for AI agents, powered by Camoufox&lt;/strong&gt;&lt;/p&gt;
  &lt;p&gt;
    &lt;a href=&#34;LICENSE&#34;&gt;&lt;img src=&#34;https://img.shields.io/badge/License-MIT-yellow.svg&#34; alt=&#34;License: MIT&#34; /&gt;&lt;/a&gt;
    &lt;a href=&#34;https://github.com/jo-inc/camofox-browser/stargazers&#34;&gt;&lt;img src=&#34;https://img.shields.io/github/stars/jo-inc/camofox-browser&#34; alt=&#34;GitHub stars&#34; /&gt;&lt;/a&gt;
    &lt;a href=&#34;https://www.npmjs.com/package/camofox-browser&#34;&gt;&lt;img src=&#34;https://img.shields.io/npm/v/camofox-browser&#34; alt=&#34;npm version&#34; /&gt;&lt;/a&gt;
    &lt;a href=&#34;https://github.com/jo-inc/camofox-browser/commits&#34;&gt;&lt;img src=&#34;https://img.shields.io/github/last-commit/jo-inc/camofox-browser&#34; alt=&#34;GitHub last commit&#34; /&gt;&lt;/a&gt;
  &lt;/p&gt;
  &lt;p&gt;
    Standing on the mighty shoulders of &lt;a href=&#34;https://camoufox.com&#34;&gt;Camoufox&lt;/a&gt; - a Firefox fork with fingerprint spoofing at the C++ level.
  &lt;/p&gt;
&lt;/div&gt;
&lt;br/&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://askjo.ai?ref=camofox&#34;&gt;&lt;img src=&#34;jo-logo.png&#34; alt=&#34;Jo&#34; width=&#34;80&#34; height=&#34;80&#34; align=&#34;left&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Built by the team behind &lt;a href=&#34;https://askjo.ai?ref=camofox&#34;&gt;&lt;strong&gt;jo, a personal AI agent&lt;/strong&gt;&lt;/a&gt; that runs half on your Mac, half on a dedicated cloud machine just for you &amp;ndash; with zero maintenance needed. Available on macOS, Telegram, WhatsApp, and email. &lt;a href=&#34;https://askjo.ai?ref=camofox&#34;&gt;Try the beta free -&amp;gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;br/&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/jo-inc/camofox-browser &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; camofox-browser
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm start
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# -&amp;gt; http://localhost:9377&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;why&#34;&gt;Why
&lt;/h2&gt;&lt;p&gt;AI agents need to browse the real web. Playwright gets blocked. Headless Chrome gets fingerprinted. Stealth plugins become the fingerprint.&lt;/p&gt;
&lt;p&gt;Camoufox patches Firefox at the &lt;strong&gt;C++ implementation level&lt;/strong&gt; - &lt;code&gt;navigator.hardwareConcurrency&lt;/code&gt;, WebGL renderers, AudioContext, screen geometry, WebRTC - all spoofed before JavaScript ever sees them. No shims, no wrappers, no tells.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;h2 id=&#34;features&#34;&gt;Features
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;C++ Anti-Detection&lt;/strong&gt; - bypasses Google, Cloudflare, and most bot detection&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Element Refs&lt;/strong&gt; - stable &lt;code&gt;e1&lt;/code&gt;, &lt;code&gt;e2&lt;/code&gt;, &lt;code&gt;e3&lt;/code&gt; identifiers for reliable interaction&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Token-Efficient&lt;/strong&gt; - accessibility snapshots are ~90% smaller than raw HTML&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runs on Anything&lt;/strong&gt; - lazy browser launch + idle shutdown keeps memory at ~40MB when idle. Designed to share a box with the rest of your stack &amp;ndash; Raspberry Pi, $5 VPS, shared infra.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session Isolation&lt;/strong&gt; - separate cookies/storage per user&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cookie Import&lt;/strong&gt; - inject Netscape-format cookie files for authenticated browsing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File Upload&lt;/strong&gt; - attach files from a configured upload directory without a native OS dialog&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proxy + GeoIP&lt;/strong&gt; - route traffic through residential proxies with automatic locale/timezone&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structured Logging&lt;/strong&gt; - JSON log lines with request IDs for production observability&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;YouTube Transcripts&lt;/strong&gt; - extract captions from any YouTube video via yt-dlp, no API key needed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Search Macros&lt;/strong&gt; - &lt;code&gt;@google_search&lt;/code&gt;, &lt;code&gt;@youtube_search&lt;/code&gt;, &lt;code&gt;@amazon_search&lt;/code&gt;, &lt;code&gt;@reddit_subreddit&lt;/code&gt;, and 10 more&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Snapshot Screenshots&lt;/strong&gt; - include a base64 PNG screenshot alongside the accessibility snapshot&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Large Page Handling&lt;/strong&gt; - automatic snapshot truncation with offset-based pagination&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Download Capture&lt;/strong&gt; - capture browser downloads and fetch them via API (optional inline base64)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DOM Image Extraction&lt;/strong&gt; - list &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; src/alt and optionally return inline data URLs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy Anywhere&lt;/strong&gt; - Docker, Fly.io, Railway&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VNC Interactive Login&lt;/strong&gt; - log into sites visually via noVNC, export storage state for agent reuse&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenAPI Docs&lt;/strong&gt; - auto-generated spec at &lt;a class=&#34;link&#34; href=&#34;http://localhost:9377/openapi.json&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;/openapi.json&lt;/code&gt;&lt;/a&gt; and interactive docs at &lt;a class=&#34;link&#34; href=&#34;http://localhost:9377/docs&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;/docs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structured Extract&lt;/strong&gt; - &lt;code&gt;POST /tabs/:tabId/extract&lt;/code&gt; with a JSON Schema that maps properties to snapshot refs via &lt;code&gt;x-ref&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session Tracing&lt;/strong&gt; - opt-in per-session Playwright trace capture (screenshots + DOM snapshots + network) with API endpoints to list, fetch, and delete trace zips&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Telemetry&lt;/strong&gt; - automatic &lt;a class=&#34;link&#34; href=&#34;lib/reporter.js#L28-L290&#34; &gt;anonymized crash/hang telemetry&lt;/a&gt; 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 &lt;code&gt;CAMOFOX_CRASH_REPORT_ENABLED=false&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;optional-dependencies&#34;&gt;Optional Dependencies
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Dependency&lt;/th&gt;
					&lt;th&gt;Purpose&lt;/th&gt;
					&lt;th&gt;Install&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/yt-dlp/yt-dlp&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;yt-dlp&lt;/a&gt;&lt;/td&gt;
					&lt;td&gt;YouTube transcript extraction (fast path)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;pip install yt-dlp&lt;/code&gt; or &lt;code&gt;brew install yt-dlp&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The Docker image includes yt-dlp. For local dev, install it for the &lt;code&gt;/youtube/transcript&lt;/code&gt; endpoint. Without it, the endpoint falls back to a slower browser-based method.&lt;/p&gt;
&lt;h2 id=&#34;quick-start&#34;&gt;Quick Start
&lt;/h2&gt;&lt;h3 id=&#34;openclaw-plugin&#34;&gt;OpenClaw Plugin
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openclaw plugins install @askjo/camofox-browser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt; &lt;code&gt;camofox_create_tab&lt;/code&gt;  |  &lt;code&gt;camofox_snapshot&lt;/code&gt;  |  &lt;code&gt;camofox_click&lt;/code&gt;  |  &lt;code&gt;camofox_type&lt;/code&gt;  |  &lt;code&gt;camofox_navigate&lt;/code&gt;  |  &lt;code&gt;camofox_scroll&lt;/code&gt;  |  &lt;code&gt;camofox_screenshot&lt;/code&gt;  |  &lt;code&gt;camofox_close_tab&lt;/code&gt;  |  &lt;code&gt;camofox_list_tabs&lt;/code&gt;  |  &lt;code&gt;camofox_import_cookies&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&#34;standalone&#34;&gt;Standalone
&lt;/h3&gt;&lt;p&gt;Run from npm:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npx @askjo/camofox-browser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Or from source:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git clone https://github.com/jo-inc/camofox-browser
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; camofox-browser
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm start  &lt;span class=&#34;c1&#34;&gt;# downloads Camoufox on first run (~300MB)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Default port is &lt;code&gt;9377&lt;/code&gt;. See &lt;a class=&#34;link&#34; href=&#34;#environment-variables&#34; &gt;Environment Variables&lt;/a&gt; for all options.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; the postinstall script unsets &lt;code&gt;PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD&lt;/code&gt; for itself before fetching the Camoufox binary. Without that override, an exported &lt;code&gt;PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1&lt;/code&gt; (common when Playwright is configured to use system Chrome) would silently skip the binary download and crash the server at runtime.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;External Camoufox executable:&lt;/strong&gt; set &lt;code&gt;CAMOUFOX_EXECUTABLE=/path/to/camoufox-bin&lt;/code&gt; before &lt;code&gt;npm install&lt;/code&gt; and when starting the server to skip the bundled download and launch that executable. Compatibility aliases are &lt;code&gt;CAMOUFOX_EXECUTABLE_PATH&lt;/code&gt; and &lt;code&gt;CAMOFOX_EXECUTABLE_PATH&lt;/code&gt;. This is useful for NixOS paths such as &lt;code&gt;/nix/store/.../camoufox-bin&lt;/code&gt;; the executable must come from a Camoufox bundle that includes &lt;code&gt;properties.json&lt;/code&gt;, &lt;code&gt;version.json&lt;/code&gt;, and &lt;code&gt;fontconfig/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Air-gapped or custom binary management:&lt;/strong&gt; prefer &lt;code&gt;CAMOUFOX_EXECUTABLE&lt;/code&gt; when you already have a Camoufox bundle. Otherwise disable the auto-fetch with &lt;code&gt;npm install --ignore-scripts&lt;/code&gt; (skips lifecycle scripts for &lt;em&gt;every&lt;/em&gt; dependency &amp;ndash; bluntest option) or, more surgically, &lt;code&gt;npm install --omit=optional&lt;/code&gt; plus a manual &lt;code&gt;npx camoufox-js fetch&lt;/code&gt; step against your mirror. Note that &lt;code&gt;PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install&lt;/code&gt; no longer skips the Camoufox download (the postinstall sanitizes the env locally); use &lt;code&gt;--ignore-scripts&lt;/code&gt; or &lt;code&gt;CAMOUFOX_EXECUTABLE&lt;/code&gt; for that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;docker&#34;&gt;Docker
&lt;/h3&gt;&lt;p&gt;The included &lt;code&gt;Makefile&lt;/code&gt; auto-detects your CPU architecture and pre-downloads Camoufox + yt-dlp binaries outside the Docker build, so rebuilds are fast (~30s vs ~3min).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Build and start (auto-detects arch: aarch64 on M1/M2, x86_64 on Intel)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make up
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Stop and remove the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make down
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Force a clean rebuild (e.g. after upgrading VERSION/RELEASE)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make reset
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Just download binaries (without building)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make fetch
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Override arch or version explicitly&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make up &lt;span class=&#34;nv&#34;&gt;ARCH&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;x86_64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;make up &lt;span class=&#34;nv&#34;&gt;VERSION&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;135.0.1 &lt;span class=&#34;nv&#34;&gt;RELEASE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;beta.24
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;windows&#34;&gt;Windows
&lt;/h4&gt;&lt;p&gt;On Windows, &lt;code&gt;make&lt;/code&gt; is not available. Use the included &lt;code&gt;build.ps1&lt;/code&gt; PowerShell script instead:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Build and start&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;ps1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;up&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Stop and remove the container&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;ps1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;down&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Build image only&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;ps1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Force a clean rebuild&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;ps1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;reset&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Download binaries only (without building)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;ps1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;fetch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# Override architecture&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;ps1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;up&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;-Arch&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x86_64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.\&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;py&#34;&gt;ps1&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;up&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;-Arch&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;aarch64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; PowerShell 7+ (&lt;code&gt;pwsh&lt;/code&gt;) is recommended but &lt;code&gt;powershell.exe&lt;/code&gt; (Windows PowerShell 5.1) also works. The script requires Docker Desktop for Windows with the WSL2 backend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Line endings:&lt;/strong&gt; This project includes a &lt;code&gt;.gitattributes&lt;/code&gt; file that forces Unix (&lt;code&gt;LF&lt;/code&gt;) line endings for &lt;code&gt;.sh&lt;/code&gt; files. If you&amp;rsquo;ve already cloned the repo and get &lt;code&gt;sh: not found&lt;/code&gt; or &lt;code&gt;set: Illegal option -&lt;/code&gt; errors during &lt;code&gt;docker build&lt;/code&gt;, run:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-powershell&#34; data-lang=&#34;powershell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;Get-ChildItem&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;-Recurse&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;*.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sh&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;ForEach-Object&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Get-Content&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;-join&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;`n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;`n&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Set-Content&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;$_&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;-NoNewline&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This converts shell scripts to LF line endings. Future clones will handle this automatically thanks to &lt;code&gt;.gitattributes&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING: Do not run &lt;code&gt;docker build&lt;/code&gt; directly.&lt;/strong&gt; The Dockerfile uses bind mounts to pull pre-downloaded binaries from &lt;code&gt;dist/&lt;/code&gt;. Always use &lt;code&gt;make up&lt;/code&gt; (or &lt;code&gt;make fetch&lt;/code&gt; then &lt;code&gt;make build&lt;/code&gt;) &amp;ndash; it downloads the binaries first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;flyio&#34;&gt;Fly.io
&lt;/h3&gt;&lt;p&gt;For Fly.io or other remote CI, you&amp;rsquo;ll need a Dockerfile that downloads binaries at build time instead of using bind mounts.&lt;/p&gt;
&lt;h3 id=&#34;railway&#34;&gt;Railway
&lt;/h3&gt;&lt;p&gt;A &lt;code&gt;railway.toml&lt;/code&gt; is included. It uses &lt;code&gt;Dockerfile.ci&lt;/code&gt; (which downloads binaries at build time) and maps Railway&amp;rsquo;s &lt;code&gt;PORT&lt;/code&gt; env var to &lt;code&gt;CAMOFOX_PORT&lt;/code&gt; automatically.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Install Railway CLI, then:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;railway link
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;railway up
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Set secrets via the Railway dashboard or CLI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;railway variables &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAMOFOX_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;your-generated-key&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;usage&#34;&gt;Usage
&lt;/h2&gt;&lt;h3 id=&#34;cookie-import&#34;&gt;Cookie Import
&lt;/h3&gt;&lt;p&gt;Import cookies from your browser into Camoufox to skip interactive login on sites like LinkedIn, Amazon, etc.&lt;/p&gt;
&lt;h4 id=&#34;setup&#34;&gt;Setup
&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;1. Generate a secret key:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# macOS / Linux&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl rand -hex &lt;span class=&#34;m&#34;&gt;32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. Set the environment variable before starting OpenClaw:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAMOFOX_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;your-generated-key&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openclaw start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The same key is used by both the plugin (to authenticate requests) and the server (to verify them). Both run from the same environment &amp;ndash; set it once.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why an env var?&lt;/strong&gt; The key is a secret. Plugin config in &lt;code&gt;openclaw.json&lt;/code&gt; is stored in plaintext, so secrets don&amp;rsquo;t belong there. Set &lt;code&gt;CAMOFOX_API_KEY&lt;/code&gt; in your shell profile, systemd unit, Docker env, or Fly.io secrets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cookie import is disabled by default.&lt;/strong&gt; If &lt;code&gt;CAMOFOX_API_KEY&lt;/code&gt; is not set, the server rejects all cookie requests with 403.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;3. Export cookies from your browser:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Install a browser extension that exports Netscape-format cookie files (e.g., &amp;ldquo;cookies.txt&amp;rdquo; for Chrome/Firefox). Export the cookies for the site you want to authenticate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Place the cookie file:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;mkdir -p ~/.camofox/cookies
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;cp ~/Downloads/linkedin_cookies.txt ~/.camofox/cookies/linkedin.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The default directory is &lt;code&gt;~/.camofox/cookies/&lt;/code&gt;. Override with &lt;code&gt;CAMOFOX_COOKIES_DIR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Ask your agent to import them:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Import my LinkedIn cookies from linkedin.txt&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The agent calls &lt;code&gt;camofox_import_cookies&lt;/code&gt; -&amp;gt; reads the file -&amp;gt; POSTs to the server with the Bearer token -&amp;gt; cookies are injected into the browser session. Subsequent &lt;code&gt;camofox_create_tab&lt;/code&gt; calls to linkedin.com will be authenticated.&lt;/p&gt;
&lt;h4 id=&#34;how-it-works&#34;&gt;How it works
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-gdscript3&#34; data-lang=&#34;gdscript3&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;~/.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;camofox&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cookies&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;linkedin&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;txt&lt;/span&gt;          &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Netscape&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;on&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;disk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;camofox_import_cookies&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;tool&lt;/span&gt;              &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parses&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;filters&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;by&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;domain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;POST&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;sessions&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;userId&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cookies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;Authorization&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;Bearer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;CAMOFOX_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;Body&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cookies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Playwright&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;cookie&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;objects&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;camofox&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;server&lt;/span&gt;                           &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;validates&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;sanitizes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;injects&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;  &lt;span class=&#34;n&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;addCookies&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Camoufox&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;browser&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;session&lt;/span&gt;                 &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;authenticated&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;browsing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cookiesPath&lt;/code&gt; is resolved relative to the cookies directory &amp;ndash; path traversal outside it is blocked&lt;/li&gt;
&lt;li&gt;Max 500 cookies per request, 5MB file size limit&lt;/li&gt;
&lt;li&gt;Cookie objects are sanitized to an allowlist of Playwright fields&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;session-persistence&#34;&gt;Session Persistence
&lt;/h3&gt;&lt;p&gt;By default, camofox persists each user&amp;rsquo;s cookies and localStorage to &lt;code&gt;~/.camofox/profiles/&lt;/code&gt;. Sessions survive browser restarts &amp;ndash; log in once (via cookies or VNC), and subsequent sessions restore the authenticated state automatically.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;~/.camofox/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;|-- cookies/          # Bootstrap cookie files (Netscape format)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;\-- profiles/         # Persisted session state (auto-managed)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    \-- &amp;lt;hashed-userId&amp;gt;/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        \-- storage_state.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Override the directory with &lt;code&gt;CAMOFOX_PROFILE_DIR&lt;/code&gt; or set &lt;code&gt;&amp;quot;profileDir&amp;quot;&lt;/code&gt; in the persistence plugin config. To disable persistence, set &lt;code&gt;&amp;quot;persistence&amp;quot;: { &amp;quot;enabled&amp;quot;: false }&lt;/code&gt; in &lt;code&gt;camofox.config.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;By default, storage state contains cookies and localStorage only. To also persist IndexedDB, set &lt;code&gt;&amp;quot;indexedDB&amp;quot;: true&lt;/code&gt; in the persistence plugin config. This captures all serializable IndexedDB records—not only authentication data—and may make snapshots significantly larger and checkpoints slower.&lt;/p&gt;
&lt;h3 id=&#34;session-tracing&#34;&gt;Session Tracing
&lt;/h3&gt;&lt;p&gt;Capture a Playwright trace of every action in a session: page screenshots, DOM snapshots, network requests, and console output. Output is a single &lt;code&gt;.zip&lt;/code&gt; file you can open in Playwright&amp;rsquo;s built-in Trace Viewer.&lt;/p&gt;
&lt;p&gt;Opt-in per session by passing &lt;code&gt;trace: true&lt;/code&gt; when opening the first tab:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X POST http://localhost:9377/tabs &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -d &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;userId&amp;#34;:&amp;#34;agent1&amp;#34;,&amp;#34;sessionKey&amp;#34;:&amp;#34;task1&amp;#34;,&amp;#34;url&amp;#34;:&amp;#34;https://example.com&amp;#34;,&amp;#34;trace&amp;#34;:true}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The trace is written when the session closes. Close the session to flush it, then list, fetch, and view:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Close the session to flush the trace&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X DELETE http://localhost:9377/sessions/agent1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# List trace files&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl http://localhost:9377/sessions/agent1/traces
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# {&amp;#34;traces&amp;#34;:[{&amp;#34;filename&amp;#34;:&amp;#34;trace-2026-04-18T04-05-00-...zip&amp;#34;,&amp;#34;sizeBytes&amp;#34;:42810,&amp;#34;createdAt&amp;#34;:...}]}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Download (Content-Type: application/zip)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip &amp;gt; session.zip
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# View it in Playwright&amp;#39;s Trace Viewer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npx playwright show-trace session.zip
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Delete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X DELETE http://localhost:9377/sessions/agent1/traces/trace-2026-04-18T04-05-00-abc.zip
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Why traces instead of video: Camoufox is Firefox-based, and Playwright&amp;rsquo;s &lt;code&gt;recordVideo&lt;/code&gt; is Chromium-only. Traces work on Firefox and give you more than video (network + DOM + console + screenshots).&lt;/p&gt;
&lt;p&gt;Tracing cannot be toggled on an existing session. &lt;code&gt;DELETE /sessions/:userId&lt;/code&gt; first if you need to change the flag.&lt;/p&gt;
&lt;p&gt;Storage defaults to &lt;code&gt;~/.camofox/traces/&amp;lt;hashed-userId&amp;gt;/&lt;/code&gt; and is swept on server startup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CAMOFOX_TRACES_DIR&lt;/code&gt; - base directory (default: &lt;code&gt;~/.camofox/traces&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CAMOFOX_TRACES_MAX_BYTES&lt;/code&gt; - max size per trace, removed at next startup if exceeded (default: 50MB)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CAMOFOX_TRACES_TTL_HOURS&lt;/code&gt; - traces older than this are removed at next startup (default: 24)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;standalone-server-usage&#34;&gt;Standalone server usage
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X POST http://localhost:9377/sessions/agent1/cookies &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Authorization: Bearer YOUR_CAMOFOX_API_KEY&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -d &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;cookies&amp;#34;:[{&amp;#34;name&amp;#34;:&amp;#34;foo&amp;#34;,&amp;#34;value&amp;#34;:&amp;#34;bar&amp;#34;,&amp;#34;domain&amp;#34;:&amp;#34;example.com&amp;#34;,&amp;#34;path&amp;#34;:&amp;#34;/&amp;#34;,&amp;#34;expires&amp;#34;:-1,&amp;#34;httpOnly&amp;#34;:false,&amp;#34;secure&amp;#34;:false}]}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;docker--flyio--railway&#34;&gt;Docker / Fly.io / Railway
&lt;/h4&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker run -p 9377:9377 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -e &lt;span class=&#34;nv&#34;&gt;CAMOFOX_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;your-generated-key&amp;#34;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -v ~/.camofox/cookies:/home/node/.camofox/cookies:ro &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  camofox-browser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For Fly.io:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;fly secrets &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAMOFOX_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;your-generated-key&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;For Railway:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;railway variables &lt;span class=&#34;nb&#34;&gt;set&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAMOFOX_API_KEY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;your-generated-key&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;proxy--geoip&#34;&gt;Proxy + GeoIP
&lt;/h3&gt;&lt;p&gt;Route all browser traffic through a proxy with automatic locale, timezone, and geolocation derived from the proxy&amp;rsquo;s IP address via Camoufox&amp;rsquo;s built-in GeoIP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Simple proxy (single endpoint):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_HOST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;166.88.179.132
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_PORT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46040&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_USERNAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;myuser
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;mypass
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Backconnect proxy (rotating sticky sessions):&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For providers like Decodo, Bright Data, or Oxylabs that offer a single gateway endpoint with session-based sticky IPs:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_STRATEGY&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;backconnect
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_BACKCONNECT_HOST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;gate.provider.com
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_BACKCONNECT_PORT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;7000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_USERNAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;myuser
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;PROXY_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;mypass
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Each browser context gets a unique sticky session, so different users get different IP addresses. Sessions rotate automatically on proxy errors or Google blocks.&lt;/p&gt;
&lt;p&gt;Or in Docker:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;docker run -p 9377:9377 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -e &lt;span class=&#34;nv&#34;&gt;PROXY_HOST&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;166.88.179.132 &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -e &lt;span class=&#34;nv&#34;&gt;PROXY_PORT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;46040&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -e &lt;span class=&#34;nv&#34;&gt;PROXY_USERNAME&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;myuser &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -e &lt;span class=&#34;nv&#34;&gt;PROXY_PASSWORD&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;mypass &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  camofox-browser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;When a proxy is configured:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All traffic routes through the proxy&lt;/li&gt;
&lt;li&gt;Camoufox&amp;rsquo;s GeoIP automatically sets &lt;code&gt;locale&lt;/code&gt;, &lt;code&gt;timezone&lt;/code&gt;, and &lt;code&gt;geolocation&lt;/code&gt; to match the proxy&amp;rsquo;s exit IP&lt;/li&gt;
&lt;li&gt;Browser fingerprint (language, timezone, coordinates) is consistent with the proxy location&lt;/li&gt;
&lt;li&gt;Without a proxy, defaults to &lt;code&gt;en-US&lt;/code&gt;, &lt;code&gt;America/Los_Angeles&lt;/code&gt;, San Francisco coordinates&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;telemetry&#34;&gt;Telemetry
&lt;/h3&gt;&lt;p&gt;Browser automation fails in ways that are hard to predict &amp;ndash; 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 &amp;ldquo;it didn&amp;rsquo;t work.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Telemetry gives us structured data on &lt;em&gt;which sites fail&lt;/em&gt;, &lt;em&gt;how they fail&lt;/em&gt;, and &lt;em&gt;how often&lt;/em&gt;, so we can prioritize fixes for the patterns that actually affect users. It files GitHub Issues automatically when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Uncaught exceptions&lt;/strong&gt; crash the process&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event loop stalls&lt;/strong&gt; exceed 5 seconds (watchdog detection)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Frustration patterns&lt;/strong&gt; &amp;ndash; 3+ consecutive failures (timeout, dead context, navigation abort) on the same tab&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each report includes the failure type, stack trace, tab health counters (HTTP status histogram, console errors, request failures, redirect depth), and the target URL &amp;ndash; all anonymized.&lt;/p&gt;
&lt;h4 id=&#34;how-it-works-1&#34;&gt;How it works
&lt;/h4&gt;&lt;p&gt;Telemetry is sent to a lightweight Cloudflare Worker endpoint at &lt;a class=&#34;link&#34; href=&#34;https://camofox-telemetry.askjo.workers.dev/health&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;https://camofox-telemetry.askjo.workers.dev&lt;/code&gt;&lt;/a&gt;. The endpoint holds the GitHub App credentials as environment secrets &amp;ndash; &lt;strong&gt;no secrets are shipped in this package&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;lib/reporter.js (client, no secrets)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    |  anonymize -&amp;gt; POST https://camofox-telemetry.askjo.workers.dev/report
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Cloudflare Worker (holds GitHub App key)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    |  validate -&amp;gt; rate-limit -&amp;gt; dedup -&amp;gt; create GitHub Issue
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    v
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;GitHub Issue created
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The endpoint source code is in this repo at &lt;a class=&#34;link&#34; href=&#34;workers/crash-reporter/index.ts&#34; &gt;&lt;code&gt;workers/crash-reporter/index.ts&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;verification&#34;&gt;Verification
&lt;/h4&gt;&lt;p&gt;You don&amp;rsquo;t have to trust us &amp;ndash; verify what the live endpoint is running:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 1. Ask the endpoint what code it&amp;#39;s running&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl https://camofox-telemetry.askjo.workers.dev/source
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# -&amp;gt; { &amp;#34;commit&amp;#34;: &amp;#34;abc1234&amp;#34;, &amp;#34;sha256&amp;#34;: &amp;#34;e3b0c44...&amp;#34;, &amp;#34;source&amp;#34;: &amp;#34;https://github.com/...&amp;#34; }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 2. Compare the sha256 against the source in this repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sha256sum workers/crash-reporter/index.ts
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# 3. Check the commit matches what CI deployed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;#    https://github.com/jo-inc/camofox-browser/actions/workflows/telemetry-deploy.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;git log --oneline workers/crash-reporter/index.ts &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; head -1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;If the hashes don&amp;rsquo;t match, the endpoint is running different code than what&amp;rsquo;s in the repo. The deploy workflow (&lt;a class=&#34;link&#34; href=&#34;.github/workflows/telemetry-deploy.yml&#34; &gt;&lt;code&gt;.github/workflows/telemetry-deploy.yml&lt;/code&gt;&lt;/a&gt;) injects the commit and source hash at deploy time &amp;ndash; every deploy is auditable in &lt;a class=&#34;link&#34; href=&#34;https://github.com/jo-inc/camofox-browser/actions/workflows/telemetry-deploy.yml&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;GitHub Actions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Or skip verification entirely: &lt;code&gt;CAMOFOX_CRASH_REPORT_ENABLED=false&lt;/code&gt; disables all telemetry, or point to &lt;a class=&#34;link&#34; href=&#34;#self-hosted-telemetry-endpoint&#34; &gt;your own endpoint&lt;/a&gt; with &lt;code&gt;CAMOFOX_CRASH_REPORT_URL&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&#34;privacy&#34;&gt;Privacy
&lt;/h4&gt;&lt;p&gt;All reported data goes through paranoid anonymization (&lt;a class=&#34;link&#34; href=&#34;lib/reporter.js#L28-L290&#34; &gt;&lt;code&gt;lib/reporter.js&lt;/code&gt; L28-290&lt;/a&gt;) before leaving the process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;URLs&lt;/strong&gt; &amp;ndash; 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 (&lt;code&gt;site-a1b2c3d4&lt;/code&gt;) &amp;ndash; same hash across reports for correlation, but not reversible to the original domain. Path segments become &lt;code&gt;*/*/*&lt;/code&gt; (depth only). Query params become &lt;code&gt;?[3]&lt;/code&gt; (count only). No keys, values, or path content is ever included.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File paths&lt;/strong&gt; -&amp;gt; stripped to filename only (&lt;code&gt;&amp;lt;path&amp;gt;/server.js&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tokens, secrets, API keys&lt;/strong&gt; -&amp;gt; &lt;code&gt;&amp;lt;token&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPs, emails, env vars&lt;/strong&gt; -&amp;gt; redacted&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker/Fly machine IDs&lt;/strong&gt; -&amp;gt; &lt;code&gt;&amp;lt;id&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tab health&lt;/strong&gt; &amp;ndash; pure counters (crash count, error count, status code histogram). No page content, no URLs, no user data.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Duplicate issues are detected by stack signature and get a &lt;code&gt;+1&lt;/code&gt; comment instead of a new issue.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Disable telemetry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAMOFOX_CRASH_REPORT_ENABLED&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Point to your own endpoint (see below)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAMOFOX_CRASH_REPORT_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;https://your-endpoint.example.com/report
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Adjust rate limit (default: 10 per hour)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAMOFOX_CRASH_REPORT_RATE_LIMIT&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;self-hosted-telemetry-endpoint&#34;&gt;Self-hosted telemetry endpoint
&lt;/h4&gt;&lt;p&gt;To file telemetry reports in your own GitHub repo instead of &lt;code&gt;jo-inc/camofox-browser&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Create a GitHub App&lt;/strong&gt; &amp;ndash; &lt;a class=&#34;link&#34; href=&#34;https://github.com/settings/apps/new&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Settings -&amp;gt; Developer settings -&amp;gt; GitHub Apps -&amp;gt; New&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Permissions: &lt;strong&gt;Repository -&amp;gt; Issues -&amp;gt; Read &amp;amp; Write&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Uncheck &lt;strong&gt;Webhook -&amp;gt; Active&lt;/strong&gt; (not needed)&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate a key&lt;/strong&gt; &amp;ndash; downloads a &lt;code&gt;.pem&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Install the app on your target repo (Install App -&amp;gt; select repo)&lt;/li&gt;
&lt;li&gt;Note your &lt;strong&gt;App ID&lt;/strong&gt; (number on the app&amp;rsquo;s General page) and &lt;strong&gt;Installation ID&lt;/strong&gt; (from the URL after installing: &lt;code&gt;github.com/settings/installations/{id}&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Deploy the endpoint&lt;/strong&gt; &amp;ndash; clone this repo and deploy the worker:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; workers/crash-reporter
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Edit wrangler.toml: set account_id to your Cloudflare account ID&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npx wrangler deploy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;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.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Set worker secrets:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; workers/crash-reporter
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;YOUR_APP_ID&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; npx wrangler secret put GH_APP_ID
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;YOUR_INSTALL_ID&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; npx wrangler secret put GH_INSTALL_ID
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Key must be PKCS#8 DER base64 (not raw PEM)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;openssl pkcs8 -topk8 -inform PEM -outform DER -nocrypt -in your-app.pem &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  base64 &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; tr -d &lt;span class=&#34;s1&#34;&gt;&amp;#39;\n&amp;#39;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; npx wrangler secret put GH_PRIVATE_KEY
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# File issues in your repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;echo&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;your-org/your-repo&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; npx wrangler secret put GH_REPO
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Point camofox-browser to your endpoint:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nb&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;CAMOFOX_CRASH_REPORT_URL&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;https://your-worker.your-subdomain.workers.dev/report
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Verify:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl https://your-worker.your-subdomain.workers.dev/health
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# -&amp;gt; {&amp;#34;status&amp;#34;:&amp;#34;ok&amp;#34;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;structured-logging&#34;&gt;Structured Logging
&lt;/h3&gt;&lt;p&gt;All log output is JSON (one object per line) for easy parsing by log aggregators:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;ts&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2026-02-11T23:45:01.234Z&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;level&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;req&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;reqId&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;a1b2c3d4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;method&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;/tabs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;userId&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;agent1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;ts&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2026-02-11T23:45:01.567Z&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;level&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;info&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;msg&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;res&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;reqId&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;a1b2c3d4&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;ms&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;333&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Health check requests (&lt;code&gt;/health&lt;/code&gt;) are excluded from request logging to reduce noise.&lt;/p&gt;
&lt;h3 id=&#34;basic-browsing&#34;&gt;Basic Browsing
&lt;/h3&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Create a tab&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X POST http://localhost:9377/tabs &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -d &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;userId&amp;#34;: &amp;#34;agent1&amp;#34;, &amp;#34;sessionKey&amp;#34;: &amp;#34;task1&amp;#34;, &amp;#34;url&amp;#34;: &amp;#34;https://example.com&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Get accessibility snapshot with element refs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl &lt;span class=&#34;s2&#34;&gt;&amp;#34;http://localhost:9377/tabs/TAB_ID/snapshot?userId=agent1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# -&amp;gt; { &amp;#34;snapshot&amp;#34;: &amp;#34;[button e1] Submit  [link e2] Learn more&amp;#34;, ... }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Click by ref&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X POST http://localhost:9377/tabs/TAB_ID/click &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -d &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;userId&amp;#34;: &amp;#34;agent1&amp;#34;, &amp;#34;ref&amp;#34;: &amp;#34;e1&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Type into an element&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X POST http://localhost:9377/tabs/TAB_ID/type &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -d &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;userId&amp;#34;: &amp;#34;agent1&amp;#34;, &amp;#34;ref&amp;#34;: &amp;#34;e2&amp;#34;, &amp;#34;text&amp;#34;: &amp;#34;hello&amp;#34;, &amp;#34;pressEnter&amp;#34;: true}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Navigate with a search macro&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X POST http://localhost:9377/tabs/TAB_ID/navigate &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -d &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;userId&amp;#34;: &amp;#34;agent1&amp;#34;, &amp;#34;macro&amp;#34;: &amp;#34;@google_search&amp;#34;, &amp;#34;query&amp;#34;: &amp;#34;best coffee beans&amp;#34;}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;api&#34;&gt;API
&lt;/h2&gt;&lt;h3 id=&#34;tab-lifecycle&#34;&gt;Tab Lifecycle
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Method&lt;/th&gt;
					&lt;th&gt;Endpoint&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Create tab with initial URL&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs?userId=X&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;List open tabs&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/stats&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Tab stats (tool calls, visited URLs)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Close tab&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/group/:groupId&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Close all tabs in a group&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/sessions/:userId&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Close all tabs for a user&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;page-interaction&#34;&gt;Page Interaction
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Method&lt;/th&gt;
					&lt;th&gt;Endpoint&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/snapshot&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Accessibility snapshot with element refs. Query params: &lt;code&gt;includeScreenshot=true&lt;/code&gt; (add base64 PNG), &lt;code&gt;offset=N&lt;/code&gt; (paginate large snapshots)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/click&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Click element by ref or CSS selector&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/type&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Type text into element&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/press&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Press a keyboard key&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/scroll&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Scroll page (up/down/left/right)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/navigate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Navigate to URL or search macro&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/wait&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Wait for selector or timeout&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/links&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Extract all links on page&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/images&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;List &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements. Query params: &lt;code&gt;includeData=true&lt;/code&gt; (return inline data URLs), &lt;code&gt;maxBytes=N&lt;/code&gt;, &lt;code&gt;limit=N&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/downloads&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;List captured downloads. Query params: &lt;code&gt;includeData=true&lt;/code&gt; (base64 file data), &lt;code&gt;consume=true&lt;/code&gt; (clear after read), &lt;code&gt;maxBytes=N&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/screenshot&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Take screenshot&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/back&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Go back&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/forward&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Go forward&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/tabs/:id/refresh&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Refresh page&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;youtube-transcript&#34;&gt;YouTube Transcript
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Method&lt;/th&gt;
					&lt;th&gt;Endpoint&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/youtube/transcript&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Extract captions from a YouTube video&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;curl -X POST http://localhost:9377/youtube/transcript &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -H &lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span class=&#34;se&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  -d &lt;span class=&#34;s1&#34;&gt;&amp;#39;{&amp;#34;url&amp;#34;: &amp;#34;https://www.youtube.com/watch?v=dQw4w9WgXcQ&amp;#34;, &amp;#34;languages&amp;#34;: [&amp;#34;en&amp;#34;]}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# -&amp;gt; { &amp;#34;status&amp;#34;: &amp;#34;ok&amp;#34;, &amp;#34;transcript&amp;#34;: &amp;#34;[00:18] [music] We&amp;#39;re no strangers to love [music]\n...&amp;#34;, &amp;#34;video_title&amp;#34;: &amp;#34;...&amp;#34;, &amp;#34;total_words&amp;#34;: 548 }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Uses &lt;a class=&#34;link&#34; href=&#34;https://github.com/yt-dlp/yt-dlp&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;yt-dlp&lt;/a&gt; when available (fast, no browser needed). Falls back to a browser-based intercept method if yt-dlp is not installed &amp;ndash; this is slower and less reliable due to YouTube ad pre-rolls.&lt;/p&gt;
&lt;h3 id=&#34;server&#34;&gt;Server
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Method&lt;/th&gt;
					&lt;th&gt;Endpoint&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/health&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Health check&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/start&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Start browser engine&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/stop&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Stop browser engine&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;sessions&#34;&gt;Sessions
&lt;/h3&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Method&lt;/th&gt;
					&lt;th&gt;Endpoint&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/sessions/:userId/cookies&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Add cookies to a user session (Playwright cookie objects)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/sessions/:userId/storage_state&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Export persisted browser storage (&lt;a class=&#34;link&#34; href=&#34;plugins/vnc/&#34; &gt;VNC plugin&lt;/a&gt;)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;/sessions/:userId/storage_state&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Reset the live session and delete its persisted browser storage (&lt;a class=&#34;link&#34; href=&#34;plugins/persistence/&#34; &gt;persistence plugin&lt;/a&gt;)&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;search-macros&#34;&gt;Search Macros
&lt;/h2&gt;&lt;p&gt;&lt;code&gt;@google_search&lt;/code&gt;  |  &lt;code&gt;@youtube_search&lt;/code&gt;  |  &lt;code&gt;@amazon_search&lt;/code&gt;  |  &lt;code&gt;@reddit_search&lt;/code&gt;  |  &lt;code&gt;@reddit_subreddit&lt;/code&gt;  |  &lt;code&gt;@wikipedia_search&lt;/code&gt;  |  &lt;code&gt;@twitter_search&lt;/code&gt;  |  &lt;code&gt;@yelp_search&lt;/code&gt;  |  &lt;code&gt;@spotify_search&lt;/code&gt;  |  &lt;code&gt;@netflix_search&lt;/code&gt;  |  &lt;code&gt;@linkedin_search&lt;/code&gt;  |  &lt;code&gt;@instagram_search&lt;/code&gt;  |  &lt;code&gt;@tiktok_search&lt;/code&gt;  |  &lt;code&gt;@twitch_search&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Reddit macros return JSON directly (no HTML parsing needed):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@reddit_search&lt;/code&gt; - search all of Reddit, returns JSON with 25 results&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@reddit_subreddit&lt;/code&gt; - browse a subreddit (e.g., query &lt;code&gt;&amp;quot;programming&amp;quot;&lt;/code&gt; -&amp;gt; &lt;code&gt;/r/programming.json&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;browser-configuration&#34;&gt;Browser Configuration
&lt;/h2&gt;&lt;p&gt;Browser behavior can be tuned in &lt;code&gt;camofox.config.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;newPageTimeoutMs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;newPageTimeoutMs&lt;/code&gt; controls how long tab creation waits for Firefox to create a page. If the context is unresponsive, Camofox replaces only that user&amp;rsquo;s context and retries once. The default is 10 seconds.&lt;/p&gt;
&lt;h2 id=&#34;environment-variables&#34;&gt;Environment Variables
&lt;/h2&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Variable&lt;/th&gt;
					&lt;th&gt;Description&lt;/th&gt;
					&lt;th&gt;Default&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_PORT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Server port&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;9377&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PORT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Server port (fallback, for platforms like Fly.io, Railway)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;9377&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_BIND_HOST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Optional server bind host. Set to &lt;code&gt;127.0.0.1&lt;/code&gt; for loopback-only access or &lt;code&gt;0.0.0.0&lt;/code&gt; for IPv4 on all interfaces. When unset, Node uses its default all-interface binding.&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_API_KEY&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Enable cookie import endpoint (disabled if unset)&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_ADMIN_KEY&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Required for &lt;code&gt;POST /stop&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_ACCESS_KEY&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;If set, all routes (except &lt;code&gt;/health&lt;/code&gt;, cookie import, and &lt;code&gt;/stop&lt;/code&gt;) require &lt;code&gt;Authorization: Bearer &amp;lt;key&amp;gt;&lt;/code&gt;. Lets you safely expose the server beyond loopback.&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_EVALUATE_MAX_BODY_SIZE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Max JSON request body size for &lt;code&gt;POST /tabs/:tabId/evaluate&lt;/code&gt;; other JSON routes remain limited to &lt;code&gt;100kb&lt;/code&gt;.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;1mb&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOUFOX_EXECUTABLE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;External Camoufox executable to use instead of downloading/launching the bundled cache. Must point to a Camoufox bundle with sibling resources.&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOUFOX_EXECUTABLE_PATH&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Compatibility alias for &lt;code&gt;CAMOUFOX_EXECUTABLE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_EXECUTABLE_PATH&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Compatibility alias for &lt;code&gt;CAMOUFOX_EXECUTABLE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_DISABLE_DEFAULT_ADDONS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Set to &lt;code&gt;1&lt;/code&gt;/&lt;code&gt;true&lt;/code&gt; 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).&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_COOKIES_DIR&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Directory for cookie files&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;~/.camofox/cookies&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_UPLOADS_DIR&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Directory allowed for &lt;code&gt;POST /tabs/:tabId/upload&lt;/code&gt; file attachments. Paths outside it, including symlink escapes, are rejected.&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;~/.camofox/uploads&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_PROFILE_DIR&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Directory for persisted session profiles&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;~/.camofox/profiles&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_TRACES_DIR&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Directory for session trace zips&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;~/.camofox/traces&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_TRACES_MAX_BYTES&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Max size per trace, removed on next startup if exceeded&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;52428800&lt;/code&gt; (50MB)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_TRACES_TTL_HOURS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Traces older than this are swept on startup&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;24&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;MAX_SESSIONS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Max concurrent browser sessions&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;50&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;MAX_TABS_PER_SESSION&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Max tabs per session&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;10&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;SESSION_TIMEOUT_MS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Session inactivity timeout&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;1800000&lt;/code&gt; (30min)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;BROWSER_IDLE_TIMEOUT_MS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Kill browser when idle (0 = never)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;300000&lt;/code&gt; (5min)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_INTERACTIVE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Interactive browser mode: &lt;code&gt;desktop&lt;/code&gt; opens a real local Camoufox window; &lt;code&gt;off&lt;/code&gt; keeps normal headless behavior&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;off&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;HANDLER_TIMEOUT_MS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Max time for any handler&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;30000&lt;/code&gt; (30s)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;MAX_CONCURRENT_PER_USER&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Concurrent request cap per user&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;3&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;MAX_OLD_SPACE_SIZE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Node.js V8 heap limit (MB)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;128&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_STRATEGY&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Proxy mode: &lt;code&gt;backconnect&lt;/code&gt; (rotating sticky sessions) or blank (single endpoint)&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_PROVIDER&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Provider name for session format (e.g. &lt;code&gt;decodo&lt;/code&gt;)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;decodo&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_HOST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Proxy hostname or IP (simple mode)&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_PORT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Proxy port (simple mode)&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_USERNAME&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Proxy auth username&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_PASSWORD&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Proxy auth password&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_BACKCONNECT_HOST&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Backconnect gateway hostname&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_BACKCONNECT_PORT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Backconnect gateway port&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;7000&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_COUNTRY&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Target country for proxy geo-targeting&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PROXY_STATE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Target state/region for proxy geo-targeting&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;TAB_INACTIVITY_MS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Close tabs idle longer than this&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;300000&lt;/code&gt; (5min)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_CRASH_REPORT_ENABLED&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Enable anonymized crash/hang telemetry (&lt;code&gt;false&lt;/code&gt; to disable)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_CRASH_REPORT_URL&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Telemetry endpoint (&lt;a class=&#34;link&#34; href=&#34;#self-hosted-telemetry-endpoint&#34; &gt;self-hosted endpoint&lt;/a&gt;)&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;https://camofox-telemetry.askjo.workers.dev/report&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_CRASH_REPORT_REPO&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;GitHub repo for telemetry issues&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;jo-inc/camofox-browser&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;CAMOFOX_CRASH_REPORT_RATE_LIMIT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Max telemetry reports per hour&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;10&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ENABLE_VNC&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Enable VNC plugin for interactive browser access (&lt;code&gt;1&lt;/code&gt;)&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;VNC_PASSWORD&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Password for VNC access (recommended in production)&lt;/td&gt;
					&lt;td&gt;-&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;NOVNC_PORT&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;noVNC web UI port&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;6080&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&#34;interactive-desktop-browser&#34;&gt;Interactive desktop browser
&lt;/h2&gt;&lt;p&gt;Camofox is headless by default. On a machine with a local graphical desktop, opt in to a visible Camoufox window:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nv&#34;&gt;CAMOFOX_INTERACTIVE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;desktop npm start
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Or set the same preference in &lt;code&gt;camofox.config.json&lt;/code&gt; and restart the server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;nt&#34;&gt;&amp;#34;interactive&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;mode&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;desktop&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;The environment variable overrides the file setting. Set &lt;code&gt;CAMOFOX_INTERACTIVE=off&lt;/code&gt; 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.&lt;/p&gt;
&lt;h2 id=&#34;architecture&#34;&gt;Architecture
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Browser Instance (Camoufox)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;\-- User Session (BrowserContext) - isolated cookies/storage
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    |-- Tab Group (sessionKey: &amp;#34;conv1&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    |   |-- Tab (google.com)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    |   \-- Tab (github.com)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    \-- Tab Group (sessionKey: &amp;#34;conv2&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        \-- Tab (amazon.com)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;When a session&amp;rsquo;s tab limit is reached, the oldest/least-used tab is automatically recycled instead of returning an error &amp;ndash; so long-running agent sessions don&amp;rsquo;t hit dead ends.&lt;/p&gt;
&lt;h2 id=&#34;security-model&#34;&gt;Security Model
&lt;/h2&gt;&lt;p&gt;This section documents the security boundaries of camofox-browser for users and automated security reviewers.&lt;/p&gt;
&lt;h3 id=&#34;code-isolation&#34;&gt;Code isolation
&lt;/h3&gt;&lt;p&gt;All &lt;code&gt;process.env&lt;/code&gt; reads are centralized in &lt;code&gt;lib/config.js&lt;/code&gt;. All &lt;code&gt;child_process&lt;/code&gt; usage is in &lt;code&gt;lib/launcher.js&lt;/code&gt; (server subprocess) and &lt;code&gt;plugins/youtube/youtube.js&lt;/code&gt; (yt-dlp). The main &lt;code&gt;server.js&lt;/code&gt; has route handlers but zero &lt;code&gt;process.env&lt;/code&gt; reads and zero &lt;code&gt;child_process&lt;/code&gt; imports. No single file combines environment/credential access with network sends.&lt;/p&gt;
&lt;h3 id=&#34;no-embedded-secrets&#34;&gt;No embedded secrets
&lt;/h3&gt;&lt;p&gt;Zero credentials, private keys, API tokens, or signing keys ship in this package. All secrets are provided at runtime via environment variables (&lt;code&gt;CAMOFOX_API_KEY&lt;/code&gt;, &lt;code&gt;CAMOFOX_ACCESS_KEY&lt;/code&gt;) or are Cloudflare Worker environment secrets (telemetry endpoint GitHub App key).&lt;/p&gt;
&lt;h3 id=&#34;cookie-import-is-disabled-by-default&#34;&gt;Cookie import is disabled by default
&lt;/h3&gt;&lt;p&gt;The cookie import endpoint (&lt;code&gt;POST /sessions/:userId/cookies&lt;/code&gt;) is gated behind &lt;code&gt;CAMOFOX_API_KEY&lt;/code&gt;. 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 (&lt;code&gt;~/.camofox/cookies/&lt;/code&gt;) with path traversal protection &amp;ndash; attempts to escape the directory are blocked. Max 500 cookies per request, 5MB file size limit.&lt;/p&gt;
&lt;h3 id=&#34;access-control&#34;&gt;Access control
&lt;/h3&gt;&lt;p&gt;&lt;code&gt;CAMOFOX_ACCESS_KEY&lt;/code&gt; provides global bearer token authentication for all routes (except &lt;code&gt;/health&lt;/code&gt;). When set, every request must include &lt;code&gt;Authorization: Bearer &amp;lt;key&amp;gt;&lt;/code&gt;. Recommended for any deployment beyond localhost.&lt;/p&gt;
&lt;h3 id=&#34;binary-download&#34;&gt;Binary download
&lt;/h3&gt;&lt;p&gt;The Camoufox browser engine (~300MB) is downloaded at &lt;code&gt;npm install&lt;/code&gt; time by &lt;a class=&#34;link&#34; href=&#34;https://www.npmjs.com/package/camoufox-js&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&lt;code&gt;camoufox-js&lt;/code&gt;&lt;/a&gt;, an npm package maintained by the &lt;a class=&#34;link&#34; href=&#34;https://camoufox.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Camoufox project&lt;/a&gt;. It downloads from &lt;a class=&#34;link&#34; href=&#34;https://github.com/nicedayzhu/camoufox/releases&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;official GitHub releases&lt;/a&gt; with integrity verification handled by &lt;code&gt;camoufox-js&lt;/code&gt;. No custom download URLs, no URL shorteners, no raw IP addresses.&lt;/p&gt;
&lt;h3 id=&#34;telemetry-1&#34;&gt;Telemetry
&lt;/h3&gt;&lt;p&gt;Anonymized crash/hang telemetry is sent to a Cloudflare Worker endpoint. The endpoint source is &lt;a class=&#34;link&#34; href=&#34;workers/crash-reporter/index.ts&#34; &gt;in this repo&lt;/a&gt; and auditable. Verification: &lt;code&gt;GET /source&lt;/code&gt; on the endpoint returns the deployed commit hash and sha256 so you can compare against the repo. The reporter (&lt;a class=&#34;link&#34; href=&#34;lib/reporter.js#L28-L290&#34; &gt;&lt;code&gt;lib/reporter.js&lt;/code&gt; L28-290&lt;/a&gt;) 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 &lt;code&gt;CAMOFOX_CRASH_REPORT_ENABLED=false&lt;/code&gt; or point to your own endpoint with &lt;code&gt;CAMOFOX_CRASH_REPORT_URL&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;session-persistence-1&#34;&gt;Session persistence
&lt;/h3&gt;&lt;p&gt;The persistence plugin saves cookies and localStorage to &lt;code&gt;~/.camofox/profiles/&amp;lt;hashed-userId&amp;gt;/&lt;/code&gt; so authenticated sessions survive browser restarts. UserIds are hashed for directory names. Disable via &lt;code&gt;camofox.config.json&lt;/code&gt; by removing &lt;code&gt;persistence&lt;/code&gt; from the plugins array.&lt;/p&gt;
&lt;h3 id=&#34;network-access&#34;&gt;Network access
&lt;/h3&gt;&lt;p&gt;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 &lt;code&gt;CAMOFOX_BIND_HOST&lt;/code&gt; when configured, optionally protected by &lt;code&gt;CAMOFOX_ACCESS_KEY&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;subprocess-usage&#34;&gt;Subprocess usage
&lt;/h3&gt;&lt;p&gt;Two subprocesses may be spawned: (1) the Camoufox browser engine (core functionality, &lt;code&gt;lib/launcher.js&lt;/code&gt;), (2) yt-dlp for YouTube transcript extraction (optional, &lt;code&gt;plugins/youtube/youtube.js&lt;/code&gt;). Both are isolated in dedicated files separate from route handlers.&lt;/p&gt;
&lt;h2 id=&#34;testing&#34;&gt;Testing
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;              &lt;span class=&#34;c1&#34;&gt;# all tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm run test:e2e      &lt;span class=&#34;c1&#34;&gt;# e2e tests only&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm run test:live     &lt;span class=&#34;c1&#34;&gt;# live site tests (Google, macros)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm run test:debug    &lt;span class=&#34;c1&#34;&gt;# with server output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;npm&#34;&gt;npm
&lt;/h2&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install @askjo/camofox-browser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;credits&#34;&gt;Credits
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://camoufox.com&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Camoufox&lt;/a&gt; - Firefox-based browser with C++ anti-detection&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://camoufox.com/about/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Donate to Camoufox&amp;rsquo;s original creator daijro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://openclaw.ai&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;OpenClaw&lt;/a&gt; - Open-source AI agent framework&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;crypto-scam-warning&#34;&gt;Crypto Scam Warning
&lt;/h2&gt;&lt;p&gt;Sketchy people are doing sketchy things with crypto tokens named &amp;ldquo;Camofox&amp;rdquo; now that this project is getting attention. &lt;strong&gt;Camofox is not a crypto project and will never be one.&lt;/strong&gt; Any token, coin, or NFT using the Camofox name has nothing to do with us.&lt;/p&gt;
&lt;h2 id=&#34;license&#34;&gt;License
&lt;/h2&gt;&lt;p&gt;MIT&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
