The Problem
HeadlessX addresses the operational gap between headless browser automation and production reliability: scraping at scale requires undetected browsing, queue-backed job management, and API access control, all self-hosted. Most teams stitch together Playwright scripts, proxy management, and ad-hoc schedulers, which breaks under load and leaves no audit trail.
What This Does
HeadlessX is a self-hosted scraping platform with a web dashboard (apps/web), a protected API (apps/api), and a CLI (packages/cli). It wraps Camoufox (a Firefox fork) via packages/headfox-js to run undetected browser sessions, then exposes operators for website extraction, Google AI Search, Tavily, Exa, and YouTube through apps/api/src/controllers/. A queue system (apps/api/src/services/queue/QueueWorker.ts) backs long-running jobs, and an MCP endpoint (apps/api/src/mcp/server.ts) exposes the API to AI agents.
The platform is a monorepo of 4 self-contained projects: apps/ (web, API, Go HTML-to-MD service, yt-engine), packages/ (headfox-js, CLI), skills/, and scripts/. It uses pnpm, Docker, and Prisma with a PostgreSQL schema in apps/api/prisma/schema.prisma.
How It Is Wired
Execution starts at several entry points. The API server (apps/api/src/app.ts) wires controllers to routes; the MCP server (apps/api/src/mcp/server.ts) handles agent requests via handleMcpRequest, which resolves API keys through resolveMcpApiKey → authenticate (a Prisma DB read) in 3 hops. The headfox-js server (packages/headfox-js/src/server.ts) launches the browser via launchHeadfoxServer → launchOptions → publicIP (an outbound network call via impit.fetch) in 3 hops. The CLI (packages/cli/src/index.ts) is the least connected module (imports 15, imported by 0), a pure entry point.
The highest-blast-radius functions: cn (42 callers, in apps/web/src/lib/utils.ts), writeStructured (25), and requestJson (19). The most-connected modules are apps/web/src/components/playground/shared/index.ts (23 dependents) and apps/api/src/database/client.ts (14 dependents, 0 imports — a stable hub). Five modules sit in an import cycle, including packages/headfox-js/src/utils.ts, which is also an oversized 653-line file. The cn utility being called from 42 places means a signature change ripples across the entire web frontend.
How To Use It
git clone https://github.com/moses-y/HeadlessX
cd HeadlessX
pnpm install
cp .env.example .env # configure DATABASE_URL, API keys
pnpm --filter @headlessx/api dev # starts API server
pnpm --filter @headlessx/web dev # starts dashboard
Configuration lives in .env.example (root) and apps/api/.env (via apps/api/src/env.ts). Google AI Search requires a one-time "Build Cookies" run in the dashboard. The CLI (packages/cli) offers headlessx init, start, logs, status, and doctor. Docker deployment scaffolding is under infra/domain-setup with Caddy.
Real-World Use
A team runs HeadlessX to extract competitor pricing data daily. They hit the API with an API key (POST /api/scrape), which enqueues a job, the queue worker (QueueWorker.ts) picks it up, spins a Camoufox browser via BrowserService.ts, and streams results back through StreamingScrapeController.ts. A downstream agent calls the MCP endpoint to query the same data without a browser.
Code Health & Issues
Static analysis found 114 issues (57 high, 57 medium) across 6 kinds. The most significant:
- High – Deep nesting (x44) – e.g.,
apps/api/src/services/scrape/StreamingScraperService.tshas max indentation depth 9; control flow is hard to follow. Fix: early returns/guard clauses. - High – Import cycle members (x5) –
packages/headfox-js/src/utils.ts,StreamingScraperService.ts,JobManager.tsparticipate in circular imports; change one and you risk runtime initialization order bugs. - High – Duplicated code blocks – 450 repeated 6-line blocks across 88 files, including
ExaController.ts,TavilyController.ts,YoutubeController.ts. Fix: extract shared helpers. - Medium – Hub modules (x6) –
apps/web/src/components/playground/shared/index.ts(23 dependents) andapps/api/src/database/client.ts(14 dependents) are high-blast-radius churn points. - Medium – Oversized file –
packages/headfox-js/src/utils.tsat 653 lines.
SDLC gaps: no CI pipeline (283 source files, zero workflows), no Dependabot/Renovate, the Go Dockerfile uses mutable base images (golang:1.26-alpine), and assets/demo.gif is 86.5MB — should be LFS or external. Test ratio is 0.021 (6 test files vs 283 source files).
The Bottom Line
This is a genuinely functional scraping platform with a real queue, MCP support, and a working CLI — not a scaffold. The codebase is broad but shallow in testing, and the import cycles and duplicated controllers will make maintenance expensive. Use it if you need a self-hosted, undetected scraping stack and can invest in CI and refactoring; skip it if you need something battle-tested out of the box.