Skip to content

TecharoHQ/foxbridge

Repository files navigation

foxbridge

A CDP to Firefox BiDi protocol proxy.

Note

This is a hostile fork of VulpineOS/foxbridge maintained for use in testing Anubis via Gubal.

What is foxbridge?

foxbridge translates Chrome DevTools Protocol (CDP) into Firefox's Juggler and WebDriver BiDi protocols. Any tool built for Chrome — Puppeteer, browser-use, Playwright CDP mode — can control Firefox-based browsers without modification.

CDP Client (Puppeteer, etc.)
  │
  ▼ WebSocket (ws://localhost:9222)
┌────────────────────────────────────┐
│ foxbridge                          │
│  CDP Server + HTTP Discovery       │
│  Bridge Layer (method translation) │
│  ├── Juggler Backend (pipe FD 3/4) │
│  └── BiDi Backend (WebSocket)      │
└────────────────────────────────────┘
  │
  ▼
Firefox

Why?

  • Most AI agent and automation tools only speak CDP, but Firefox exposes Juggler and WebDriver BiDi instead
  • No existing tool bridges CDP to Firefox's automation protocols
  • Dual backend: Juggler (Playwright's protocol, production-ready) and WebDriver BiDi (W3C standard, future-proof)

Running foxbridge

Install the foxbridge binary (needs Go 1.26+), then point it at a Firefox binary:

go install github.com/TecharoHQ/foxbridge/cmd/foxbridge@latest

foxbridge --binary /usr/bin/firefox --headless --backend bidi

That command:

  • --binary /usr/bin/firefox — launches this Firefox binary (drop the flag to let foxbridge auto-detect one on your PATH).
  • --headless — runs Firefox without a visible window; omit it to watch the browser drive.
  • --backend bidi — translates CDP to Firefox's WebDriver BiDi protocol. Drop the flag to use the default Juggler backend instead.

Firefox version support

The bidi backend works with stock Firefox. Its full command surface (navigation, script evaluation, screenshots, cookies, request interception, and input) is smoke-tested against every archived release from Firefox 124 through 152 — see Testing.

124 is the floor. Firefox's WebDriver BiDi storage module (used for cookies) landed in 124, so that is the oldest release where the whole surface works. Firefox 122–123 work for everything except cookies. On Firefox 120 and older, BiDi is too incomplete — Firefox closes the connection when foxbridge subscribes to events it doesn't implement — so those are unsupported. foxbridge subscribes to events one at a time and tolerates unsupported ones, so it still starts and serves discovery on older browsers, but expect degraded behavior.

The juggler backend needs a Firefox build that ships the Juggler protocol (e.g. Playwright's Firefox or Camoufox); stock Firefox does not have it, so use --backend bidi there.

foxbridge launches Firefox, then serves a CDP endpoint on ws://localhost:9222. Point any CDP tool at it:

// Puppeteer
puppeteer.connect({ browserWSEndpoint: 'ws://localhost:9222' });

Working from a checkout of this repo? Swap foxbridge for go run ./cmd/foxbridge to run straight from source without installing anything:

go run ./cmd/foxbridge --binary /usr/bin/firefox --headless --backend bidi

More ways to run

# Juggler backend (default) on the default port
foxbridge --binary /usr/bin/firefox

# BiDi backend on a custom port
foxbridge --binary /usr/bin/firefox --backend bidi --port 9222

# Connect to an already-running BiDi endpoint (don't launch Firefox)
foxbridge --backend bidi --bidi-url ws://localhost:9223/session

# Serve CDP over a Unix domain socket instead of a TCP port
foxbridge --binary /usr/bin/firefox --socket /tmp/foxbridge.sock

# Deterministic runtime shims for low-flake tests
foxbridge --binary /usr/bin/firefox \
  --deterministic-time 1700000000000 --deterministic-seed 42

# Record a live CDP session to JSONL
foxbridge --binary /usr/bin/firefox --record ./trace.jsonl

# Replay a captured CDP session without launching Firefox
foxbridge replay --recording ./trace.jsonl

# Print a protocol coverage report
foxbridge doctor

CDP Coverage Snapshot

foxbridge doctor is now the source of truth for protocol coverage:

foxbridge doctor
foxbridge doctor --format json

Current snapshot on main from foxbridge doctor:

  • 662 upstream CDP methods in the bundled protocol snapshot
  • 89 implemented methods
  • 203 stubbed compatibility methods
  • 370 missing methods
  • 8 foxbridge-only extensions

The strongest covered areas today are DOM, Page, Target, Network, Fetch, Runtime, Input, Emulation, IO, and Performance. Stubbed domains still exist for compatibility, but foxbridge should no longer be described as “13 fully implemented CDP domains”.

Event Translation

Firefox Event CDP Event
Browser.attachedToTarget Target.attachedToTarget (tab + page dual session)
Browser.detachedFromTarget Target.targetDestroyed
Page.navigationCommitted Page.frameNavigated + lifecycle events
Page.eventFired(load) Page.loadEventFired + Page.frameStoppedLoading
Page.eventFired(DOMContentLoaded) Page.domContentEventFired
Runtime.executionContextCreated Runtime.executionContextCreated
Runtime.console Runtime.consoleAPICalled
Page.dialogOpened Page.javascriptDialogOpening
Network.requestWillBeSent Network.requestWillBeSent
Network.responseReceived Network.responseReceived
Browser.requestIntercepted Fetch.requestPaused

HTTP Discovery

# Browser info
curl http://localhost:9222/json/version

# Active page targets
curl http://localhost:9222/json/list

Dual Backend Architecture

foxbridge implements the Backend interface — swap between Juggler and BiDi without changing the bridge layer:

type Backend interface {
    Call(sessionID, method string, params json.RawMessage) (json.RawMessage, error)
    Subscribe(event string, handler func(sessionID string, params json.RawMessage))
    Close() error
}
  • Juggler (--backend juggler): Pipe FD 3/4 transport, null-byte JSON framing. Direct protocol match with Playwright. Default and most battle-tested.
  • BiDi (--backend bidi): WebSocket transport, W3C standard. The BiDi client internally translates Juggler-style calls to BiDi equivalents so the bridge layer works unchanged.

Embedded Mode

foxbridge can run as an embedded CDP server inside a host process sharing a single Firefox instance:

Host -> Firefox (single process) -> Juggler pipe -> juggler.Client
  ├── Direct Juggler calls
  └── Embedded foxbridge CDP server on :9222
       └── CDP clients connect via cdpUrl -> same Firefox contexts

No separate process needed — the host imports foxbridge as a Go library and starts the CDP server in-process. CDP clients connect to Firefox through the embedded bridge.

CLI Flags

Flag Default Description
--port 9222 CDP WebSocket port
--socket Unix-domain socket path for the CDP HTTP/WebSocket server
--binary auto-detect Firefox binary path
--headless false Run headless
--profile Firefox profile directory
--backend juggler Backend: juggler or bidi
--bidi-url Connect to existing BiDi endpoint
--bidi-port 9223 BiDi port when auto-launching Firefox
--record Path to write a JSONL CDP wire capture
--deterministic-time 0 Base epoch in milliseconds for deterministic Date.now / performance.now injection
--deterministic-seed 0 Seed for deterministic Math.random / crypto.getRandomValues injection

When --socket is set, foxbridge listens on a Unix domain socket instead of binding a TCP port. Discovery endpoints are still available over HTTP, and the browser-level WebSocket URL remains /devtools/browser/foxbridge; clients must dial that URL using their library's Unix-socket transport or socketPath option.

When either deterministic flag is set, foxbridge injects a deterministic runtime prelude into page sessions so Date.now, performance.now, Math.random, and crypto.getRandomValues become repeatable across runs.

When --record is set, foxbridge writes every inbound and outbound CDP frame for the active session to JSONL. The resulting file can be replayed later with foxbridge replay --recording /path/to/trace.jsonl to reproduce client traffic without launching a browser.

Testing

foxbridge has comprehensive test coverage:

  • Go unit tests — all passing with race detector (go test -race ./...)

  • chromedp end-to-end suite — drives a live Firefox through the CDP proxy (go run test/chromedp/main.go against a running foxbridge)

  • Firefox version matrix — smoke-tests the BiDi backend against archived Firefox releases (fully green 124–152):

    # needs docker + network access to ghcr.io
    test/firefox-versions.sh              # default list (129 … 152)
    test/firefox-versions.sh 129 140 152  # specific versions

    For each version it boots ghcr.io/techarohq/gubal/firefox:<major> with the freshly built foxbridge binary and runs a per-command driver (test/chromedp/drive/main.go) that exercises each distinct BiDi command.

go test -race ./...

CI runs on every push via GitHub Actions (build + vet + race-detected tests).

License

MIT

About

A hostile fork of VulpineOS/foxbridge made for testing Anubis

Resources

Code of conduct

Security policy

Stars

5 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors