An open API service indexing awesome lists of open source software.

https://github.com/redf0x1/camofox-browser

Anti-detection browser server for AI agents — REST API wrapping Camoufox engine with OpenClaw plugin support
https://github.com/redf0x1/camofox-browser

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

Last synced: 29 days ago
JSON representation

Anti-detection browser server for AI agents — REST API wrapping Camoufox engine with OpenClaw plugin support

Awesome Lists containing this project

README

          

# CamoFox Browser Server

> Anti-detection browser server for AI agents — TypeScript REST API wrapping the [Camoufox](https://github.com/daijro/camoufox) stealth browser engine

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](tsconfig.json)
[![Node](https://img.shields.io/badge/Node-20%2B-green)](package.json)
[![npm](https://img.shields.io/npm/v/camofox-browser)](https://www.npmjs.com/package/camofox-browser)

## Table of Contents

- [Why CamoFox?](#why-camofox)
- [Features](#features)
- [Quick Start](#quick-start)
- [CLI](#cli)
- [Console Capture](#console-capture)
- [Playwright Tracing](#playwright-tracing)
- [Security](#security)
- [Usage with AI Agents](#usage-with-ai-agents)
- [Architecture](#architecture)
- [API Reference](#api-reference)
- [Search Macros](#search-macros)
- [Geo Presets](#geo-presets)
- [Environment Variables](#environment-variables)
- [Deployment](#deployment)
- [Used With](#used-with)
- [Project Structure](#project-structure)
- [Contributing](#contributing)
- [Credits](#credits)
- [License](#license)

## Why CamoFox?

**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](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.

| 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 |

## Features

- **C++ Anti-Detection** — fingerprint spoofing at the Camoufox engine level (not JS injection)
- **REST API** — language-agnostic HTTP endpoints for browser automation and AI agent integration
- **Multi-Session** — concurrent isolated browser contexts per `userId` (defaults: max 50 sessions, max 10 tabs/session)
- **Persistent Browser Profiles** — Each user gets a dedicated Firefox profile. Cookies, localStorage, IndexedDB, and all browser storage persist across sessions automatically.
- **Geo Presets** — 8 built-in region presets (locale/timezone/geolocation) + custom presets file
- **14 Search Macros** — Google, YouTube, Amazon, Reddit (search + subreddit JSON), Wikipedia, Twitter, Yelp, Spotify, Netflix, LinkedIn, Instagram, TikTok, Twitch
- **Element Refs** — accessibility snapshots annotated with stable `eN` element references for precise interaction
- **Cookie Persistence** — import Netscape/Playwright-style cookies into a session (optional, gated by API key)
- **OpenClaw Plugin** — OpenClaw-compatible endpoints (`/start`, `/tabs/open`, `/act`, etc.)
- **TypeScript** — strict mode, typed request shapes, modular Express routes
- **YouTube Transcript Extraction** — yt-dlp primary pipeline with browser fallback
- **Snapshot Pagination** — offset-based windowing for large page snapshots
- **Browser Health Monitoring** — health probe with recovery/degraded state tracking
- 🖥️ **CLI Mode** — 50+ commands for terminal-based browser automation
- 🔐 **Auth Vault** — AES-256-GCM encrypted credential storage (LLM-safe)
- 📜 **Pipeline Scripting** — Execute command scripts from files
- 🔍 **Console Capture** — capture and filter browser console messages and uncaught errors
- 📼 **Playwright Tracing** — record and export Playwright traces for debugging

## Quick Start

### From Source

```bash
git clone https://github.com/redf0x1/camofox-browser.git
cd camofox-browser
npm install
npm run build
npm start
```

### Using npm (CLI)

```bash
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](#cli) for the complete command reference.

### Using Docker

> Docker image: `ghcr.io/redf0x1/camofox-browser`

```bash
docker build -t camofox-browser .
docker run -d \
--name camofox-browser \
-p 9377:9377 \
-p 6080:6080 \
-v ~/.camofox:/home/node/.camofox \
camofox-browser
```

To persist browser profiles (cookies, localStorage, IndexedDB, etc.) across container restarts, keep the volume mount shown above.

### Using Docker Compose

```yaml
services:
camofox-browser:
build: .
ports:
- "9377:9377"
environment:
CAMOFOX_PORT: "9377"
# Optional auth gates
# 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: ""
```

### Verify

```bash
curl http://localhost:9377/health
# {"ok":true,"engine":"camoufox","browserConnected":true}
```

## CLI

CamoFox Browser includes a powerful CLI for browser automation directly from the terminal. The CLI auto-starts the server when needed.

### Installation

```bash
# Global install (recommended)
npm install -g camofox-browser

# Or use npx (no install needed)
npx camofox open https://example.com
```

### Quick Start

```bash
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
```

### Core Commands

```bash
# Browser lifecycle
camofox open # Open URL in new tab
camofox close [tabId] # Close tab
camofox navigate # 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

# Interaction
camofox click # Click element by ref
camofox type # Type text into element
camofox fill '[e1]="user" [e2]="pw"' # Fill multiple fields at once
camofox scroll # Scroll up/down/left/right
camofox select # Select dropdown option
camofox hover # Hover over element
camofox press # Press keyboard key
camofox drag # 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 [--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.

### Session Management

```bash
camofox session save # Save current browser state
camofox session load # Restore browser state
camofox session list # List saved sessions
camofox session delete # Delete saved session
```

### Cookie Management

```bash
camofox cookie export # Export cookies to JSON file
camofox cookie import # Import cookies from JSON file
```

### Auth Vault

Securely store credentials locally with AES-256-GCM encryption. Credentials are **never** output to stdout — safe for LLM agent automation.

```bash
camofox auth save [--url URL] # Save credentials (prompts for master password)
camofox auth load # Show profile info (username only)
camofox auth list # List saved profiles (no secrets shown)
camofox auth delete # Delete a profile
camofox auth change-password # 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 `--inject` flag sends credentials directly to the browser — the LLM agent never sees the password.

### Pipeline Scripting

Execute multiple commands from a file for automation workflows:

```bash
# 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 -
```

### Server Management

```bash
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
```

### Diagnostics

```bash
camofox health # System health report
camofox version # CLI + server version
camofox info # Configuration info
```

### Console Capture

```bash
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
```

### Playwright Tracing

```bash
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](https://trace.playwright.dev)

### Global Options

| Flag | Env Var | Description | Default |
|------|---------|-------------|---------|
| `--user ` | `CAMOFOX_USER` | User/profile ID | `cli-default` |
| `--port ` | `PORT` | Server port | `9377` |
| `--format ` | — | Output: `json`, `text`, `plain` | `text` |
| `-V, --version` | — | Show version | — |
| `-h, --help` | — | Show help | — |

### Output Formats

```bash
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 json` for programmatic parsing and LLM agent integration.

## Security

### Anti-Detection
CamoFox 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.

### Auth Vault
- **AES-256-GCM** encryption with **Argon2id** key derivation (PBKDF2 fallback)
- Credentials **never** appear in stdout (safe for LLM agent pipelines)
- Vault files stored with `0600` permissions
- Master password required for all vault operations

### LLM Agent Safety
- The `--inject` flag sends credentials directly to the browser — the LLM agent orchestrating the CLI never sees raw passwords
- Output formats are designed for safe parsing without credential exposure
- Pipeline scripts can reference auth profiles without embedding secrets

## Usage with AI Agents

CamoFox works seamlessly with AI coding agents and LLM-powered automation:

### AI Coding Assistants (Recommended)

Add CamoFox skills to your AI coding assistant for full browser automation context:

```bash
npx skills add redf0x1/camofox-browser
```

This 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).

**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.

#### Claude Code

```bash
npx skills add redf0x1/camofox-browser
# Installs to .claude/skills/camofox-browser/SKILL.md
```

#### Cursor / GitHub Copilot / Codex

```bash
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.md` files.

### MCP Integration (Recommended)
Use [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).

### CLI Integration
AI agents can use the CLI with `--format json` for structured output:

```bash
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
```

### Pipeline Automation
Create reusable automation scripts that AI agents can execute:

```bash
camofox run automation-flow.txt # Execute multi-step workflow
```

## Architecture

```text
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 │
└──────────────────────────────────────────┘
```

### Persistent Profiles (v1.3.0)

- Each `userId` runs in its own persistent Firefox process/context (backed by `launchPersistentContext(userDataDir)`)
- Profile data is stored at `~/.camofox/profiles/{userId}/` (override via `CAMOFOX_PROFILES_DIR`)
- Idle user contexts are closed via LRU eviction (profile data remains on disk)

## API Reference

Base URL: `http://localhost:9377`

### Core Endpoints

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[] }` | `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`) | None |
| 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`) | None |
| 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` | None |
| POST | `/tabs/:tabId/click` | Click by `ref` (e.g. `e12`) or CSS `selector` | Body: `userId` + (`ref` or `selector`) | None |
| POST | `/tabs/:tabId/type` | Type into an element by `ref` or CSS `selector` | Body: `userId` + (`ref` or `selector`) + `text` | None |
| POST | `/tabs/:tabId/press` | Press a key (e.g. `Enter`, `Escape`) | Body: `userId` + `key` | None |
| POST | `/tabs/:tabId/scroll` | Scroll up/down by pixels | Body: `userId` | None |
| POST | `/tabs/:tabId/scroll-element` | Scroll specific element into view | Body: userId, ref/selector | None |
| POST | `/tabs/:tabId/back` | Go back | Body: `userId` | None |
| POST | `/tabs/:tabId/forward` | Go forward | Body: `userId` | None |
| POST | `/tabs/:tabId/refresh` | Refresh | Body: `userId` | None |
| GET | `/tabs/:tabId/links?userId=...&limit=50&offset=0` | Extract links (paginated) | Query: `userId` | None |
| 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` | None |
| DELETE | `/tabs/group/:listItemId` | Close a tab group (expects JSON body: `{ "userId": "..." }`) | Body: `userId` | None |
| DELETE | `/sessions/:userId` | Close all sessions for a user | Path: `userId` | None |
| 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` | None |
| POST | `/tabs/:tabId/extract-resources` | Extract downloadable resources | Body: `userId` | None |
| POST | `/tabs/:tabId/batch-download` | Batch download resources | Body: `userId` | None |
| 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 | `/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` |

### Toggle Display Mode
```bash
POST /sessions/:userId/toggle-display
{"headless": "virtual"}
```
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:
```json
{"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.

### Browser Viewer (noVNC)
When the display mode is set to `"virtual"` or `false`, the server automatically starts a VNC viewer accessible via web browser.

```bash
# 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`).

### Evaluate JavaScript
Execute a JavaScript expression in the page context and return the JSON-serializable result.

Auth: required only when `CAMOFOX_API_KEY` is set on the server; otherwise no auth is required.

Note: async expressions must be wrapped in an async IIFE (for example, `(async () => { ... })()`). Top-level `await` is not supported.

```bash
POST /tabs/:tabId/evaluate
{"userId": "agent1", "expression": "document.title"}
```
Returns: `{"ok": true, "result": "Page Title", "resultType": "string", "truncated": false}`

### Evaluate JavaScript (Extended)
Execute a long-running JavaScript expression (up to 300s timeout). Conditionally API-key protected. Rate limited.

Auth: required only when `CAMOFOX_API_KEY` is set on the server; otherwise no auth is required.

Note: async expressions must be wrapped in an async IIFE (for example, `(async () => { ... })()`). Top-level `await` is not supported.

```bash
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 Endpoints

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` | None |
| 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` | None |
| GET | `/snapshot?userId=...&targetId=...` | Snapshot (OpenClaw response shape) | Query: `userId` + `targetId` | None |
| POST | `/act` | Combined actions (`click`, `type`, `press`, `scroll`, `scrollIntoView`, `hover`, `wait`, `close`) | Body: `userId` + `targetId` + `kind` | None |

## Search Macros

Use macros via `POST /tabs/:tabId/navigate` with `{ "macro": "@google_search", "query": "..." }`.

| Macro | Engine |
|-------|--------|
| `@google_search` | Google |
| `@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` | LinkedIn |
| `@instagram_search` | Instagram tags |
| `@tiktok_search` | TikTok |
| `@twitch_search` | Twitch |

## Geo Presets

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:

```bash
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).

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `CAMOFOX_PORT` | `9377` | Server port |
| `PORT` | (optional) | Alternative port env var (common in PaaS) |
| `NODE_ENV` | `development` | Node environment |
| `CAMOFOX_ADMIN_KEY` | (empty) | Required for `POST /stop` (sent via `x-admin-key`) |
| `CAMOFOX_API_KEY` | (empty) | Enables cookie import endpoint; sent via `Authorization: Bearer ...` |
| `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 |
| `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_IDLE_TIMEOUT_MS` | `1800000` | CLI server idle timeout |
| `CAMOFOX_PRESETS_FILE` | (unset) | Optional JSON file defining/overriding geo presets |
| `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 (enables proxy routing) |
| `PROXY_PORT` | (empty) | Proxy port |
| `PROXY_USERNAME` | (empty) | Proxy username |
| `PROXY_PASSWORD` | (empty) | Proxy password |
| `CAMOFOX_MAX_SNAPSHOT_CHARS` | `80000` | Max characters in snapshot 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_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 |

## Deployment

### Docker (Recommended)

```bash
docker build -t camofox-browser .
docker run -p 9377:9377 -p 6080:6080 \
-v ~/.camofox:/home/node/.camofox \
-e CAMOFOX_PORT=9377 \
camofox-browser
```

### Fly.io

This repo includes a starter `fly.toml` for one-command deploys.

```bash
fly launch
fly deploy
```

### Railway

- Create a new project → deploy from this GitHub repo
- Set `CAMOFOX_PORT=9377` (Railway will also provide `PORT`, which is supported)
- Ensure the service exposes port `9377`

### Render

- Create a new Web Service → deploy from this GitHub repo
- Use Docker (recommended) and expose port `9377`
- Set `CAMOFOX_PORT=9377` (or rely on Render `PORT`)

### System Requirements

- Node.js 20+
- 2GB+ RAM (browser + contexts require significant memory)
- Linux recommended for production; macOS is fine for development

## Used With

| Project | Description |
|---------|-------------|
| [CamoFox MCP](https://github.com/redf0x1/camofox-mcp) | MCP (Model Context Protocol) server for Claude, Cursor, VS Code |
| [OpenClaw](https://openclaw.ai) | Open-source AI agent framework (compat endpoints included) |
| [Camoufox](https://github.com/daijro/camoufox) | Anti-detection Firefox browser engine |

## Project Structure

```text
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
```

## Contributing

See [CONTRIBUTING.md](CONTRIBUTING.md)

## Credits

This 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).

- [Camoufox](https://camoufox.com) - Firefox-based browser with C++ anti-detection
- [Donate to Camoufox's original creator daijro](https://camoufox.com/about/)
- [OpenClaw](https://openclaw.ai) - Open-source AI agent framework

## License

[MIT](LICENSE)

## Crypto Scam Warning

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.