| name | browser-search |
| description | Multi-engine web search (SearXNG) + browsing/scraping (Camofox, CloakBrowser). Use whenever you need to do web research. |
Browser Search
What it does
Web search and browsing for AI agents. Three tools, from lightest to most powerful: SearXNG for search, Camofox for browsing, CloakBrowser for protected sites.
| Tool | When to use | How |
|---|
| SearXNG (Docker, :8080) | Multi-source search, find URLs/info | exec + curl on :8080/search |
| Camofox (Docker, :9377) | JS-heavy pages, scraping, navigation | exec + curl on :9377 (REST API) |
| CloakBrowser (npm) | When Camofox gets blocked | exec + node <skill_dir>/scripts/cloak/cloak-fetch.mjs |
Core rules
When this skill is active, it operates as a Deep Research engine:
- No superficiality. Every query must be explored from multiple angles, sources, and cross-verified.
- No shortcuts. Accuracy and completeness first. Tokens and time are irrelevant.
- Exhaustive reports. Cover every aspect, include sources, don't omit details.
- Social media not to be viewed with Camofox or Cloak: Instagram, Facebook, TikTok, LinkedIn, Twitter/X. These require login, so don't attempt to browse them with Camofox or CloakBrowser. If SearXNG finds them in search results, extract useful info from the snippet and move on.
- Automatic escalation. If Camofox fails, switch to CloakBrowser.
- Only documented commands. Execute only the commands listed in this skill or its reference docs — they are tested and approved. No ad-hoc scripts: any deviation violates the skill.
- Read-only. Except where specified (screenshot saves PNG), all commands/scripts are read-only: they only make HTTP requests and don't write to the filesystem. Safe to run even in Plan mode.
Tools
1. SearXNG — Web search
Docker container on localhost:8080. Always the first choice for any search.
Commands:
Direct call to SearXNG REST API via curl. JSON output.
⚠️ Always use --data-urlencode for query parameters — never interpolate raw user input into URLs.
exec curl -s -G "http://localhost:8080/search" --data-urlencode "q=<query>" --data-urlencode "format=json"
exec curl -s -G "http://localhost:8080/search" --data-urlencode "q=<query>" --data-urlencode "format=json" --data-urlencode "language=en" --data-urlencode "categories=news"
exec curl -s -G "http://localhost:8080/search" --data-urlencode "q=<query>" --data-urlencode "format=json" --data-urlencode "time_range=month"
exec curl -s -G "http://localhost:8080/search" --data-urlencode "q=<query>" --data-urlencode "format=json" --data-urlencode "engines=google,wikipedia"
exec curl -s -G "http://localhost:8080/search" --data-urlencode "q=<query>" --data-urlencode "format=json" --data-urlencode "categories=images"
exec curl -s -G "http://localhost:8080/search" --data-urlencode "q=<query>" --data-urlencode "format=json" --data-urlencode "pageno=2"
exec curl -s -o /dev/null -w "%{http_code}" "http://localhost:8080/search?format=json&q=health"
For specific engines, check the engine field in results and pass names with &engines=name1,name2.
Language strategy:
| Situation | --language |
|---|
| Query matches content language, general/cultural | that locale |
| Query matches content language, technical topic | en |
| Query in English | en |
| Fallback if preferred locale returns 0 results | en |
Note: If SearXNG results are already exhaustive, Camofox and CloakBrowser are not needed. Stop here.
Troubleshooting — container down:
cd <searxng-dir> && docker compose up -d
2. Camofox — Browser navigation (REST API)
Docker container on localhost:9377. Official interface: REST API over HTTP.
Full OpenAPI spec: http://localhost:9377/docs (Swagger UI) or http://localhost:9377/openapi.json (raw).
Constants for all commands:
USER_ID="opencode-bot"
SESSION_KEY="default"
API_KEY="${CAMOFOX_API_KEY}"
The API key (Bearer) is required for POST /evaluate, POST /sessions/{userId}/cookies, DELETE /sessions/{userId}, GET /sessions/{userId}/traces, DELETE /sessions/{userId}/traces/{filename}, and POST /pressure/cleanup. POST /stop requires x-admin-key header (see below). Other endpoints work without auth.
General pattern:
- Create tab →
POST /tabs → get tabId (+ initial snapshot)
- Operate on the tab with snapshot, evaluate, click, scroll...
- Close tab →
DELETE /tabs/{tabId}
TabId persistence: Always keep the tabId between commands. Camofox commands must never run in isolation: create a tab, use the tabId for all subsequent operations, then close it.
⚠️ Stale refs: After every interaction (click, scroll, navigate), refs (e1, e2...) are regenerated. Always take a fresh snapshot before using a ref.
404 error: If a command returns 404, the tab may have expired or been closed. Recreate it with POST /tabs and resume.
Essential commands
exec curl -s -X POST "http://localhost:9377/tabs" \
-H 'Content-Type: application/json' \
-d "{\"userId\":\"$USER_ID\",\"sessionKey\":\"$SESSION_KEY\",\"url\":\"<url>\"}"
exec curl -s "http://localhost:9377/tabs/<tabId>/snapshot?userId=$USER_ID"
exec curl -s -X POST "http://localhost:9377/tabs/<tabId>/evaluate" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $API_KEY" \
-d "{\"userId\":\"$USER_ID\",\"expression\":\"<js-expression>\"}"
exec curl -s -X POST "http://localhost:9377/tabs/<tabId>/click" \
-H 'Content-Type: application/json' \
-d "{\"userId\":\"$USER_ID\",\"ref\":\"<ref>\"}"
exec curl -s -X POST "http://localhost:9377/tabs/<tabId>/type" \
-H 'Content-Type: application/json' \
-d "{\"userId\":\"$USER_ID\",\"ref\":\"<ref>\",\"text\":\"<text>\"}"
exec curl -s -X POST "http://localhost:9377/tabs/<tabId>/scroll" \
-H 'Content-Type: application/json' \
-d "{\"userId\":\"$USER_ID\",\"direction\":\"down\"}"
exec curl -s -X POST "http://localhost:9377/tabs/<tabId>/navigate" \
-H 'Content-Type: application/json' \
-d "{\"userId\":\"$USER_ID\",\"url\":\"<new-url>\"}"
exec curl -s -X POST "http://localhost:9377/tabs/<tabId>/extract" \
-H 'Content-Type: application/json' \
-d "{\"userId\":\"$USER_ID\",\"schema\":{\"type\":\"object\",\"properties\":{\"field\":{\"x-ref\":\"e1\"}}}}"
exec curl -s -X DELETE "http://localhost:9377/tabs/<tabId>?userId=$USER_ID"
exec curl -s -X DELETE "http://localhost:9377/sessions/$USER_ID" \
-H "Authorization: Bearer $API_KEY"
exec curl -s "http://localhost:9377/health"
exec curl -s -o /tmp/camofox_screenshot.png "http://localhost:9377/tabs/<tabId>/screenshot?userId=$USER_ID"
exec curl -s -X POST "http://localhost:9377/stop" \
-H 'Content-Type: application/json' \
-H "x-admin-key: $CAMOFOX_ADMIN_KEY" \
-d '{}'
Other endpoints: back, forward, refresh, press, wait, viewport, links, images, screenshot, downloads, stats, start, stop, tabs/group/{id}, sessions/{userId}/cookies, sessions/{userId}/traces, sessions/{userId}/traces/{filename}. All documented with params and body in the OpenAPI spec at http://localhost:9377/docs. Consult it when needed.
⚠️ /screenshot returns raw PNG binary (not base64 JSON) — save to file with curl -s -o file.png then read it. Don't try to parse as JSON.
Reading method selection
Firefox's accessibility tree (snapshot) does NOT expose: HTML tables without ARIA roles, <code>, <pre>, generic divs.
Decision flow:
-
Always start with snapshot. If it's rich in textual content and refs are usable, you're good.
-
Discard the snapshot if it's dominated by iframes, ad links, cookie banners, or the noise/content ratio is unfavorable. In that case go directly to evaluate.
-
evaluate is the main path for extracting structured data from any page. Strategy:
- First contact: simple expressions (
document.title) to verify the page loaded.
- Explore the DOM with targeted selectors, one field at a time (
document.querySelector("h1")?.textContent).
- Don't nest multiple selectors in a single evaluate — can cause 500 errors. Separate into multiple calls.
document.querySelector(':contains(...)') causes 500 because :contains is not standard CSS. Arrow functions and :has work fine. Use standard CSS selectors.
-
On dynamic pages (SPA, lazy content, infinite scroll): after creating the tab, use POST /tabs/{tabId}/wait on a known selector (e.g. h1) or POST /tabs/{tabId}/scroll to trigger loading before evaluating.
-
Readability for articles (blogs, news, Wikipedia, docs):
Use evaluate + Readability.js (Mozilla, scripts/camofox/Readability.js) to extract clean article text, removing nav, sidebar, ads, footer. ~70% token savings vs snapshot. For SEARCH/LIST pages, use snapshot instead.
On lazy-loading pages: scroll before Readability.
Command:
python3 -c "
import json
js = open('<skill_dir>/scripts/camofox/Readability.js').read()
expr = js + '; var a = new Readability(document.cloneNode(true)).parse(); JSON.stringify({title: a?.title, text: a?.textContent, excerpt: a?.excerpt, length: a?.length})'
json.dump({'userId': '$USER_ID', 'expression': expr}, open('/tmp/rb_article.json', 'w'))
"
exec curl -s -X POST "http://localhost:9377/tabs/<tabId>/evaluate" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $API_KEY" \
-d @/tmp/rb_article.json
Search Macros
Use with POST /tabs/{tabId}/navigate passing macro and query:
| Macro | Site |
|---|
@google_search | Google |
@youtube_search | YouTube |
@amazon_search | Amazon |
@reddit_search | Reddit |
@reddit_subreddit | Subreddit |
@wikipedia_search | Wikipedia |
@yelp_search | Yelp |
@spotify_search | Spotify |
@netflix_search | Netflix |
@linkedin_search | LinkedIn |
@twitter_search | Twitter/X |
@instagram_search | Instagram |
@tiktok_search | TikTok |
@twitch_search | Twitch |
Full workflow example
exec curl -s -X POST "http://localhost:9377/tabs" \
-H 'Content-Type: application/json' \
-d '{"userId":"$USER_ID","sessionKey":"$SESSION_KEY","url":"https://example.com"}'
exec curl -s "http://localhost:9377/tabs/abc123/snapshot?userId=$USER_ID"
exec curl -s -X POST "http://localhost:9377/tabs/abc123/evaluate" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $API_KEY" \
-d '{"userId":"$USER_ID","expression":"document.querySelector(\"main\")?.innerHTML || document.body.innerHTML"}'
exec curl -s -X POST "http://localhost:9377/tabs/abc123/scroll" \
-H 'Content-Type: application/json' \
-d '{"userId":"$USER_ID","direction":"down","amount":500}'
exec curl -s "http://localhost:9377/tabs/abc123/snapshot?userId=$USER_ID"
exec curl -s -X POST "http://localhost:9377/tabs/abc123/click" \
-H 'Content-Type: application/json' \
-d '{"userId":"$USER_ID","ref":"e3"}'
exec curl -s "http://localhost:9377/tabs/abc123/snapshot?userId=$USER_ID"
exec curl -s -X POST "http://localhost:9377/tabs/abc123/extract" \
-H 'Content-Type: application/json' \
-d '{"userId":"$USER_ID","schema":{"type":"object","properties":{"title":{"x-ref":"e1"}}}}'
exec curl -s -X DELETE "http://localhost:9377/tabs/abc123?userId=$USER_ID"
exec curl -s -X DELETE "http://localhost:9377/sessions/$USER_ID" \
-H "Authorization: Bearer $API_KEY"
Troubleshooting — container down:
docker start camofox-browser
docker run -d --name camofox-browser --restart unless-stopped \
-p 127.0.0.1:9377:9377 \
--env-file .env \
camofox-browser:latest
3. CloakBrowser — Protected sites
For sites with Cloudflare, Akamai, Kasada, DataDome, or when Camofox gets blocked.
Uses launch() from the npm package cloakbrowser.
Script: <skill_dir>/scripts/cloak/cloak-fetch.mjs
exec node <skill_dir>/scripts/cloak/cloak-fetch.mjs "https://example.com"
exec node <skill_dir>/scripts/cloak/cloak-fetch.mjs "https://example.com" --format text
exec node <skill_dir>/scripts/cloak/cloak-fetch.mjs "https://example.com" --format html
exec node <skill_dir>/scripts/cloak/cloak-fetch.mjs "https://ebay.com/..." --scroll
exec node <skill_dir>/scripts/cloak/cloak-fetch.mjs "https://protected-site.com"
exec node <skill_dir>/scripts/cloak/cloak-fetch.mjs "https://..." --proxy "socks5://user:pass@proxy:1080" --geoip
exec node <skill_dir>/scripts/cloak/cloak-fetch.mjs "https://..." --seed 12345 --platform windows
exec node <skill_dir>/scripts/cloak/cloak-script.mjs --script "<skill_dir>/scripts/cloak/scripts/screenshot.mjs" "https://example.com"
cloak-script.mjs — For complex interactions
When click, login, multi-step, or custom data extraction is needed:
exec node <skill_dir>/scripts/cloak/cloak-script.mjs \
--script "<skill_dir>/scripts/cloak/scripts/<your-script>.mjs" \
--proxy "socks5://..." --seed 12345
Full guide: <skill_dir>/scripts/cloak/guida-fetch.md
Security rules
These rules are enforced by the tooling — do not attempt to bypass them.
- No internal URLs. CloakBrowser blocks
localhost, 127.x, 10.x, 192.168.x, 169.254.x, and cloud metadata endpoints. DNS resolution is also checked to prevent DNS rebinding attacks.
- No path traversal.
--script must point within the skill directory. Symlinks are resolved before the check. Use --unsafe only if you know the risks.
- Sandbox enabled by default. User scripts receive proxied Playwright objects with only whitelisted methods. Note: Node.js APIs remain available — for full isolation, a
vm.Context would be required. Use --unsafe to bypass the Playwright sandbox.
- Rate limiting. 30 requests/minute by default. Use
--no-rate-limit to disable.
- API keys. Never paste API keys on the command line. Use env vars (
$CAMOFOX_API_KEY) or --env-file for Docker.
- URL encoding. Always use
--data-urlencode with curl — never interpolate raw input into URLs.
- Docker binding. Always use
127.0.0.1: prefix for port mapping. Never expose to 0.0.0.0.
- No social media browsing. Instagram, Facebook, TikTok, LinkedIn, Twitter/X require login — don't attempt with Camofox or CloakBrowser.
Security-relevant CLI flags
| Flag | Script | Purpose | When to use |
|---|
--unsafe | cloak-fetch.mjs, cloak-script.mjs | Bypass SSRF protection, sandbox, and path traversal | Access local/internal services or blocked Playwright APIs |
--no-rate-limit | Both | Disable 30 req/min limit | Bulk scraping (use responsibly) |
--verbose | Both | Include stack traces in errors | Debugging only |
--retry <n> | cloak-fetch.mjs | Retry on failure | Unreliable networks |
--timeout <ms> | Both | Navigation/launch timeout | Slow sites (default: 30000) |
--wait <ms> | cloak-fetch.mjs | Extra wait after page load | Dynamic content (default: 1000) |
--max-chars <n> | cloak-fetch.mjs | Truncate output | Large pages (default: 100000) |
--persistent <dir> | Both | Persistent browser profile | Cookie/session persistence |
Technical reference — Docker containers
Initial setup and diagnostics for SearXNG and Camofox. See <skill_dir>/docker/setup.md when needed.