{"id":44695725,"url":"https://github.com/redf0x1/camofox-browser","last_synced_at":"2026-05-05T07:03:29.959Z","repository":{"id":338542668,"uuid":"1158263687","full_name":"redf0x1/camofox-browser","owner":"redf0x1","description":"Anti-detection browser server for AI agents — REST API wrapping Camoufox engine with OpenClaw plugin support","archived":false,"fork":false,"pushed_at":"2026-02-27T15:58:35.000Z","size":8679,"stargazers_count":12,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-02-27T16:40:52.622Z","etag":null,"topics":["ai-agent","anti-detection","automation","bot-detection","browser-automation","browser-server","camofox","camoufox","docker","express","fingerprint","firefox","headless-browser","mcp","openclaw","playwright","rest-api","stealth-browser","typescript","web-scraping"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/redf0x1.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-15T03:59:59.000Z","updated_at":"2026-02-27T15:58:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/redf0x1/camofox-browser","commit_stats":null,"previous_names":["redf0x1/camofox-browser"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/redf0x1/camofox-browser","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redf0x1%2Fcamofox-browser","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redf0x1%2Fcamofox-browser/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redf0x1%2Fcamofox-browser/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redf0x1%2Fcamofox-browser/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/redf0x1","download_url":"https://codeload.github.com/redf0x1/camofox-browser/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/redf0x1%2Fcamofox-browser/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29920518,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T19:37:42.220Z","status":"ssl_error","status_checked_at":"2026-02-27T19:37:41.463Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","anti-detection","automation","bot-detection","browser-automation","browser-server","camofox","camoufox","docker","express","fingerprint","firefox","headless-browser","mcp","openclaw","playwright","rest-api","stealth-browser","typescript","web-scraping"],"created_at":"2026-02-15T08:11:54.491Z","updated_at":"2026-05-05T07:03:29.951Z","avatar_url":"https://github.com/redf0x1.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CamoFox Browser Server\n\n\u003e Anti-detection browser server for AI agents — TypeScript REST API wrapping the [Camoufox](https://github.com/daijro/camoufox) stealth browser engine\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](tsconfig.json)\n[![Node](https://img.shields.io/badge/Node-20%2B-green)](package.json)\n[![npm](https://img.shields.io/npm/v/camofox-browser)](https://www.npmjs.com/package/camofox-browser)\n\n## Table of Contents\n\n- [Why CamoFox?](#why-camofox)\n- [Features](#features)\n- [Preview Status](#preview-status)\n- [Quick Start](#quick-start)\n- [CLI](#cli)\n- [Console Capture](#console-capture)\n- [Playwright Tracing](#playwright-tracing)\n- [Security](#security)\n- [Usage with AI Agents](#usage-with-ai-agents)\n- [Architecture](#architecture)\n- [API Reference](#api-reference)\n- [Structured Extract](#structured-extract)\n- [Search Macros](#search-macros)\n- [Geo Presets](#geo-presets)\n- [Environment Variables](#environment-variables)\n- [Deployment](#deployment)\n- [Used With](#used-with)\n- [Project Structure](#project-structure)\n- [Contributing](#contributing)\n- [Credits](#credits)\n- [License](#license)\n\n## Why CamoFox?\n\n**The Problem**: Standard browser automation (Puppeteer, Playwright, Selenium) is easily detected by modern anti-bot systems. JavaScript-level patches are fragile and get bypassed quickly.\n\n**The Solution**: CamoFox Browser Server wraps [Camoufox](https://github.com/daijro/camoufox), a Firefox fork with **C++ engine-level fingerprint spoofing**. No JavaScript injection — anti-detection happens at the browser engine level.\n\n| Feature | Puppeteer/Playwright | CamoFox Browser Server |\n|---------|---------------------|------------------------|\n| Anti-detection | JavaScript patches (fragile) | C++ engine-level (robust) |\n| Fingerprint spoofing | Limited | Full (engine-level) |\n| Token efficiency | Raw HTML / screenshots | Accessibility snapshots (smaller + structured) |\n| Integration | Direct SDK | REST API for any language / AI agent |\n| AI agent support | Varies | MCP + OpenClaw compatible |\n\n## Features\n\n- **C++ Anti-Detection** — fingerprint spoofing at the Camoufox engine level (not JS injection)\n- **REST API** — language-agnostic HTTP endpoints for browser automation and AI agent integration\n- **Multi-Session** — concurrent isolated browser contexts per `userId` (defaults: max 50 sessions, max 10 tabs/session)\n- **Persistent Browser Profiles** — Each user gets a dedicated Firefox profile. Cookies, localStorage, IndexedDB, and all browser storage persist across sessions automatically.\n- **Geo Presets** — 8 built-in region presets (locale/timezone/geolocation) + custom presets file\n- **Session-Level Proxy/Geo Overrides** — per-session proxy configuration via named profiles or raw credentials, with hybrid geo modes (`explicit-wins` or `proxy-locked`)\n- **14 Search Macros** — Google, YouTube, Amazon, Reddit (search + subreddit JSON), Wikipedia, Twitter, Yelp, Spotify, Netflix, LinkedIn, Instagram, TikTok, Twitch\n- **Element Refs** — accessibility snapshots annotated with stable `eN` element references for precise interaction\n- **Cookie Persistence** — import Netscape/Playwright-style cookies into a session (bearer auth required only when `CAMOFOX_API_KEY` is set)\n- **OpenClaw Plugin** — OpenClaw-compatible endpoints (`/start`, `/tabs/open`, `/act`, etc.)\n- **TypeScript** — strict mode, typed request shapes, modular Express routes\n- **YouTube Transcript Extraction** — yt-dlp + browser fallback (service-level; no public API route currently exposed)\n- **Snapshot Pagination** — offset-based windowing for large page snapshots\n- **Image Listing Route** — image-only extraction over the shared resource extractor with selector, extension, lazy-load, and blob-resolution controls\n- **Structured Extract** — deterministic schema-driven JSON extraction across core API, CLI, and OpenClaw without arbitrary JavaScript\n- **Browser Health Monitoring** — health probe with recovery/degraded state tracking\n- 🖥️ **CLI Mode** — 50+ commands for terminal-based browser automation\n- 🔐 **Auth Vault** — AES-256-GCM encrypted credential storage (LLM-safe)\n- 📜 **Pipeline Scripting** — Execute command scripts from files\n- 🔍 **Console Capture** — capture and filter browser console messages and uncaught errors\n- 📼 **Playwright Tracing** — record and export Playwright traces for debugging\n- 🗂️ **Trace Artifact Management** — list, download, and delete managed trace ZIPs per user session\n\n## Preview Status\n\nCamoFox Browser Server is in **Preview** (Phase 1). Preview releases are functional for browser automation and agent integration, but carry specific compatibility commitments and explicit non-goals.\n\n### What Preview Means\n- The REST API and CLI are usable for agent workflows today; [CamoFox MCP](https://github.com/redf0x1/camofox-mcp) is available as an external companion integration\n- New features may be added between minor versions\n- Backward-compatible aliases are maintained for renamed or moved endpoints (see [Compatibility Policy](#compatibility-policy))\n- Local state (profiles, registries, sessions) uses versioned formats with fail-closed integrity checks\n\n### What Preview Does NOT Guarantee\n- **Frozen API surface** — endpoint behavior, request shapes, or response formats may change between minor versions\n- **Automatic local-state migration** — browser profiles, download registries, and session files use versioned sidecar formats; incompatible upgrades require manual reset (see [Local State Recovery](#local-state-recovery))\n- **Downgrade safety** — rolling back to an older version may require clearing local state\n- **Fixed GA timeline** — promotion to GA requires meeting evidence-based exit criteria, not a calendar date\n\n### Compatibility Policy\nDuring Preview, CamoFox follows an **additive-only deprecation model**:\n- **Legacy aliases** (e.g., `listItemId` accepted alongside `sessionKey`, OpenClaw `/act` routing to core endpoints) continue to work alongside their replacements\n- **Deprecated fields** are accepted silently; no removal until GA or a documented migration window with advance notice in CHANGELOG\n- **No existing endpoint** is removed in a minor version — removals happen only in major versions with prior CHANGELOG notice\n\n### Local State Recovery\nBrowser profiles, download registries, and CLI session files use versioned sidecar formats. When upgrading CamoFox:\n- **Compatible versions**: State loads normally\n- **Incompatible or corrupt state**: The server refuses to load incompatible profiles and download registries; the CLI rejects incompatible saved-session files. Both log an actionable error with the specific recovery path.\n- **Recovery**: Delete the affected profile directory, session file, or download registry as indicated in the error message. Clean state is recreated on next use.\n\nSupported sidecars include limited forward-migration paths (e.g., fingerprint v0 → v1); when no migration path exists for a given version, the server refuses to load the file and logs an actionable recovery message. There is no silent repair or downgrade path — this fail-closed default prevents data corruption at the cost of manual intervention on unsupported version jumps.\n\n## Quick Start\n\n### From Source\n\n```bash\ngit clone https://github.com/redf0x1/camofox-browser.git\ncd camofox-browser\nnpm install\nnpm run build\nnpm start\n```\n\n### Using npm (CLI)\n\n```bash\nnpm install -g camofox-browser\n\n# Start the server\ncamofox-browser\n\n# Or use the CLI for browser automation\ncamofox open https://example.com\ncamofox snapshot\ncamofox click e5\n```\n\n\u003e See [CLI](#cli) for the complete command reference.\n\n### Using Docker\n\n\u003e Docker image: `ghcr.io/redf0x1/camofox-browser`\n\n```bash\ndocker build -t camofox-browser .\ndocker run -d \\\n  --name camofox-browser \\\n  -p 9377:9377 \\\n  -p 6080:6080 \\\n  -e CAMOFOX_HOST=0.0.0.0 \\\n  -e CAMOFOX_API_KEY=change-me \\\n  -v ~/.camofox:/home/node/.camofox \\\n  camofox-browser\n```\n\nTo persist browser profiles (cookies, localStorage, IndexedDB, etc.) across container restarts, keep the volume mount shown above.\n\n### Using Docker Compose\n\n```yaml\nservices:\n  camofox-browser:\n    build: .\n    ports:\n      - \"9377:9377\"\n    environment:\n      CAMOFOX_HOST: \"0.0.0.0\"\n      CAMOFOX_PORT: \"9377\"\n      # Required when CAMOFOX_HOST is non-loopback\n      CAMOFOX_API_KEY: \"change-me\"\n      # CAMOFOX_ADMIN_KEY: \"change-me\"\n      # Optional: proxy routing (also enables Camoufox geoip mode)\n      # PROXY_HOST: \"\"\n      # PROXY_PORT: \"\"\n      # PROXY_USERNAME: \"\"\n      # PROXY_PASSWORD: \"\"\n```\n\n### Verify\n\n```bash\ncurl http://localhost:9377/health\n# {\"ok\":true,\"engine\":\"camoufox\",\"browserConnected\":true}\n```\n\n## CLI\n\nCamoFox Browser includes a powerful CLI for browser automation directly from the terminal. The CLI auto-starts the server when needed.\n\n### Installation\n\n```bash\n# Global install (recommended)\nnpm install -g camofox-browser\n\n# Or use npx (no install needed)\nnpx camofox-browser open https://example.com\n```\n\n### Quick Start\n\n```bash\ncamofox open https://example.com       # Open a page in anti-detection browser\ncamofox snapshot                       # Get accessibility tree with element refs\ncamofox click e5                       # Click element [e5]\ncamofox type e3 \"hello world\"         # Type into element [e3]\ncamofox screenshot --output page.png   # Save screenshot\ncamofox close                          # Close the tab\n```\n\n### Core Commands\n\n```bash\n# Browser lifecycle\ncamofox open \u003curl\u003e                     # Open URL in new tab\ncamofox close [tabId]                  # Close tab\ncamofox navigate \u003curl\u003e                 # Navigate current tab to URL\n\n# Inspection\ncamofox snapshot                       # Get accessibility tree with [eN] refs\ncamofox screenshot [--output file]     # Take screenshot (saves to file)\ncamofox annotate                       # Screenshot + element ref overlay\ncamofox get-url                        # Get current page URL\ncamofox get-text                       # Get page text content\ncamofox get-links                      # Get all links on page\ncamofox get-tabs                       # List open tabs\ncamofox extract-structured @schema.json # Extract deterministic JSON from a schema\n\n# Interaction\ncamofox click \u003cref\u003e                    # Click element by ref\ncamofox type \u003cref\u003e \u003ctext\u003e              # Type text into element\ncamofox fill '[e1]=\"user\" [e2]=\"pw\"'  # Fill multiple fields at once\ncamofox scroll \u003cdirection\u003e             # Scroll up/down/left/right\ncamofox select \u003cref\u003e \u003cvalue\u003e           # Select dropdown option\ncamofox hover \u003cref\u003e                    # Hover over element\ncamofox press \u003ckey\u003e                    # Press keyboard key\ncamofox drag \u003cfrom\u003e \u003cto\u003e               # Drag element to target\n\n# Navigation\ncamofox go-back                        # Browser back\ncamofox go-forward                     # Browser forward\ncamofox search \"query\" --engine google # Search (14 engines supported)\ncamofox eval \"document.title\"          # Execute JavaScript\ncamofox wait \u003cselector\u003e [--timeout ms] # Wait for element\n```\n\n\u003e **Text input:** CamoFox has no character limit for typed or filled text. Short text stays humanized for anti-detection, while long text automatically switches to bulk DOM insertion so large inputs do not truncate.\n\n### Session Management\n\n```bash\ncamofox session save \u003cname\u003e            # Save current browser state\ncamofox session load \u003cname\u003e            # Restore browser state\ncamofox session list                   # List saved sessions\ncamofox session delete \u003cname\u003e          # Delete saved session\n```\n\n### Cookie Management\n\n```bash\ncamofox cookie export \u003cfile\u003e           # Export cookies to JSON file\ncamofox cookie import \u003cfile\u003e           # Import cookies from JSON file\n```\n\n### Auth Vault\n\nSecurely store credentials locally with AES-256-GCM encryption. Credentials are **never** output to stdout — safe for LLM agent automation.\n\n```bash\ncamofox auth save \u003cprofile\u003e [--url URL]  # Save credentials (prompts for master password)\ncamofox auth load \u003cprofile\u003e              # Show profile info (username only)\ncamofox auth list                        # List saved profiles (no secrets shown)\ncamofox auth delete \u003cprofile\u003e            # Delete a profile\ncamofox auth change-password \u003cprofile\u003e   # Change master password\n\n# Inject credentials into a browser tab (LLM-safe)\ncamofox snapshot                         # Get element refs first\ncamofox auth load gmail --inject --username-ref e5 --password-ref e12\n```\n\n\u003e **Security:** Master passwords use Argon2id KDF (with PBKDF2 fallback). Vault files are stored with 0600 permissions. The `--inject` flag sends credentials directly to the browser — the LLM agent never sees the password.\n\n### Pipeline Scripting\n\nExecute multiple commands from a file for automation workflows:\n\n```bash\n# Create a script\ncat \u003e login-flow.txt \u003c\u003c 'EOF'\n# Login automation script\nopen https://example.com/login\nsnapshot\ntype e3 \"username\"\ntype e5 \"password\"\nclick e7\nwait .dashboard --timeout 5000\nscreenshot --output result.png\nclose\nEOF\n\n# Run it\ncamofox run login-flow.txt\n\n# Continue on errors\ncamofox run login-flow.txt --continue-on-error\n\n# Read from stdin\necho \"get-url\" | camofox run -\n```\n\n### Server Management\n\n```bash\ncamofox server start                   # Start server daemon\ncamofox server start --background      # Start in background\ncamofox server stop                    # Stop server daemon\ncamofox server status                  # Check server status\n```\n\n### Diagnostics\n\n```bash\ncamofox health                         # System health report\ncamofox version                        # CLI + server version\ncamofox info                           # Configuration info\n```\n\n### Console Capture\n\n```bash\ncamofox console [tabId]                  # View console messages\ncamofox console [tabId] --type error     # Filter by type (log/warning/error/info/debug)\ncamofox console [tabId] --clear          # View then clear messages\ncamofox errors [tabId]                   # View uncaught JavaScript errors\ncamofox errors [tabId] --clear           # View then clear errors\n```\n\n### Playwright Tracing\n\n```bash\ncamofox trace start [tabId]              # Start recording trace\ncamofox trace stop [tabId] [-o file.zip] # Stop and save trace ZIP\ncamofox trace chunk-start [tabId]        # Start new trace chunk\ncamofox trace chunk-stop [tabId] [-o f]  # Stop chunk and save ZIP\ncamofox trace status [tabId]             # Check active trace status\n```\n\nView traces at [trace.playwright.dev](https://trace.playwright.dev)\n\n### Global Options\n\n| Flag | Env Var | Description | Default |\n|------|---------|-------------|---------|\n| `--user \u003cid\u003e` | `CAMOFOX_USER` | User/profile ID | `cli-default` |\n| `--port \u003cport\u003e` | `PORT` | Server port | `9377` |\n| `--format \u003cfmt\u003e` | — | Output: `json`, `text`, `plain` | `text` |\n| `-V, --version` | — | Show version | — |\n| `-h, --help` | — | Show help | — |\n\n### Output Formats\n\n```bash\ncamofox get-url --format json          # {\"url\":\"https://example.com\"}\ncamofox get-url --format text          # URL: https://example.com\ncamofox get-url --format plain         # https://example.com\n```\n\n\u003e **Tip:** Use `--format json` for programmatic parsing and LLM agent integration.\n\n## Security\n\n### Anti-Detection\nCamoFox uses [Camoufox](https://github.com/daijro/camoufox), a Firefox fork with **C++ level fingerprint spoofing**. Unlike Chromium-based tools, CamoFox passes bot detection on Google, Cloudflare, and other anti-bot services.\n\n### Auth Vault\n- **AES-256-GCM** encryption with **Argon2id** key derivation (PBKDF2 fallback)\n- Credentials **never** appear in stdout (safe for LLM agent pipelines)\n- Vault files stored with `0600` permissions\n- Master password required for all vault operations\n\n### LLM Agent Safety\n- The `--inject` flag sends credentials directly to the browser — the LLM agent orchestrating the CLI never sees raw passwords\n- Output formats are designed for safe parsing without credential exposure\n- Pipeline scripts can reference auth profiles without embedding secrets\n\n## Usage with AI Agents\n\nCamoFox works seamlessly with AI coding agents and LLM-powered automation:\n\n### AI Coding Assistants (Recommended)\n\nAdd CamoFox skills to your AI coding assistant for full browser automation context:\n\n```bash\nnpx skills add redf0x1/camofox-browser\n```\n\nThis works with **Claude Code**, **Codex**, **Cursor**, **Gemini CLI**, **GitHub Copilot**, **Goose**, **OpenCode**, **Windsurf**, and [40+ other agents](https://github.com/vercel-labs/skills#supported-agents).\n\n**Available skills:**\n\n| Skill | Focus | Best For |\n|-------|-------|----------|\n| `camofox-browser` | Full coverage (CLI + API + OpenClaw) | Complete reference |\n| `camofox-cli` | CLI-only (50 commands) | Terminal-first workflows |\n| `dogfood` | QA testing workflow | Systematic web app testing |\n| `gemini-image` | Gemini image generation | AI image automation |\n| `reddit` | Reddit automation | Reddit posting/commenting |\n\nThe installer will prompt you to choose which skills and which agents to configure.\n\n#### Claude Code\n\n```bash\nnpx skills add redf0x1/camofox-browser\n# Installs to .claude/skills/camofox-browser/SKILL.md\n```\n\n#### Cursor / GitHub Copilot / Codex\n\n```bash\nnpx skills add redf0x1/camofox-browser\n# Installs to .agents/skills/ directory\n```\n\n\u003e **Tip:** Skills are symlinked from the repo, so they stay up to date. Do not manually copy `SKILL.md` files.\n\n### MCP Integration (Recommended)\nUse [CamoFox MCP](https://github.com/redf0x1/camofox-mcp) for direct integration with Claude, Cursor, Windsurf, and other MCP-compatible agents. See [Used With](#used-with).\n\n### CLI Integration\nAI agents can use the CLI with `--format json` for structured output:\n\n```bash\ncamofox open https://example.com       # Open page\ncamofox snapshot --format json         # Get structured element tree\ncamofox click e5                       # Interact with elements\ncamofox auth load gmail --inject --username-ref e5 --password-ref e12  # Safe credential injection\n```\n\n### Pipeline Automation\nCreate reusable automation scripts that AI agents can execute:\n\n```bash\ncamofox run automation-flow.txt        # Execute multi-step workflow\n```\n\n## Architecture\n\n```text\nAI Agent (MCP / OpenClaw / REST Client)\n    │\n    ▼ HTTP REST API (port 9377)\n┌──────────────────────────────────────────┐\n│          CamoFox Browser Server          │\n│          (Express + TypeScript)          │\n├──────────────────────────────────────────┤\n│ Routes                 Services          │\n│  ├── Core API           ├── Browser      │\n│  └── OpenClaw compat    ├── Session      │\n│                         └── Tab ops      │\n├──────────────────────────────────────────┤\n│        Camoufox Engine (anti-detect)     │\n│   Firefox fork + engine-level spoofing   │\n└──────────────────────────────────────────┘\n```\n\n### Persistent Profiles (v1.3.0)\n\n- Each `userId` runs in its own persistent Firefox process/context (backed by `launchPersistentContext(userDataDir)`)\n- Profile data is stored at `~/.camofox/profiles/{userId}/` (override via `CAMOFOX_PROFILES_DIR`)\n- Idle user contexts are closed via LRU eviction (profile data remains on disk)\n\n## API Reference\n\nBase URL: `http://localhost:9377`\n\n\u003e **Security defaults:** `CAMOFOX_HOST` now defaults to `127.0.0.1`. If you bind beyond loopback (for example `0.0.0.0` in Docker or PaaS), `CAMOFOX_API_KEY` becomes required at startup. On non-loopback binds, navigation targets on loopback/private/link-local/metadata hosts are blocked by default unless you explicitly set `CAMOFOX_ALLOW_PRIVATE_NETWORK=true`. If you also configure `PROXY_HOST`/`PROXY_PORT`, exposed deployments must opt into `CAMOFOX_ALLOW_PRIVATE_NETWORK=true` until proxy-side private-target validation exists.\n\n### API Documentation\n\nThe Camofox Browser API includes OpenAPI 3.1.0 docs for a representative subset of the shipped route surface:\n\n- **Interactive API Explorer**: [http://localhost:9377/api/docs](http://localhost:9377/api/docs) — Swagger UI with live request testing\n- **OpenAPI Specification**: [http://localhost:9377/openapi.json](http://localhost:9377/openapi.json) — Machine-readable OpenAPI 3.1.0 spec\n\nThe OpenAPI spec covers a representative subset of core and OpenClaw endpoints, including request schemas, response shapes, authentication requirements, and validation rules.\n\n### Core Endpoints\n\nNote: For any endpoint that targets an existing tab (`/tabs/:tabId/...`), the server resolves `tabId` **within a `userId` scope**. If you omit `userId`, you will typically get `404 Tab not found`.\n\n| Method | Endpoint | Description | Required | Auth |\n|--------|----------|-------------|----------|------|\n| POST | `/sessions/:userId/cookies` | Import cookies into a user session (Playwright cookie objects) | Path: `userId`; Body: `{ \"cookies\": Cookie[] }` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/health` | Health check (also pre-launches the browser) | None | None |\n| GET | `/presets` | List available geo presets (built-in + custom) | None | None |\n| POST | `/tabs` | Create a new tab (supports `preset` + per-field overrides) | Body: `userId` + (`sessionKey` or `listItemId`) | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs?userId=...` | List all tabs for a user (OpenClaw-compatible response shape) | Query: `userId` | None |\n| POST | `/tabs/:tabId/navigate` | Navigate to a URL, or expand a search `macro` + `query` | Body: `userId` + (`url` or `macro`) | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs/:tabId/snapshot?userId=...` | Accessibility snapshot annotated with `eN` element refs | Query: `userId` | None |\n| POST | `/tabs/:tabId/wait` | Wait for page readiness (DOM + optional network idle) | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/click` | Click by `ref` (e.g. `e12`) or CSS `selector` | Body: `userId` + (`ref` or `selector`) | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/type` | Type into an element by `ref` or CSS `selector` | Body: `userId` + (`ref` or `selector`) + `text` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/press` | Press a key (e.g. `Enter`, `Escape`) | Body: `userId` + `key` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/scroll` | Scroll up/down/left/right by pixels | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/scroll-element` | Scroll specific element into view | Body: userId, ref/selector | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/back` | Go back | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/forward` | Go forward | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/refresh` | Refresh | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs/:tabId/links?userId=...\u0026limit=50\u0026offset=0` | Extract links (paginated) | Query: `userId` | None |\n| GET | `/tabs/:tabId/images?userId=...` | List extracted images | Query: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs/:tabId/screenshot?userId=...\u0026fullPage=true` | Screenshot (PNG bytes) | Query: `userId` | None |\n| GET | `/tabs/:tabId/stats?userId=...` | Tab stats + visited URLs | Query: `userId` | None |\n| DELETE | `/tabs/:tabId` | Close a tab (expects JSON body: `{ \"userId\": \"...\" }`) | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| DELETE | `/tabs/group/:listItemId` | Close a tab group (expects JSON body: `{ \"userId\": \"...\" }`) | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| DELETE | `/sessions/:userId` | Close all sessions for a user | Path: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/sessions/:userId/toggle-display` | Toggle display mode (headless/headed/virtual) | Path: `userId`; Body: `{ \"headless\": true\\|false\\|\"virtual\" }` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs/:tabId/cookies` | Export tab cookies | Query: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs/:tabId/downloads` | List tab downloads | Query: `userId` | None |\n| GET | `/users/:userId/downloads` | List user downloads | Path: `userId` | None |\n| GET | `/downloads/:downloadId` | Download metadata | Query: `userId` | None |\n| GET | `/downloads/:downloadId/content` | Stream download content | Query: `userId` | None |\n| DELETE | `/downloads/:downloadId` | Delete tracked download | Body or Query: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/extract-resources` | Extract downloadable resources | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/batch-download` | Batch download resources | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/resolve-blobs` | Resolve blob URLs to base64 | Body: `userId` + `urls[]` | None |\n| POST | `/tabs/:tabId/trace/start` | Start trace recording | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/trace/stop` | Stop and save trace ZIP | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/trace/chunk/start` | Start trace chunk | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/trace/chunk/stop` | Stop chunk and save ZIP | Body: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs/:tabId/trace/status` | Check trace status | Query: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/sessions/:userId/traces` | List managed trace ZIPs for a user | Path: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/sessions/:userId/traces/:filename` | Download a managed trace ZIP | Path: `userId`, `filename` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| DELETE | `/sessions/:userId/traces/:filename` | Delete a managed trace ZIP | Path: `userId`, `filename` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs/:tabId/console` | Get console messages | Query: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/tabs/:tabId/errors` | Get uncaught JS errors | Query: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/console/clear` | Clear console + errors | Body or Query: `userId` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/tabs/:tabId/extract-structured` | Extract deterministic JSON from a structured schema | Body: `userId` + `schema` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n\n### Toggle Display Mode\n```bash\nPOST /sessions/:userId/toggle-display\n{\"headless\": \"virtual\"}\n```\n**Auth:** Conditional — requires `Authorization: Bearer $CAMOFOX_API_KEY` when `CAMOFOX_API_KEY` is set.\nSwitch browser between headless and headed mode. When encountering CAPTCHAs or issues requiring visual interaction, switch to headed mode to show the browser window.\n\nReturns:\n```json\n{\"ok\": true, \"headless\": \"virtual\", \"vncUrl\": \"http://localhost:6080/vnc.html?autoconnect=true\u0026resize=scale\u0026token=...\", \"message\": \"Browser visible via VNC\", \"userId\": \"agent1\"}\n```\n\n**Note:** This restarts the browser context. All tabs are invalidated but cookies/auth state persist via the persistent profile.\n\n### Browser Viewer (noVNC)\nWhen the display mode is set to `\"virtual\"` or `false`, the server automatically starts a VNC viewer accessible via web browser.\n\n```bash\n# 1. Switch to virtual mode\nPOST /sessions/:userId/toggle-display\n{\"headless\": \"virtual\"}\n# Response includes vncUrl — open in browser to see Firefox\n\n# 2. Solve CAPTCHA or interact with the browser\n\n# 3. Switch back to headless\nPOST /sessions/:userId/toggle-display\n{\"headless\": true}\n# VNC automatically stops\n```\n\nThe VNC session auto-terminates after 2 minutes (configurable via `CAMOFOX_VNC_TIMEOUT_MS`).\n\n### Evaluate JavaScript\nExecute a JavaScript expression in the page context and return the JSON-serializable result.\n\nAuth: required only when `CAMOFOX_API_KEY` is set on the server; otherwise no auth is required.\n\nNote: async expressions must be wrapped in an async IIFE (for example, `(async () =\u003e { ... })()`). Top-level `await` is not supported.\n\n```bash\nPOST /tabs/:tabId/evaluate\n{\"userId\": \"agent1\", \"expression\": \"document.title\"}\n```\nReturns: `{\"ok\": true, \"result\": \"Page Title\", \"resultType\": \"string\", \"truncated\": false}`\n\n### Evaluate JavaScript (Extended)\nExecute a long-running JavaScript expression (up to 300s timeout). Conditionally API-key protected. Rate limited.\n\nAuth: required only when `CAMOFOX_API_KEY` is set on the server; otherwise no auth is required.\n\nNote: async expressions must be wrapped in an async IIFE (for example, `(async () =\u003e { ... })()`). Top-level `await` is not supported.\n\n```bash\nPOST /tabs/:tabId/evaluate-extended\n{\"userId\": \"agent1\", \"expression\": \"(async () =\u003e { const response = await fetch('/api/data'); return await response.json(); })()\", \"timeout\": 60000}\n```\nReturns: `{\"ok\": true, \"result\": {...}, \"resultType\": \"object\", \"truncated\": false}`\n\n### OpenClaw Endpoints\n\nOpenClaw-compatible aliases (used by the OpenClaw plugin).\n\n| Method | Endpoint | Description | Required | Auth |\n|--------|----------|-------------|----------|------|\n| GET | `/` | Status (alias of `/health`) | None | None |\n| POST | `/tabs/open` | Open tab (OpenClaw request/response shape) | Body: `userId` + `url` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| POST | `/start` | Start browser engine | None | None |\n| POST | `/stop` | Stop browser engine | None | `x-admin-key: $CAMOFOX_ADMIN_KEY` |\n| POST | `/navigate` | Navigate (OpenClaw request shape: `targetId` in body) | Body: `userId` + `targetId` + `url` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n| GET | `/snapshot?userId=...\u0026targetId=...` | Snapshot (OpenClaw response shape) | Query: `userId` + `targetId` | None |\n| POST | `/act` | Combined actions (`click`, `type`, `press`, `scroll`, `scrollIntoView`, `hover`, `wait`, `close`, `extractStructured`) | Body: `userId` + `targetId` + `kind` | Conditional: `Authorization: Bearer $CAMOFOX_API_KEY` |\n\n### Structured Extract\n\nStructured extract returns deterministic JSON from a DOM schema without arbitrary JavaScript. Use it when you want stable data contracts instead of ad-hoc `evaluate()` calls.\n\nCore API:\n\n```bash\ncurl -X POST \"$CAMOFOX_URL/tabs/$TAB_ID/extract-structured\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"userId\": \"agent1\",\n    \"schema\": {\n      \"kind\": \"object\",\n      \"fields\": {\n        \"title\": { \"kind\": \"text\", \"selector\": \"h1\", \"required\": true, \"trim\": true },\n        \"products\": {\n          \"kind\": \"list\",\n          \"selector\": \".product\",\n          \"item\": {\n            \"kind\": \"object\",\n            \"fields\": {\n              \"name\": { \"kind\": \"text\", \"selector\": \".name\", \"required\": true, \"trim\": true },\n              \"href\": { \"kind\": \"url\", \"selector\": \"a.product-link\", \"attr\": \"href\", \"required\": true }\n            }\n          }\n        }\n      }\n    }\n  }'\n```\n\nCLI:\n\n```bash\ncamofox extract-structured @schema.json \u003ctabId\u003e --user \u003cuserId\u003e --format json\n```\n\nOpenClaw:\n\n```bash\ncurl -X POST \"$CAMOFOX_URL/act\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"kind\": \"extractStructured\",\n    \"targetId\": \"tab-123\",\n    \"userId\": \"agent1\",\n    \"schema\": {\n      \"kind\": \"object\",\n      \"fields\": {\n        \"title\": { \"kind\": \"text\", \"selector\": \"h1\", \"required\": true }\n      }\n    }\n  }'\n```\n\nNotes:\n\n- invalid schemas fail with HTTP 400\n- required runtime misses fail the whole request with HTTP 422 and `fieldPath`\n- optional scalar/object/list nodes normalize to `null` / `null` / `[]`\n- selectors must be CSS; no XPath, arbitrary JavaScript, or AI extraction\n- raw resource extraction and structured extraction stay separate on purpose\n\n## Search Macros\n\nUse macros via `POST /tabs/:tabId/navigate` with `{ \"macro\": \"@google_search\", \"query\": \"...\" }`.\n\n| Macro | Engine |\n|-------|--------|\n| `@google_search` | Google |\n| `@youtube_search` | YouTube |\n| `@amazon_search` | Amazon |\n| `@reddit_search` | Reddit (JSON) |\n| `@reddit_subreddit` | Reddit subreddit (JSON) |\n| `@wikipedia_search` | Wikipedia |\n| `@twitter_search` | Twitter/X |\n| `@yelp_search` | Yelp |\n| `@spotify_search` | Spotify |\n| `@netflix_search` | Netflix |\n| `@linkedin_search` | LinkedIn |\n| `@instagram_search` | Instagram tags |\n| `@tiktok_search` | TikTok |\n| `@twitch_search` | Twitch |\n\n## Geo Presets\n\nBuilt-in presets (also exposed via `GET /presets`):\n\n| Preset | Locale | Timezone | Location |\n|--------|--------|----------|----------|\n| `us-east` | `en-US` | `America/New_York` | New York (40.7128, -74.0060) |\n| `us-west` | `en-US` | `America/Los_Angeles` | Los Angeles (34.0522, -118.2437) |\n| `japan` | `ja-JP` | `Asia/Tokyo` | Tokyo (35.6895, 139.6917) |\n| `uk` | `en-GB` | `Europe/London` | London (51.5074, -0.1278) |\n| `germany` | `de-DE` | `Europe/Berlin` | Berlin (52.5200, 13.4050) |\n| `vietnam` | `vi-VN` | `Asia/Ho_Chi_Minh` | Ho Chi Minh City (10.8231, 106.6297) |\n| `singapore` | `en-SG` | `Asia/Singapore` | Singapore (1.3521, 103.8198) |\n| `australia` | `en-AU` | `Australia/Sydney` | Sydney (-33.8688, 151.2093) |\n\nCreate a tab with a preset:\n\n```bash\ncurl -X POST http://localhost:9377/tabs \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"userId\":\"agent1\",\"sessionKey\":\"task1\",\"preset\":\"japan\",\"url\":\"https://example.com\"}'\n```\n\nCustom presets: set `CAMOFOX_PRESETS_FILE=/path/to/presets.json` (JSON object; keys become preset names).\n\n## Session-Level Proxy and Geo Overrides\n\nCamoFox supports session-level proxy and geolocation configuration with a hybrid model that combines server defaults with per-session overrides.\n\n### Proxy Configuration Model\n\n**Server-level baseline** (via environment variables):\n- `PROXY_HOST`, `PROXY_PORT`, `PROXY_USERNAME`, `PROXY_PASSWORD` — applied as the default for all sessions\n- Enables Camoufox geoip mode when configured\n\n**Session-level overrides** (via `POST /tabs` or CLI):\n- `proxyProfile` — select a named proxy profile from `CAMOFOX_PROXY_PROFILES_FILE`\n- `proxy` — provide raw proxy fields (`host`, `port`, `username`, `password`) directly\n- Session-level proxy overrides the server baseline for that specific `userId + sessionKey` combination\n\n**Session identity rules**:\n- The same `userId` may run different `sessionKey` profiles in parallel with different proxy/geo configurations\n- The same `userId + sessionKey` combination maintains a stable proxy/geo identity — requests with conflicting proxy/geo fields are rejected\n- Session reuse and cleanup scope proxy/geo identity by `userId + sessionKey`, not just `userId`\n\n### Geo Mode Behavior\n\nCamoFox offers two geo modes that control how explicit geo fields (locale, timezone, geolocation) interact with proxy-derived geo:\n\n**`geoMode=explicit-wins`** (default):\n- Explicit geo fields (locale, timezone, geolocation) remain authoritative\n- Proxy-derived geo suggestions are ignored\n- Use this mode when you want precise geo control regardless of proxy location\n\n**`geoMode=proxy-locked`**:\n- Explicit geo fields that conflict with proxy-derived geo are rejected\n- Proxy-derived geo is authoritative\n- Use this mode to ensure geo consistency with proxy exit location\n\n### CLI Examples\n\nSession-level proxy with named profile:\n```bash\ncamofox open https://example.com --proxy-profile tokyo-exit --user agent1\n```\n\nSession-level proxy with raw fields:\n```bash\ncamofox open https://example.com \\\n  --proxy-host proxy.example.com \\\n  --proxy-port 8080 \\\n  --proxy-username user \\\n  --proxy-password pass \\\n  --user agent1\n```\n\nCombine proxy with geo mode:\n```bash\ncamofox open https://example.com \\\n  --proxy-profile london-exit \\\n  --geo uk \\\n  --geo-mode proxy-locked \\\n  --user agent1\n```\n\n### API Examples\n\nUsing a named proxy profile with explicit geo:\n```bash\ncurl -X POST http://localhost:9377/tabs \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"userId\": \"agent1\",\n    \"sessionKey\": \"task1\",\n    \"url\": \"https://example.com\",\n    \"proxyProfile\": \"tokyo-exit\",\n    \"preset\": \"japan\",\n    \"geoMode\": \"explicit-wins\"\n  }'\n```\n\nUsing raw proxy fields with proxy-locked geo:\n```bash\ncurl -X POST http://localhost:9377/tabs \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"userId\": \"agent1\",\n    \"sessionKey\": \"task2\",\n    \"url\": \"https://example.com\",\n    \"proxy\": {\n      \"host\": \"proxy.example.com\",\n      \"port\": 8080,\n      \"username\": \"user\",\n      \"password\": \"pass\"\n    },\n    \"geoMode\": \"proxy-locked\"\n  }'\n```\n\n### Named Proxy Profiles\n\nDefine proxy profiles in a JSON file and point `CAMOFOX_PROXY_PROFILES_FILE` to it:\n\n```json\n{\n  \"tokyo-exit\": {\n    \"server\": \"http://tokyo.proxy.example.com:8080\",\n    \"username\": \"user\",\n    \"password\": \"pass\"\n  },\n  \"london-exit\": {\n    \"server\": \"http://london.proxy.example.com:8080\"\n  }\n}\n```\n\nThen use profiles by name in API requests or CLI commands.\n\n## Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `CAMOFOX_PORT` | `9377` | Server port |\n| `PORT` | (optional) | Alternative port env var (common in PaaS) |\n| `NODE_ENV` | `development` | Node environment |\n| `CAMOFOX_HOST` | `127.0.0.1` | Server bind host. Set `0.0.0.0` for Docker/PaaS/network exposure. Non-loopback binds require `CAMOFOX_API_KEY`. |\n| `CAMOFOX_ADMIN_KEY` | (empty) | Required for `POST /stop` (sent via `x-admin-key`) |\n| `CAMOFOX_API_KEY` | (empty) | Guards protected endpoints (tab creation, navigation, interaction, session management, downloads, image extraction, tracing, console) via `Authorization: Bearer` header when set. Required whenever `CAMOFOX_HOST` exposes the server beyond loopback. |\n| `CAMOFOX_ALLOW_PRIVATE_NETWORK` | `true` on loopback binds, `false` otherwise | Allows navigation to loopback/private/link-local/metadata targets. Leave unset for the safe default; enable only for trusted deployments that intentionally need internal-network reachability. |\n| `CAMOFOX_HEADLESS` | `true` | Display mode: `true` (headless), `false` (headed), `virtual` (Xvfb) |\n| `CAMOFOX_VNC_RESOLUTION` | `1920x1080x24` | Virtual Xvfb display resolution (`WIDTHxHEIGHTxDEPTH`) |\n| `CAMOFOX_VNC_TIMEOUT_MS` | `120000` | Max VNC session duration in ms before auto-stop |\n| `CAMOFOX_EVAL_EXTENDED_RATE_LIMIT_MAX` | `20` | Max evaluate-extended requests per user per window |\n| `CAMOFOX_EVAL_EXTENDED_RATE_LIMIT_WINDOW_MS` | `60000` | Rate limit window duration in ms |\n| `CAMOFOX_COOKIES_DIR` | `~/.camofox/cookies` | Directory used by the OpenClaw plugin cookie tool |\n| `CAMOFOX_PROFILES_DIR` | `~/.camofox/profiles` | Profile storage directory (persistent per-user Firefox profiles) |\n| `CAMOFOX_DOWNLOADS_DIR` | `~/.camofox/downloads` | Download artifact directory |\n| `CAMOFOX_DOWNLOAD_TTL_MS` | `86400000` | Download metadata retention TTL |\n| `CAMOFOX_MAX_DOWNLOAD_SIZE_MB` | `100` | Max single download size |\n| `CAMOFOX_MAX_BATCH_CONCURRENCY` | `5` | Batch download concurrency cap |\n| `CAMOFOX_MAX_BLOB_SIZE_MB` | `5` | Max blob payload size |\n| `CAMOFOX_MAX_DOWNLOADS_PER_USER` | `500` | Per-user download record cap |\n| `HANDLER_TIMEOUT_MS` | `30000` | Handler timeout fallback |\n| `MAX_CONCURRENT_PER_USER` | `3` | Concurrent operations per user |\n| `CAMOFOX_VNC_BASE_PORT` | `6080` | noVNC/websockify base port |\n| `CAMOFOX_VNC_HOST` | `localhost` | noVNC host in returned URL |\n| `CAMOFOX_CLI_USER` | `cli-default` | Default CLI user id |\n| `CAMOFOX_IDLE_TIMEOUT_MS` | `1800000` | Stage 1 idle cleanup threshold (ms) |\n| `CAMOFOX_IDLE_EXIT_TIMEOUT_MS` | `1800000` | Stage 2 daemon exit quiet window (ms, defaults to match Stage 1) |\n| `CAMOFOX_PRESETS_FILE` | (unset) | Optional JSON file defining/overriding geo presets |\n| `CAMOFOX_PROXY_PROFILES_FILE` | (unset) | Optional JSON file defining named proxy profiles for session-level overrides |\n| `CAMOFOX_SESSION_TIMEOUT` | `1800000` | Session idle timeout in ms (min `60000`) |\n| `CAMOFOX_MAX_SESSIONS` | `50` | Maximum concurrent sessions |\n| `CAMOFOX_MAX_TABS` | `10` | Maximum tabs per session |\n| `PROXY_HOST` | (empty) | Proxy host (server-level default; enables proxy routing) |\n| `PROXY_PORT` | (empty) | Proxy port (server-level default) |\n| `PROXY_USERNAME` | (empty) | Proxy username (server-level default) |\n| `PROXY_PASSWORD` | (empty) | Proxy password (server-level default) |\n| `CAMOFOX_MAX_SNAPSHOT_CHARS` | `80000` | Max characters in snapshot before truncation |\n| `CAMOFOX_SNAPSHOT_TAIL_CHARS` | `5000` | Characters preserved at end of truncated snapshot |\n| `CAMOFOX_BUILDREFS_TIMEOUT_MS` | `12000` | Timeout for building element refs |\n| `CAMOFOX_TAB_LOCK_TIMEOUT_MS` | `30000` | Timeout for acquiring tab lock |\n| `CAMOFOX_HEALTH_PROBE_INTERVAL_MS` | `60000` | Health probe check interval |\n| `CAMOFOX_FAILURE_THRESHOLD` | `3` | Consecutive failures before health degradation |\n| `CAMOFOX_YT_DLP_TIMEOUT_MS` | `30000` | Timeout for yt-dlp subtitle extraction |\n| `CAMOFOX_YT_BROWSER_TIMEOUT_MS` | `25000` | Timeout for browser transcript fallback |\n| `CAMOFOX_OS` | (unset) | Optional server-wide Camoufox OS override (`windows`, `macos`, `linux`, or comma-separated list for randomization) |\n| `CAMOFOX_ALLOW_WEBGL` | (unset) | Optional server-wide WebGL override; malformed values fail startup |\n| `CAMOFOX_SCREEN_WIDTH` | (unset) | Optional screen width override; applied only with `CAMOFOX_SCREEN_HEIGHT` |\n| `CAMOFOX_SCREEN_HEIGHT` | (unset) | Optional screen height override; applied only with `CAMOFOX_SCREEN_WIDTH` |\n| `CAMOFOX_HUMANIZE` | (unset) | Optional server-wide humanization override |\n\n\u003e `CAMOFOX_OS` and `CAMOFOX_SCREEN_*` are **generation-time** controls: they affect only newly generated fingerprints and have no effect while an existing `fingerprint.json` sidecar is in use. Reset the profile or delete `fingerprint.json` to force regeneration under the new defaults. `CAMOFOX_ALLOW_WEBGL` and `CAMOFOX_HUMANIZE` are **launch-time** overrides and apply on every browser launch regardless of whether a sidecar is reused.\n\n### Idle Lifecycle Policy\n\nCamoFox implements a two-stage idle lifecycle for graceful cleanup and daemon exit:\n\n**Stage 1 — Idle Cleanup**\n- After `CAMOFOX_IDLE_TIMEOUT_MS` of idle time (default: 30 minutes), the server runs cleanup to close idle sessions and tabs\n- Cleanup is delayed if browser contexts are launching or sessions are being created\n- New interactive activity (tab creation, navigation, interaction) cancels any pending cleanup\n\n**Stage 2 — Daemon Exit**\n- After Stage 1 cleanup completes, the server waits for `CAMOFOX_IDLE_EXIT_TIMEOUT_MS` (default: matches Stage 1 timeout)\n- If no new activity occurs during this quiet window, the daemon process exits gracefully\n- Any new request activity cancels the pending exit timer\n\n**Activity detection**:\n- Live tabs, launching browser contexts, or staged session creation count as active work and prevent cleanup\n- Empty sessions (sessions with no tabs) do not block cleanup but do disarm pending daemon exit\n- New interactive activity resets both cleanup and exit timers\n\nThis two-stage model ensures cleanup runs before daemon exit, preventing resource leaks while allowing the server to shut down cleanly when fully idle.\n\n## Deployment\n\n### Docker (Recommended)\n\n```bash\ndocker build -t camofox-browser .\ndocker run -p 9377:9377 -p 6080:6080 \\\n  -v ~/.camofox:/home/node/.camofox \\\n  -e CAMOFOX_HOST=0.0.0.0 \\\n  -e CAMOFOX_PORT=9377 \\\n  -e CAMOFOX_API_KEY=change-me \\\n  camofox-browser\n```\n\n### Fly.io\n\nThis repo includes a starter `fly.toml` for one-command deploys.\n\n```bash\nfly launch\nfly deploy\n```\n\n### Railway\n\n- Create a new project → deploy from this GitHub repo\n- Set `CAMOFOX_HOST=0.0.0.0`\n- Set `CAMOFOX_API_KEY` to a strong secret\n- Set `CAMOFOX_PORT=9377` (Railway will also provide `PORT`, which is supported)\n- Ensure the service exposes port `9377`\n\n### Render\n\n- Create a new Web Service → deploy from this GitHub repo\n- Use Docker (recommended) and expose port `9377`\n- Set `CAMOFOX_HOST=0.0.0.0`\n- Set `CAMOFOX_API_KEY` to a strong secret\n- Set `CAMOFOX_PORT=9377` (or rely on Render `PORT`)\n\n### System Requirements\n\n- Node.js 20+\n- 2GB+ RAM (browser + contexts require significant memory)\n- Linux recommended for production; macOS is fine for development\n\n## Used With\n\n| Project | Description |\n|---------|-------------|\n| [CamoFox MCP](https://github.com/redf0x1/camofox-mcp) | MCP (Model Context Protocol) server for Claude, Cursor, VS Code |\n| [OpenClaw](https://openclaw.ai) | Open-source AI agent framework (compat endpoints included) |\n| [Camoufox](https://github.com/daijro/camoufox) | Anti-detection Firefox browser engine |\n\n## Project Structure\n\n```text\nsrc/\n├── cli/\n│   ├── commands/       # Command modules (core, navigation, interaction, etc.)\n│   │   ├── console.ts   # Console capture commands\n│   │   └── trace.ts     # Playwright tracing commands\n│   ├── vault/          # Auth vault (encryption, storage)\n│   ├── server/         # Server lifecycle management\n│   ├── transport/      # HTTP transport layer\n│   ├── output/         # Output formatting\n│   └── utils/          # Shared utilities\n├── server.ts           # Express app entry point\n├── types.ts            # Shared TypeScript interfaces\n├── routes/\n│   ├── core.ts         # Core REST API (~42 endpoints)\n│   └── openclaw.ts     # OpenClaw compatibility (~7 endpoints)\n├── services/\n│   ├── browser.ts      # Browser lifecycle + persistent context pool\n│   ├── batch-downloader.ts # Batch download orchestrator\n│   ├── context-pool.ts # Browser context pool with LRU eviction\n│   ├── download.ts     # Download tracking service\n│   ├── health.ts       # Browser health tracking\n│   ├── resource-extractor.ts # Page resource extraction\n│   ├── session.ts      # Session management + limits\n│   ├── tab.ts          # Tab operations (snapshot/click/type/etc.)\n│   ├── tracing.ts      # Playwright tracing service\n│   ├── vnc.ts          # VNC/virtual display lifecycle\n│   └── youtube.ts      # YouTube transcript extraction\n├── middleware/\n│   ├── auth.ts         # API/admin auth helpers\n│   ├── errors.ts       # Error handling\n│   ├── logging.ts      # Structured logging\n│   └── rate-limit.ts   # In-memory rate limiter\n└── utils/\n  ├── config.ts       # Environment config parsing\n  ├── cookies.ts      # Cookie utilities\n  ├── download-helpers.ts # Download helper functions\n  ├── launcher.ts     # Browser launcher utilities\n  ├── macros.ts       # Search macro expansion\n  ├── presets.ts      # Geo preset definitions/loader\n  └── snapshot.ts     # Snapshot truncation/windowing\n```\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md)\n\n## Credits\n\nThis project is based on [camofox-browser](https://github.com/jo-inc/camofox-browser) by [Jo Inc](https://github.com/jo-inc) (YC W24) and the [Camoufox](https://github.com/daijro/camoufox) anti-detection browser engine by [daijro](https://github.com/daijro).\n\n- [Camoufox](https://camoufox.com) - Firefox-based browser with C++ anti-detection\n- [Donate to Camoufox's original creator daijro](https://camoufox.com/about/)\n- [OpenClaw](https://openclaw.ai) - Open-source AI agent framework\n\n## License\n\n[MIT](LICENSE)\n\n## Crypto Scam Warning\n\nSketchy people are doing sketchy things with crypto tokens named \"Camofox\" now that this project is getting attention. **Camofox is not a crypto project and will never be one.** Any token, coin, or NFT using the Camofox name has nothing to do with us.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredf0x1%2Fcamofox-browser","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fredf0x1%2Fcamofox-browser","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fredf0x1%2Fcamofox-browser/lists"}