Install
openclaw skills install camofox-cloaked-browserUse Camofox/Camoufox as an opt-in anti-detection browser server for agent workflows that need cloaked browsing. Covers npm/npx startup, OpenClaw plugin tools, REST API commands, session/tab workflow, environment variables, process-scoped CAMOFOX_URL for Hermes, and hard rules such as always sending userId and re-snapshotting after state-changing actions. Do not use for normal web search, text extraction, curl fetches, or ordinary browser automation.
openclaw skills install camofox-cloaked-browserUse this skill only when the task needs Camofox/Camoufox specifically:
camofox-browser plugin/tools availableDo not use it for ordinary web search, simple page fetches, static text extraction, or normal browser automation. Use the cheaper default stack unless cloaking is actually load-bearing.
If the user names another browser target explicitly — Browserbase, Selkies, a tailnet browser, normal Hermes browser, Chrome DevTools, etc. — stop and use that target/skill instead. Do not silently route through Camofox.
Default local server:
http://127.0.0.1:9377
Equivalent localhost URL is usually fine:
http://localhost:9377
Prefer 127.0.0.1 in examples to avoid IPv6/localhost oddities.
There is no container target in this skill. Do not mention or rely on container names; this skill is about the npm server and OpenClaw plugin/API.
Camofox Browser is a Node server and OpenClaw plugin wrapper around Camoufox, a Firefox-based anti-detection browser.
Primary local startup:
npx -y @askjo/camofox-browser
# serves http://127.0.0.1:9377 by default
Alternative cloned-repo startup:
git clone https://github.com/jo-inc/camofox-browser
cd camofox-browser
npm install
npm start
Alternative global install:
npm install -g @askjo/camofox-browser
camofox-browser
npm install / npx downloads the Camoufox browser binary on first run via the package postinstall unless CAMOUFOX_EXECUTABLE points to an existing compatible Camoufox bundle. Expect roughly a few hundred MB for the browser payload.
If OpenClaw has the upstream plugin installed, prefer the plugin tools over raw curl because they auto-manage userId from ctx.agentId, use sessionKey, and can auto-start the server.
Install shape:
openclaw plugins install @askjo/camofox-browser
# or whatever ClawHub install command the registry page currently shows
Useful OpenClaw CLI commands from the plugin:
openclaw camofox status
openclaw camofox start
openclaw camofox stop
openclaw camofox tabs
openclaw camofox configure
Plugin config shape shown by upstream:
plugins:
entries:
camofox-browser:
enabled: true
config:
port: 9377
autoStart: true
maxSessions: 5
maxTabsPerSession: 3
sessionTimeoutMs: 600000
browserIdleTimeoutMs: 300000
maxOldSpaceSize: 128
The upstream plugin exposes these core tools:
camofox_create_tab — create tab; returns tabIdcamofox_snapshot — accessibility snapshot with refs and screenshot; primary observation toolcamofox_click — click by ref or CSS selectorcamofox_type — type by ref or selector; optional pressEntercamofox_navigate — navigate by URL or search macrocamofox_scroll — scroll pagecamofox_screenshot — screenshot onlycamofox_close_tab — close a tabcamofox_evaluate — execute JS; gated by server auth middlewarecamofox_list_tabs — list tabs for the current usercamofox_import_cookies — import Netscape cookies; use CAMOFOX_API_KEY for this sensitive endpointAlways follow these rules when using Camofox:
/health before doing browser work.userId in raw REST calls.sessionKey when creating tabs so task tabs group together.Set base variables:
BASE="${CAMOFOX_BASE_URL:-http://127.0.0.1:9377}"
USER_ID="${CAMOFOX_USER_ID:-agent1}"
SESSION_KEY="${CAMOFOX_SESSION_KEY:-task1}"
If CAMOFOX_ACCESS_KEY or CAMOFOX_API_KEY is set, include auth where required:
AUTH_HEADER=()
if [ -n "${CAMOFOX_ACCESS_KEY:-}" ]; then
AUTH_HEADER=(-H "Authorization: Bearer ${CAMOFOX_ACCESS_KEY}")
elif [ -n "${CAMOFOX_API_KEY:-}" ]; then
AUTH_HEADER=(-H "Authorization: Bearer ${CAMOFOX_API_KEY}")
fi
curl -fsS "$BASE/health"
TAB_ID="$(curl -fsS -X POST "$BASE/tabs" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"sessionKey\":\"$SESSION_KEY\",\"url\":\"https://example.com\"}" \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["tabId"])')"
printf 'TAB_ID=%s\n' "$TAB_ID"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/navigate" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"url\":\"https://example.com\"}"
Search macro example:
curl -fsS -X POST "$BASE/tabs/$TAB_ID/navigate" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"macro\":\"@google_search\",\"query\":\"site:example.com pricing\"}"
Known macros include:
@google_search@youtube_search@amazon_search@reddit_search@wikipedia_search@twitter_search@yelp_search@spotify_search@netflix_search@linkedin_search@instagram_search@tiktok_search@twitch_searchcurl -fsS "$BASE/tabs/$TAB_ID/snapshot?userId=$USER_ID"
With screenshot and pagination offset:
curl -fsS "$BASE/tabs/$TAB_ID/snapshot?userId=$USER_ID&includeScreenshot=true&offset=0"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/click" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"ref\":\"e1\"}"
Selector fallback:
curl -fsS -X POST "$BASE/tabs/$TAB_ID/click" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"selector\":\"button[type=submit]\"}"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/type" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"ref\":\"e2\",\"text\":\"hello world\"}"
Type and submit:
curl -fsS -X POST "$BASE/tabs/$TAB_ID/type" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"ref\":\"e2\",\"text\":\"query\",\"pressEnter\":true}"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/press" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"key\":\"Enter\"}"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/wait" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"timeout\":3000}"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/scroll" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"direction\":\"down\",\"amount\":700}"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/back" -H 'Content-Type: application/json' "${AUTH_HEADER[@]}" -d "{\"userId\":\"$USER_ID\"}"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/forward" -H 'Content-Type: application/json' "${AUTH_HEADER[@]}" -d "{\"userId\":\"$USER_ID\"}"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/refresh" -H 'Content-Type: application/json' "${AUTH_HEADER[@]}" -d "{\"userId\":\"$USER_ID\"}"
curl -fsS "$BASE/tabs/$TAB_ID/links?userId=$USER_ID&limit=50"
curl -fsS "$BASE/tabs/$TAB_ID/images?userId=$USER_ID&limit=50"
curl -fsS "$BASE/tabs/$TAB_ID/screenshot?userId=$USER_ID" --output screenshot.png
This endpoint is auth-gated by the server middleware. Use only when needed.
curl -fsS -X POST "$BASE/tabs/$TAB_ID/evaluate" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"expression\":\"document.title\"}"
curl -fsS -X POST "$BASE/tabs/$TAB_ID/extract" \
-H 'Content-Type: application/json' \
"${AUTH_HEADER[@]}" \
-d "{\"userId\":\"$USER_ID\",\"schema\":{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\"}}}}"
curl -fsS "$BASE/tabs?userId=$USER_ID"
curl -fsS -X DELETE "$BASE/tabs/$TAB_ID?userId=$USER_ID" "${AUTH_HEADER[@]}"
This endpoint is auth-gated.
curl -fsS -X DELETE "$BASE/sessions/$USER_ID" "${AUTH_HEADER[@]}"
CAMOFOX_PORT: server port; default 9377PORT: generic port fallback if CAMOFOX_PORT is unsetCAMOFOX_BASE_URL: not an upstream server env var; useful shell convention for scripts in this skillCAMOFOX_URL: Hermes-specific browser-backend switch; do not set/export globally; see belowCAMOFOX_ACCESS_KEY: global bearer token for all routes except /health; use when anything can reach the server beyond loopbackCAMOFOX_API_KEY: optional bearer token used only for sensitive endpoints such as cookie import/traces; not needed for normal browsing, snapshots, navigation, clicks, or typingCAMOFOX_ADMIN_KEY: protects /stop when configuredFor local development with neither key set, upstream allows loopback requests in non-production mode. Do not expose that to a network. That would be the kind of convenience feature that becomes an incident report.
CAMOFOX_COOKIES_DIR: cookie import source directory; default ~/.camofox/cookiesCAMOFOX_PROFILE_DIR: profile directory; default ~/.camofox/profilesCAMOFOX_TRACES_DIR: trace directory; default ~/.camofox/tracesCAMOFOX_TRACES_MAX_BYTES: default 50MBCAMOFOX_TRACES_TTL_HOURS: default 24MAX_CONCURRENT_PER_USER: default 3MAX_SESSIONS: default 50MAX_TABS_PER_SESSION: default 10MAX_TABS_GLOBAL: default 50SESSION_TIMEOUT_MS: default 600000 in current codeTAB_INACTIVITY_MS: default 300000BROWSER_IDLE_TIMEOUT_MS: default 300000NAVIGATE_TIMEOUT_MS: default 25000BUILDREFS_TIMEOUT_MS: default 12000NATIVE_MEM_RESTART_THRESHOLD_MB: default 300BROWSER_RSS_RESTART_THRESHOLD_MB: default 1500CAMOUFOX_EXECUTABLE: external Camoufox executable/bundle; skips bundled download when validCAMOUFOX_EXECUTABLE_PATH: compatibility aliasCAMOFOX_EXECUTABLE_PATH: legacy aliasCAMOFOX_SKIP_DOWNLOAD=1|true: skip postinstall browser download; only use if an executable is provided another wayPROXY_STRATEGYPROXY_PROVIDERPROXY_HOSTPROXY_PORTPROXY_PORTSPROXY_USERNAMEPROXY_PASSWORDPROXY_BACKCONNECT_HOSTPROXY_BACKCONNECT_PORTPROXY_COUNTRYPROXY_STATEPROXY_CITYPROXY_ZIPPROXY_SESSION_DURATION_MINUTESUpstream crash/hang telemetry is enabled unless disabled:
CAMOFOX_CRASH_REPORT_ENABLED=false
Use that for privacy-conservative local runs unless the user wants upstream crash reporting.
CAMOFOX_URL footgunThis section is load-bearing for Hermes agents. Read it before setting environment variables.
In Hermes, CAMOFOX_URL is not just a harmless client hint. It is a browser-backend switch. If CAMOFOX_URL is visible in the Hermes process environment, Hermes considers Camofox mode enabled for that process and normal Hermes browser calls can be forced through Camofox.
Hard rules for Hermes:
CAMOFOX_URL to ~/.hermes/.env.CAMOFOX_URL in shell profiles such as ~/.zshrc, ~/.bashrc, launchd plists, Docker env, or service env.CAMOFOX_URL in the Hermes gateway environment unless the explicit intent is for the entire gateway process to use Camofox for browser calls.hermes config set ... CAMOFOX_URL ...; this is runtime state, not durable config.CAMOFOX_URL only as an inline, one-process env var for a dedicated cloaked Hermes run:CAMOFOX_URL="http://127.0.0.1:9377" hermes chat -q 'Use cloaked Camofox browsing for this task: <task>'
CAMOFOX_URL, restart the affected Hermes CLI/gateway process; running processes keep their old environment.Safer default for Hermes agents: run the Camofox server normally, store the stable server URL as skills.config.camofox.base_url or CAMOFOX_BASE_URL for REST examples, and do not set CAMOFOX_URL at all unless launching a dedicated cloaked Hermes process.
This routing claim is Hermes-specific. For OpenClaw, use the upstream plugin config/tools unless you have verified equivalent env semantics.
Before claiming success:
curl -fsS "${CAMOFOX_BASE_URL:-http://127.0.0.1:9377}/health"
openclaw camofox status
Raw REST mode: create a tab, snapshot it, close it.
Hermes mode: verify CAMOFOX_URL is absent from global Hermes env and present only on the dedicated cloaked Hermes process, if Hermes process routing is intentionally being used.
Cleanup: close tabs or stop the managed server if the task started it only for one job.
userId in raw REST calls.CAMOFOX_URL globally in Hermes (~/.hermes/.env, gateway env, shell profile, service env) and accidentally routing all browser calls through Camofox.CAMOFOX_API_KEY is needed for normal browser work; it is only for sensitive endpoints such as cookie import/traces.@askjo/camofox-browser plus its REST API.Report:
CAMOFOX_ACCESS_KEY, or CAMOFOX_API_KEY for sensitive endpointsuserId, sessionKeytabId used/closed/preserved