
Security News
/Research
Fake Corepack Site Distributes Infostealer and Proxyware to Developers
A fake corepack.org site is impersonating the Node.js tool and delivers an infostealer and proxyware to developers who download it.
camofox-browser
Advanced tools
Anti-detection browser server for AI agents — REST API wrapping Camoufox engine with OpenClaw plugin support
Anti-detection browser server for AI agents — TypeScript REST API wrapping the Camoufox stealth browser engine
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.
The Solution: CamoFox Browser Server wraps Camoufox, a Firefox fork with C++ engine-level fingerprint spoofing. No JavaScript injection — anti-detection happens at the browser engine level.
| Feature | Puppeteer/Playwright | CamoFox Browser Server |
|---|---|---|
| Anti-detection | JavaScript patches (fragile) | C++ engine-level (robust) |
| Fingerprint spoofing | Limited | Full (engine-level) |
| Token efficiency | Raw HTML / screenshots | Accessibility snapshots (smaller + structured) |
| Integration | Direct SDK | REST API for any language / AI agent |
| AI agent support | Varies | MCP + OpenClaw compatible |
userId (defaults: max 50 sessions, max 10 tabs/session)explicit-wins or proxy-locked)eN element references for precise interactionCAMOFOX_API_KEY is set)/start, /tabs/open, /act, etc.)CamoFox 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.
During Preview, CamoFox follows an additive-only deprecation model:
listItemId accepted alongside sessionKey, OpenClaw /act routing to core endpoints) continue to work alongside their replacementsBrowser profiles, download registries, and CLI session files use versioned sidecar formats. When upgrading CamoFox:
Supported 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.
git clone https://github.com/redf0x1/camofox-browser.git
cd camofox-browser
npm install
npm run build
npm start
npm install -g camofox-browser
# Start the server
camofox-browser
# Or use the CLI for browser automation
camofox open https://example.com
camofox snapshot
camofox click e5
See CLI for the complete command reference.
Docker image:
ghcr.io/redf0x1/camofox-browser
docker build -t camofox-browser .
docker run -d \
--name camofox-browser \
-p 9377:9377 \
-p 6080:6080 \
-e CAMOFOX_HOST=0.0.0.0 \
-e CAMOFOX_AUTH_MODE=auto \
-e CAMOFOX_API_KEY=change-me \
-v ~/.camofox:/home/node/.camofox \
camofox-browser
To persist browser profiles (cookies, localStorage, IndexedDB, etc.) across container restarts, keep the volume mount shown above.
services:
camofox-browser:
build: .
ports:
- "9377:9377"
environment:
CAMOFOX_HOST: "0.0.0.0"
CAMOFOX_PORT: "9377"
# auto requires CAMOFOX_API_KEY when CAMOFOX_HOST is non-loopback
CAMOFOX_AUTH_MODE: "auto"
CAMOFOX_API_KEY: "change-me"
# CAMOFOX_ADMIN_KEY: "change-me"
# Optional: proxy routing (also enables Camoufox geoip mode)
# PROXY_HOST: ""
# PROXY_PORT: ""
# PROXY_USERNAME: ""
# PROXY_PASSWORD: ""
curl http://localhost:9377/health
# {"ok":true,"engine":"camoufox","browserConnected":true}
CamoFox Browser includes a powerful CLI for browser automation directly from the terminal. The CLI auto-starts the server when needed.
# Global install (recommended)
npm install -g camofox-browser
# Or use npx (no install needed)
npx camofox-browser open https://example.com
camofox open https://example.com # Open a page in anti-detection browser
camofox snapshot # Get accessibility tree with element refs
camofox click e5 # Click element [e5]
camofox type e3 "hello world" # Type into element [e3]
camofox screenshot --output page.png # Save screenshot
camofox close # Close the tab
# Browser lifecycle
camofox open <url> # Open URL in new tab
camofox close [tabId] # Close tab
camofox navigate <url> # Navigate current tab to URL
# Inspection
camofox snapshot # Get accessibility tree with [eN] refs
camofox screenshot [--output file] # Take screenshot (saves to file)
camofox annotate # Screenshot + element ref overlay
camofox get-url # Get current page URL
camofox get-text # Get page text content
camofox get-links # Get all links on page
camofox get-tabs # List open tabs
camofox extract-structured @schema.json # Extract deterministic JSON from a schema
# Interaction
camofox click <ref> # Click element by ref
camofox type <ref> <text> # Type text into element
camofox fill '[e1]="user" [e2]="pw"' # Fill multiple fields at once
camofox scroll <direction> # Scroll up/down/left/right
camofox select <ref> <value> # Select dropdown option
camofox hover <ref> # Hover over element
camofox press <key> # Press keyboard key
camofox drag <from> <to> # Drag element to target
# Navigation
camofox go-back # Browser back
camofox go-forward # Browser forward
camofox search "query" --engine google # Search (14 engines supported)
camofox eval "document.title" # Execute JavaScript
camofox wait <selector> [--timeout ms] # Wait for element
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.
camofox session save <name> # Save current browser state
camofox session load <name> # Restore browser state
camofox session list # List saved sessions
camofox session delete <name> # Delete saved session
camofox cookie export <file> # Export cookies to JSON file
camofox cookie import <file> # Import cookies from JSON file
Securely store credentials locally with AES-256-GCM encryption. Credentials are never output to stdout — safe for LLM agent automation.
camofox auth save <profile> [--url URL] # Save credentials (prompts for master password)
camofox auth load <profile> # Show profile info (username only)
camofox auth list # List saved profiles (no secrets shown)
camofox auth delete <profile> # Delete a profile
camofox auth change-password <profile> # Change master password
# Inject credentials into a browser tab (LLM-safe)
camofox snapshot # Get element refs first
camofox auth load gmail --inject --username-ref e5 --password-ref e12
Security: Master passwords use Argon2id KDF (with PBKDF2 fallback). Vault files are stored with 0600 permissions. The
--injectflag sends credentials directly to the browser — the LLM agent never sees the password.
Execute multiple commands from a file for automation workflows:
# Create a script
cat > login-flow.txt << 'EOF'
# Login automation script
open https://example.com/login
snapshot
type e3 "username"
type e5 "password"
click e7
wait .dashboard --timeout 5000
screenshot --output result.png
close
EOF
# Run it
camofox run login-flow.txt
# Continue on errors
camofox run login-flow.txt --continue-on-error
# Read from stdin
echo "get-url" | camofox run -
camofox server start # Start server daemon
camofox server start --background # Start in background
camofox server stop # Stop server daemon
camofox server status # Check server status
camofox health # System health report
camofox version # CLI + server version
camofox info # Configuration info
camofox console [tabId] # View console messages
camofox console [tabId] --type error # Filter by type (log/warning/error/info/debug)
camofox console [tabId] --clear # View then clear messages
camofox errors [tabId] # View uncaught JavaScript errors
camofox errors [tabId] --clear # View then clear errors
camofox trace start [tabId] # Start recording trace
camofox trace stop [tabId] [-o file.zip] # Stop and save trace ZIP
camofox trace chunk-start [tabId] # Start new trace chunk
camofox trace chunk-stop [tabId] [-o f] # Stop chunk and save ZIP
camofox trace status [tabId] # Check active trace status
View traces at trace.playwright.dev
| Flag | Env Var | Description | Default |
|---|---|---|---|
--user <id> | CAMOFOX_USER | User/profile ID | cli-default |
--port <port> | PORT | Server port | 9377 |
--format <fmt> | — | Output: json, text, plain | text |
-V, --version | — | Show version | — |
-h, --help | — | Show help | — |
camofox get-url --format json # {"url":"https://example.com"}
camofox get-url --format text # URL: https://example.com
camofox get-url --format plain # https://example.com
Tip: Use
--format jsonfor programmatic parsing and LLM agent integration.
CamoFox uses 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.
0600 permissions--inject flag sends credentials directly to the browser — the LLM agent orchestrating the CLI never sees raw passwordsCamoFox works seamlessly with AI coding agents and LLM-powered automation:
Add CamoFox skills to your AI coding assistant for full browser automation context:
npx skills add redf0x1/camofox-browser
This works with Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, Goose, OpenCode, Windsurf, and 40+ other agents.
Available skills:
| Skill | Focus | Best For |
|---|---|---|
camofox-browser | Full coverage (CLI + API + OpenClaw) | Complete reference |
camofox-cli | CLI-only (50+ commands) | Terminal-first workflows |
dogfood | QA testing workflow | Systematic web app testing |
gemini-image | Gemini image generation | AI image automation |
reddit | Reddit automation | Reddit posting/commenting |
The installer will prompt you to choose which skills and which agents to configure.
npx skills add redf0x1/camofox-browser
# Installs to .claude/skills/camofox-browser/SKILL.md
npx skills add redf0x1/camofox-browser
# Installs to .agents/skills/ directory
Tip: Skills are symlinked from the repo, so they stay up to date. Do not manually copy
SKILL.mdfiles.
Use CamoFox MCP for direct integration with Claude, Cursor, Windsurf, and other MCP-compatible agents. See Used With.
AI agents can use the CLI with --format json for structured output:
camofox open https://example.com # Open page
camofox snapshot --format json # Get structured element tree
camofox click e5 # Interact with elements
camofox auth load gmail --inject --username-ref e5 --password-ref e12 # Safe credential injection
Create reusable automation scripts that AI agents can execute:
camofox run automation-flow.txt # Execute multi-step workflow
AI Agent (MCP / OpenClaw / REST Client)
│
▼ HTTP REST API (port 9377)
┌──────────────────────────────────────────┐
│ CamoFox Browser Server │
│ (Express + TypeScript) │
├──────────────────────────────────────────┤
│ Routes Services │
│ ├── Core API ├── Browser │
│ └── OpenClaw compat ├── Session │
│ └── Tab ops │
├──────────────────────────────────────────┤
│ Camoufox Engine (anti-detect) │
│ Firefox fork + engine-level spoofing │
└──────────────────────────────────────────┘
userId runs in its own persistent Firefox process/context (backed by launchPersistentContext(userDataDir))~/.camofox/profiles/{userId}/ (override via CAMOFOX_PROFILES_DIR)Base URL: http://localhost:9377
Security defaults:
CAMOFOX_HOSTnow defaults to127.0.0.1, andCAMOFOX_AUTH_MODE=autokeeps local loopback usage open while requiringCAMOFOX_API_KEYwhen you bind beyond loopback (for example0.0.0.0in Docker or PaaS). SetCAMOFOX_AUTH_MODE=requiredto require API-key auth everywhere. SetCAMOFOX_AUTH_MODE=disabledonly for trusted private agent networks whose clients cannot send bearer tokens; this disables API-key auth for protected endpoints and keeps private-network navigation blocked. On non-loopback binds, navigation targets on loopback/private/link-local/metadata hosts are blocked by default unless you explicitly setCAMOFOX_ALLOW_PRIVATE_NETWORK=true. If you also configurePROXY_HOST/PROXY_PORT, exposed deployments must opt intoCAMOFOX_ALLOW_PRIVATE_NETWORK=trueuntil proxy-side private-target validation exists.
The Camofox Browser API includes OpenAPI 3.1.0 docs for a representative subset of the shipped route surface:
The OpenAPI spec covers a representative subset of core and OpenClaw endpoints, including request schemas, response shapes, authentication requirements, and validation rules.
Note: 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.
| Method | Endpoint | Description | Required | Auth |
|---|---|---|---|---|
| POST | /sessions/:userId/cookies | Import cookies into a user session (Playwright cookie objects) | Path: userId; Body: { "cookies": Cookie[] } | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /health | Health check (also pre-launches the browser) | None | None |
| GET | /presets | List available geo presets (built-in + custom) | None | None |
| POST | /tabs | Create a new tab (supports preset + per-field overrides) | Body: userId + (sessionKey or listItemId) | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /tabs?userId=... | List all tabs for a user (OpenClaw-compatible response shape) | Query: userId | None |
| 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 |
| GET | /tabs/:tabId/snapshot?userId=... | Accessibility snapshot annotated with eN element refs | Query: userId | None |
| POST | /tabs/:tabId/wait | Wait for page readiness (DOM + optional network idle) | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/click | Click by ref (e.g. e12) or CSS selector | Body: userId + (ref or selector) | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| 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 |
| POST | /tabs/:tabId/press | Press a key (e.g. Enter, Escape) | Body: userId + key | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/scroll | Scroll up/down/left/right by pixels | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/scroll-element | Scroll specific element into view | Body: userId, ref/selector | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/back | Go back | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/forward | Go forward | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/refresh | Refresh | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /tabs/:tabId/links?userId=...&limit=50&offset=0 | Extract links (paginated) | Query: userId | None |
| GET | /tabs/:tabId/images?userId=... | List extracted images | Query: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /tabs/:tabId/screenshot?userId=...&fullPage=true | Screenshot (PNG bytes) | Query: userId | None |
| GET | /tabs/:tabId/stats?userId=... | Tab stats + visited URLs | Query: userId | None |
| DELETE | /tabs/:tabId | Close a tab (expects JSON body: { "userId": "..." }) | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| DELETE | /tabs/group/:listItemId | Close a tab group (expects JSON body: { "userId": "..." }) | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| DELETE | /sessions/:userId | Close all sessions for a user | Path: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /sessions/:userId/toggle-display | Toggle display mode (headless/headed/virtual) | Path: userId; Body: { "headless": true|false|"virtual" } | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /tabs/:tabId/cookies | Export tab cookies | Query: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /tabs/:tabId/downloads | List tab downloads | Query: userId | None |
| GET | /users/:userId/downloads | List user downloads | Path: userId | None |
| GET | /downloads/:downloadId | Download metadata | Query: userId | None |
| GET | /downloads/:downloadId/content | Stream download content | Query: userId | None |
| DELETE | /downloads/:downloadId | Delete tracked download | Body or Query: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/extract-resources | Extract downloadable resources | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/batch-download | Batch download resources | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/resolve-blobs | Resolve blob URLs to base64 | Body: userId + urls[] | None |
| POST | /tabs/:tabId/trace/start | Start trace recording | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/trace/stop | Stop and save trace ZIP | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/trace/chunk/start | Start trace chunk | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/trace/chunk/stop | Stop chunk and save ZIP | Body: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /tabs/:tabId/trace/status | Check trace status | Query: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /sessions/:userId/traces | List managed trace ZIPs for a user | Path: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /sessions/:userId/traces/:filename | Download a managed trace ZIP | Path: userId, filename | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| DELETE | /sessions/:userId/traces/:filename | Delete a managed trace ZIP | Path: userId, filename | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /tabs/:tabId/console | Get console messages | Query: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /tabs/:tabId/errors | Get uncaught JS errors | Query: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/console/clear | Clear console + errors | Body or Query: userId | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /tabs/:tabId/extract-structured | Extract deterministic JSON from a structured schema | Body: userId + schema | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
POST /sessions/:userId/toggle-display
{"headless": "virtual"}
Auth: Conditional — requires Authorization: Bearer $CAMOFOX_API_KEY when CAMOFOX_AUTH_MODE=required or when auto mode has an API key configured. No auth is required in CAMOFOX_AUTH_MODE=disabled.
Switch browser between headless and headed mode. When encountering CAPTCHAs or issues requiring visual interaction, switch to headed mode to show the browser window.
Returns:
{"ok": true, "headless": "virtual", "vncUrl": "http://localhost:6080/vnc.html?autoconnect=true&resize=scale&token=...", "message": "Browser visible via VNC", "userId": "agent1"}
Note: This restarts the browser context. All tabs are invalidated but cookies/auth state persist via the persistent profile.
When the display mode is set to "virtual" or false, the server automatically starts a VNC viewer accessible via web browser.
# 1. Switch to virtual mode
POST /sessions/:userId/toggle-display
{"headless": "virtual"}
# Response includes vncUrl — open in browser to see Firefox
# 2. Solve CAPTCHA or interact with the browser
# 3. Switch back to headless
POST /sessions/:userId/toggle-display
{"headless": true}
# VNC automatically stops
The VNC session auto-terminates after 2 minutes (configurable via CAMOFOX_VNC_TIMEOUT_MS).
Execute a JavaScript expression in the page context and return the JSON-serializable result.
Auth: required when CAMOFOX_AUTH_MODE=required or when auto mode has an API key configured; no auth is required in CAMOFOX_AUTH_MODE=disabled.
Note: async expressions must be wrapped in an async IIFE (for example, (async () => { ... })()). Top-level await is not supported.
POST /tabs/:tabId/evaluate
{"userId": "agent1", "expression": "document.title"}
Returns: {"ok": true, "result": "Page Title", "resultType": "string", "truncated": false}
Execute a long-running JavaScript expression (up to 300s timeout). Conditionally API-key protected. Rate limited.
Auth: required when CAMOFOX_AUTH_MODE=required or when auto mode has an API key configured; no auth is required in CAMOFOX_AUTH_MODE=disabled.
Note: async expressions must be wrapped in an async IIFE (for example, (async () => { ... })()). Top-level await is not supported.
POST /tabs/:tabId/evaluate-extended
{"userId": "agent1", "expression": "(async () => { const response = await fetch('/api/data'); return await response.json(); })()", "timeout": 60000}
Returns: {"ok": true, "result": {...}, "resultType": "object", "truncated": false}
OpenClaw-compatible aliases (used by the OpenClaw plugin).
| Method | Endpoint | Description | Required | Auth |
|---|---|---|---|---|
| GET | / | Status (alias of /health) | None | None |
| POST | /tabs/open | Open tab (OpenClaw request/response shape) | Body: userId + url | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| POST | /start | Start browser engine | None | None |
| POST | /stop | Stop browser engine | None | x-admin-key: $CAMOFOX_ADMIN_KEY |
| POST | /navigate | Navigate (OpenClaw request shape: targetId in body) | Body: userId + targetId + url | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
| GET | /snapshot?userId=...&targetId=... | Snapshot (OpenClaw response shape) | Query: userId + targetId | None |
| POST | /act | Combined actions (click, type, press, scroll, scrollIntoView, hover, wait, close, extractStructured) | Body: userId + targetId + kind | Conditional: Authorization: Bearer $CAMOFOX_API_KEY |
Structured 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.
Core API:
curl -X POST "$CAMOFOX_URL/tabs/$TAB_ID/extract-structured" \
-H 'Content-Type: application/json' \
-d '{
"userId": "agent1",
"schema": {
"kind": "object",
"fields": {
"title": { "kind": "text", "selector": "h1", "required": true, "trim": true },
"products": {
"kind": "list",
"selector": ".product",
"item": {
"kind": "object",
"fields": {
"name": { "kind": "text", "selector": ".name", "required": true, "trim": true },
"href": { "kind": "url", "selector": "a.product-link", "attr": "href", "required": true }
}
}
}
}
}
}'
CLI:
camofox extract-structured @schema.json <tabId> --user <userId> --format json
OpenClaw:
curl -X POST "$CAMOFOX_URL/act" \
-H 'Content-Type: application/json' \
-d '{
"kind": "extractStructured",
"targetId": "tab-123",
"userId": "agent1",
"schema": {
"kind": "object",
"fields": {
"title": { "kind": "text", "selector": "h1", "required": true }
}
}
}'
Notes:
fieldPathnull / null / []Use macros via POST /tabs/:tabId/navigate with { "macro": "@google_search", "query": "..." }.
| Macro | Engine |
|---|---|
@google_search | |
@youtube_search | YouTube |
@amazon_search | Amazon |
@reddit_search | Reddit (JSON) |
@reddit_subreddit | Reddit subreddit (JSON) |
@wikipedia_search | Wikipedia |
@twitter_search | Twitter/X |
@yelp_search | Yelp |
@spotify_search | Spotify |
@netflix_search | Netflix |
@linkedin_search | |
@instagram_search | Instagram tags |
@tiktok_search | TikTok |
@twitch_search | Twitch |
Built-in presets (also exposed via GET /presets):
| Preset | Locale | Timezone | Location |
|---|---|---|---|
us-east | en-US | America/New_York | New York (40.7128, -74.0060) |
us-west | en-US | America/Los_Angeles | Los Angeles (34.0522, -118.2437) |
japan | ja-JP | Asia/Tokyo | Tokyo (35.6895, 139.6917) |
uk | en-GB | Europe/London | London (51.5074, -0.1278) |
germany | de-DE | Europe/Berlin | Berlin (52.5200, 13.4050) |
vietnam | vi-VN | Asia/Ho_Chi_Minh | Ho Chi Minh City (10.8231, 106.6297) |
singapore | en-SG | Asia/Singapore | Singapore (1.3521, 103.8198) |
australia | en-AU | Australia/Sydney | Sydney (-33.8688, 151.2093) |
Create a tab with a preset:
curl -X POST http://localhost:9377/tabs \
-H 'Content-Type: application/json' \
-d '{"userId":"agent1","sessionKey":"task1","preset":"japan","url":"https://example.com"}'
Custom presets: set CAMOFOX_PRESETS_FILE=/path/to/presets.json (JSON object; keys become preset names).
CamoFox supports session-level proxy and geolocation configuration with a hybrid model that combines server defaults with per-session overrides.
Server-level baseline (via environment variables):
PROXY_HOST, PROXY_PORT, PROXY_USERNAME, PROXY_PASSWORD — applied as the default for all sessionsSession-level overrides (via POST /tabs or CLI):
proxyProfile — select a named proxy profile from CAMOFOX_PROXY_PROFILES_FILEproxy — provide raw proxy fields (host, port, username, password) directlyuserId + sessionKey combinationSession identity rules:
userId may run different sessionKey profiles in parallel with different proxy/geo configurationsuserId + sessionKey combination maintains a stable proxy/geo identity — requests with conflicting proxy/geo fields are rejecteduserId + sessionKey, not just userIdCamoFox offers two geo modes that control how explicit geo fields (locale, timezone, geolocation) interact with proxy-derived geo:
geoMode=explicit-wins (default):
geoMode=proxy-locked:
Session-level proxy with named profile:
camofox open https://example.com --proxy-profile tokyo-exit --user agent1
Session-level proxy with raw fields:
camofox open https://example.com \
--proxy-host proxy.example.com \
--proxy-port 8080 \
--proxy-username user \
--proxy-password pass \
--user agent1
Combine proxy with geo mode:
camofox open https://example.com \
--proxy-profile london-exit \
--geo uk \
--geo-mode proxy-locked \
--user agent1
Using a named proxy profile with explicit geo:
curl -X POST http://localhost:9377/tabs \
-H 'Content-Type: application/json' \
-d '{
"userId": "agent1",
"sessionKey": "task1",
"url": "https://example.com",
"proxyProfile": "tokyo-exit",
"preset": "japan",
"geoMode": "explicit-wins"
}'
Using raw proxy fields with proxy-locked geo:
curl -X POST http://localhost:9377/tabs \
-H 'Content-Type: application/json' \
-d '{
"userId": "agent1",
"sessionKey": "task2",
"url": "https://example.com",
"proxy": {
"host": "proxy.example.com",
"port": 8080,
"username": "user",
"password": "pass"
},
"geoMode": "proxy-locked"
}'
Define proxy profiles in a JSON file and point CAMOFOX_PROXY_PROFILES_FILE to it:
{
"tokyo-exit": {
"server": "http://tokyo.proxy.example.com:8080",
"username": "user",
"password": "pass"
},
"london-exit": {
"server": "http://london.proxy.example.com:8080"
}
}
Then use profiles by name in API requests or CLI commands.
| Variable | Default | Description |
|---|---|---|
CAMOFOX_PORT | 9377 | Server port |
PORT | (optional) | Alternative port env var (common in PaaS) |
NODE_ENV | development | Node environment |
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 unless CAMOFOX_AUTH_MODE=disabled. |
CAMOFOX_ADMIN_KEY | (empty) | Required for POST /stop (sent via x-admin-key) |
CAMOFOX_AUTH_MODE | auto | API-key policy: auto allows loopback without auth and requires CAMOFOX_API_KEY for non-loopback binds; required requires CAMOFOX_API_KEY for every bind; disabled disables API-key auth for protected endpoints and is only for trusted private agent networks. |
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 in CAMOFOX_AUTH_MODE=required, and required by auto whenever CAMOFOX_HOST exposes the server beyond loopback. Must be unset when CAMOFOX_AUTH_MODE=disabled. |
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. |
CAMOFOX_HEADLESS | true | Display mode: true (headless), false (headed), virtual (Xvfb) |
CAMOFOX_VNC_RESOLUTION | 1920x1080x24 | Virtual Xvfb display resolution (WIDTHxHEIGHTxDEPTH) |
CAMOFOX_VNC_TIMEOUT_MS | 120000 | Max VNC session duration in ms before auto-stop |
CAMOFOX_EVAL_EXTENDED_RATE_LIMIT_MAX | 20 | Max evaluate-extended requests per user per window |
CAMOFOX_EVAL_EXTENDED_RATE_LIMIT_WINDOW_MS | 60000 | Rate limit window duration in ms |
CAMOFOX_COOKIES_DIR | ~/.camofox/cookies | Directory used by the OpenClaw plugin cookie tool |
CAMOFOX_PROFILES_DIR | ~/.camofox/profiles | Profile storage directory (persistent per-user Firefox profiles) |
CAMOFOX_DOWNLOADS_DIR | ~/.camofox/downloads | Download artifact directory |
CAMOFOX_DOWNLOAD_TTL_MS | 86400000 | Download metadata retention TTL |
CAMOFOX_MAX_DOWNLOAD_SIZE_MB | 100 | Max single download size |
CAMOFOX_MAX_BATCH_CONCURRENCY | 5 | Batch download concurrency cap |
CAMOFOX_MAX_BLOB_SIZE_MB | 5 | Max blob payload size |
CAMOFOX_MAX_DOWNLOADS_PER_USER | 500 | Per-user download record cap |
CAMOFOX_CONSOLE_BUFFER_SIZE | 1000 | Per-tab console/error message buffer size (minimum 100) |
HANDLER_TIMEOUT_MS | 30000 | Handler timeout fallback |
MAX_CONCURRENT_PER_USER | 3 | Concurrent operations per user |
CAMOFOX_VNC_BASE_PORT | 6080 | noVNC/websockify base port |
CAMOFOX_VNC_HOST | localhost | noVNC host in returned URL |
CAMOFOX_CLI_USER | cli-default | Default CLI user id |
CAMOFOX_SERVER_PID_FILE | (unset) | Optional daemon PID file path used by the CLI server manager |
CAMOFOX_IDLE_TIMEOUT_MS | 1800000 | Stage 1 idle cleanup threshold (ms) |
CAMOFOX_IDLE_EXIT_TIMEOUT_MS | 1800000 | Stage 2 daemon exit quiet window (ms, defaults to match Stage 1) |
CAMOFOX_PRESETS_FILE | (unset) | Optional JSON file defining/overriding geo presets |
CAMOFOX_PROXY_PROFILES_FILE | (unset) | Optional JSON file defining named proxy profiles for session-level overrides |
CAMOFOX_SESSION_TIMEOUT | 1800000 | Session idle timeout in ms (min 60000) |
CAMOFOX_MAX_SESSIONS | 50 | Maximum concurrent sessions |
CAMOFOX_MAX_TABS | 10 | Maximum tabs per session |
PROXY_HOST | (empty) | Proxy host (server-level default; enables proxy routing) |
PROXY_PORT | (empty) | Proxy port (server-level default) |
PROXY_USERNAME | (empty) | Proxy username (server-level default) |
PROXY_PASSWORD | (empty) | Proxy password (server-level default) |
CAMOFOX_MAX_SNAPSHOT_CHARS | 80000 | Max characters in snapshot before truncation |
CAMOFOX_MAX_SNAPSHOT_NODES | 2000 | Max accessibility snapshot nodes before truncation |
CAMOFOX_SNAPSHOT_TAIL_CHARS | 5000 | Characters preserved at end of truncated snapshot |
CAMOFOX_BUILDREFS_TIMEOUT_MS | 12000 | Timeout for building element refs |
CAMOFOX_TAB_LOCK_TIMEOUT_MS | 30000 | Timeout for acquiring tab lock |
CAMOFOX_TRACES_DIR | ~/.camofox/traces | Managed Playwright trace artifact directory |
CAMOFOX_TRACE_MAX_DURATION_MS | 300000 | Maximum trace recording duration before automatic stop |
CAMOFOX_HEALTH_PROBE_INTERVAL_MS | 60000 | Health probe check interval |
CAMOFOX_FAILURE_THRESHOLD | 3 | Consecutive failures before health degradation |
CAMOFOX_YT_DLP_TIMEOUT_MS | 30000 | Timeout for yt-dlp subtitle extraction |
CAMOFOX_YT_BROWSER_TIMEOUT_MS | 25000 | Timeout for browser transcript fallback |
CAMOFOX_OS | (unset) | Optional server-wide Camoufox OS override (windows, macos, linux, or comma-separated list for randomization) |
CAMOFOX_ALLOW_WEBGL | (unset) | Optional server-wide WebGL override; malformed values fail startup |
CAMOFOX_SCREEN_WIDTH | (unset) | Optional screen width override; applied only with CAMOFOX_SCREEN_HEIGHT |
CAMOFOX_SCREEN_HEIGHT | (unset) | Optional screen height override; applied only with CAMOFOX_SCREEN_WIDTH |
CAMOFOX_HUMANIZE | (unset) | Optional server-wide humanization override |
CAMOFOX_OSandCAMOFOX_SCREEN_*are generation-time controls: they affect only newly generated fingerprints and have no effect while an existingfingerprint.jsonsidecar is in use. Reset the profile or deletefingerprint.jsonto force regeneration under the new defaults.CAMOFOX_ALLOW_WEBGLandCAMOFOX_HUMANIZEare launch-time overrides and apply on every browser launch regardless of whether a sidecar is reused.
CamoFox implements a two-stage idle lifecycle for graceful cleanup and daemon exit:
Stage 1 — Idle Cleanup
CAMOFOX_IDLE_TIMEOUT_MS of idle time (default: 30 minutes), the server runs cleanup to close idle sessions and tabsStage 2 — Daemon Exit
CAMOFOX_IDLE_EXIT_TIMEOUT_MS (default: matches Stage 1 timeout)Activity detection:
This two-stage model ensures cleanup runs before daemon exit, preventing resource leaks while allowing the server to shut down cleanly when fully idle.
docker build -t camofox-browser .
docker run -p 9377:9377 -p 6080:6080 \
-v ~/.camofox:/home/node/.camofox \
-e CAMOFOX_HOST=0.0.0.0 \
-e CAMOFOX_PORT=9377 \
-e CAMOFOX_AUTH_MODE=auto \
-e CAMOFOX_API_KEY=change-me \
camofox-browser
For private Docker networks used by agents such as Hermes, OpenClaw, or GoClaw that cannot send a bearer token, keep the service off published host ports and disable API-key auth explicitly:
docker network create camofox-agents
docker run -d \
--name camofox-browser \
--network camofox-agents \
-e CAMOFOX_HOST=0.0.0.0 \
-e CAMOFOX_AUTH_MODE=disabled \
-v ~/.camofox:/home/node/.camofox \
camofox-browser
Then configure the agent with CAMOFOX_URL=http://camofox-browser:9377 from another container on the same Docker network. Do not combine CAMOFOX_AUTH_MODE=disabled with public port publishing or CAMOFOX_ALLOW_PRIVATE_NETWORK=true.
This repo includes a starter fly.toml for one-command deploys.
fly launch
fly deploy
CAMOFOX_HOST=0.0.0.0CAMOFOX_API_KEY to a strong secretCAMOFOX_PORT=9377 (Railway will also provide PORT, which is supported)93779377CAMOFOX_HOST=0.0.0.0CAMOFOX_API_KEY to a strong secretCAMOFOX_PORT=9377 (or rely on Render PORT)| Project | Description |
|---|---|
| CamoFox MCP | MCP (Model Context Protocol) server for Claude, Cursor, VS Code |
| OpenClaw | Open-source AI agent framework (compat endpoints included) |
| Camoufox | Anti-detection Firefox browser engine |
src/
├── cli/
│ ├── commands/ # Command modules (core, navigation, interaction, etc.)
│ │ ├── console.ts # Console capture commands
│ │ └── trace.ts # Playwright tracing commands
│ ├── vault/ # Auth vault (encryption, storage)
│ ├── server/ # Server lifecycle management
│ ├── transport/ # HTTP transport layer
│ ├── output/ # Output formatting
│ └── utils/ # Shared utilities
├── server.ts # Express app entry point
├── types.ts # Shared TypeScript interfaces
├── routes/
│ ├── core.ts # Core REST API (~42 endpoints)
│ └── openclaw.ts # OpenClaw compatibility (~7 endpoints)
├── services/
│ ├── browser.ts # Browser lifecycle + persistent context pool
│ ├── batch-downloader.ts # Batch download orchestrator
│ ├── context-pool.ts # Browser context pool with LRU eviction
│ ├── download.ts # Download tracking service
│ ├── health.ts # Browser health tracking
│ ├── resource-extractor.ts # Page resource extraction
│ ├── session.ts # Session management + limits
│ ├── tab.ts # Tab operations (snapshot/click/type/etc.)
│ ├── tracing.ts # Playwright tracing service
│ ├── vnc.ts # VNC/virtual display lifecycle
│ └── youtube.ts # YouTube transcript extraction
├── middleware/
│ ├── auth.ts # API/admin auth helpers
│ ├── errors.ts # Error handling
│ ├── logging.ts # Structured logging
│ └── rate-limit.ts # In-memory rate limiter
└── utils/
├── config.ts # Environment config parsing
├── cookies.ts # Cookie utilities
├── download-helpers.ts # Download helper functions
├── launcher.ts # Browser launcher utilities
├── macros.ts # Search macro expansion
├── presets.ts # Geo preset definitions/loader
└── snapshot.ts # Snapshot truncation/windowing
See CONTRIBUTING.md
This project is based on camofox-browser by Jo Inc (YC W24) and the Camoufox anti-detection browser engine by daijro.
Sketchy 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.
FAQs
Anti-detection browser server for AI agents — REST API wrapping Camoufox engine with OpenClaw plugin support
The npm package camofox-browser receives a total of 527 weekly downloads. As such, camofox-browser popularity was classified as not popular.
We found that camofox-browser demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
/Research
A fake corepack.org site is impersonating the Node.js tool and delivers an infostealer and proxyware to developers who download it.

Research
/Security News
A large-scale campaign abused GitHub Actions in compromised repositories to exploit CVE-2026-41940 in cPanel and WHM and steal server credentials.

Security News
Five frontier LLMs generated the same nonexistent package names, leaving 53 available for potential slopsquatting across PyPI and npm.