# Web Scraping & Search Toolkit Technical reference for the three-tier web research stack configured on Core. ## Tier 1 — SearXNG (self-hosted, unlimited) - **URL:** `http://127.0.0.1:8888` - **Docker:** `~/docker/searxng/` - **Port:** 8888 (localhost only) - **API:** `http://127.0.0.1:8888/search?q=query&format=json` - **Config:** `searxng-data/settings.yml` — must enable JSON format: `search.formats: [html, json]` - **Auth:** None (localhost only, no auth needed) - **Capacity:** Unlimited — self-hosted, no rate limits Used for: quick web lookups, private search, government site search (basic). Does NOT render JavaScript. ## Tier 2 — Firecrawl (cloud, free tier) - **API key:** in `~/.hermes/.env` or per-profile `.env` - **Plan:** Starter (1,000 credits/mo) - **Endpoint:** `https://api.firecrawl.dev/v1/scrape` - **Native Hermes tools:** `web_search`, `web_extract` (loaded per session) - **Config:** `web.backend: firecrawl` in config.yaml Used for: content extraction from URLs, web search grounded in current data. Free tier good for ~30 lookups/day. Upgrade to Standard (~$50-100/mo) for 5,000 credits if needed. **Note:** The native `web_search` and `web_extract` tools require the API key to be present in `.env` at session START. Adding the key mid-session won't enable them until the next `/reset` or new Hermes invocation. ## Tier 3 — ScrapingAnt (cloud, headless Chrome) - **API key:** in `~/.hermes/.env` or per-profile `.env` - **Plan:** Free (10,000 credits), then $19/mo for 100,000 credits - **Endpoint:** `https://api.scrapingant.com/v2/general?url=...&x-api-key=...` - **JS rendering:** `browser=true` parameter for headless Chrome execution Used for: JS-heavy sites (LinkedIn, Indeed, CBDriver, government portals, SPA apps). Essential for any site that serves blank HTML without JavaScript execution. ## Usage Decision Matrix | Site type | Tool | Timeout | |---|---|---| | Simple HTML pages, blogs, docs | SearXNG (free, instant) | 5s | | JSON API documentation, content extraction | Firecrawl | 10s | | JavaScript-heavy, SPAs, login gates | ScrapingAnt (browser=true) | 30s | | Government sites (Texas Legislature, court records) | ScrapingAnt (browser=true) | 30s+ | | People search (CBDriver, Indeed) | ScrapingAnt | 15s | | LinkedIn public profiles | ScrapingAnt (proxy_country=US) | 30s | ## Credit Tracking Usage logged at `~/.hermes/scripts/firecrawl-usage.json` via `track-firecrawl.py` cron. Daily check at 9 AM. Also tracked in portal at `capabilities.html`. ## Sharing Across Profiles When another profile (e.g. Anita's) needs web tools, the API keys must be added to THEIR profile's `.env` — the main profile's `.env` is NOT inherited: ```bash # ~/.hermes/profiles/anita/.env FIRECRAWL_API_KEY=... SCRAPINGANT_API_KEY=... SEARXNG_BASE_URL=http://127.0.0.1:8888 ``` Also add to their `config.yaml`: ```yaml web: backend: firecrawl use_gateway: true ``` Enable the web toolset: `hermes tools enable web --profile ` **CRITICAL: Profile Isolation — Config changes need gateway restart AND Telegram token** 1. After config changes, the gateway must be RESTARTED — values are NOT hot-reloaded 2. If the gateway restarts and comes up with NO messaging platforms connected, the Telegram bot token is likely missing from that profile's `.env` 3. The main profile's Telegram token is NOT inherited by child profiles 4. Each profile that needs Telegram must have `TELEGRAM_BOT_TOKEN`, `TELEGRAM_ALLOWED_USERS`, and `TELEGRAM_HOME_CHANNEL` in its own `.env` 5. Anita's gateway died during config update because the restart cron killed the old process and spawned one with web API keys but no Telegram token 6. Fix: copy Telegram env vars from main `.env` to the profile's `.env`, then kill the orphaned gateway PID and restart ## Data Broker Removal Full action plan at `/root/data-broker-removal-action-plan.md`. Recommendation: use Kanary ($84/yr) for automated removal across 50+ sites. No tools needed beyond the search stack — the same ScrapingAnt + Firecrawl combo can scan for a person's presence on data broker sites. ## Caddy Multi-Domain Reverse Proxy Caddy handles HTTPS termination and routing for multiple domains on the same server (Core, 152.53.192.33). Each domain gets a separate block in `/etc/caddy/Caddyfile`: ```caddy sign.itpropartner.com { reverse_proxy 127.0.0.1:3000 } core.itpropartner.com { header Access-Control-Allow-Origin "*" @vehicles path /vehicles.json handle @vehicles { root * /var/www/static file_server } } app.itpropartner.com { reverse_proxy 127.0.0.1:8081 } ``` **Caddy commands:** - `caddy fmt --overwrite /etc/caddy/Caddyfile` — format the config file - `systemctl reload caddy` — hot-reload without downtime - `systemctl restart caddy` — full restart (needed when adding new domains) - Caddy auto-provisions Let's Encrypt certificates for each domain - After adding a new domain to the Caddyfile, Caddy needs ~10-15s to provision the certificate before the domain starts serving HTTPS **New domain DNS setup:** 1. Add A record at DNS provider → 152.53.192.33 2. Add `domain.com { ... }` block to Caddyfile 3. `caddy fmt --overwrite` + `systemctl restart caddy` 4. Wait ~15s for Let's Encrypt 5. HTTPS works automatically **Port 443 note:** If Tailscale Serve is running, it holds port 443. Run `tailscale serve off` to free it before starting Caddy. This disables all Tailscale Serve routes.