Skip to content

feat: add ChromeDriver proxy for WebDriver and BiDi protocol support#164

Merged
rgarcia merged 11 commits into
mainfrom
tanmay/kernel-915-validation-gate
Mar 9, 2026
Merged

feat: add ChromeDriver proxy for WebDriver and BiDi protocol support#164
rgarcia merged 11 commits into
mainfrom
tanmay/kernel-915-validation-gate

Conversation

@tnsardesai

@tnsardesai tnsardesai commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

Install ChromeDriver matching the Chromium version in both headful and headless images, managed via supervisord on internal port 9225. A new ChromeDriver proxy (exposed on port 9224) intercepts session creation to inject goog:chromeOptions.debuggerAddress so ChromeDriver attaches to the already-running browser (at port 9222), rewrites webSocketUrl in responses to route BiDi traffic back through the proxy, and transparently proxies all other HTTP and WebSocket traffic.

Extracts the shared WebSocket bidirectional pump logic from devtoolsproxy into a reusable wsproxy package with a MessageTransform hook, used by both the existing DevTools proxy and the new ChromeDriver proxy. Expands CDP discovery endpoint proxying to include /json/list and makes proxy ports configurable via environment variables.

Includes comprehensive unit tests for the ChromeDriver proxy and manual BiDi validation scripts (raw WebSocket, Puppeteer, Selenium).

Checklist

  • A link to a related issue in our repository
  • A description of the changes proposed in the pull request.
  • @mentions of the person or team responsible for reviewing proposed changes.

Note

Medium Risk
Moderate risk because it adds a new externally exposed ChromeDriver proxy/port and rewrites WebDriver/BiDi session payloads, which can break existing automation clients or routing if misconfigured.

Overview
Adds first-class WebDriver/BiDi support via ChromeDriver. The Chromium headful/headless images now install a matching chromedriver, run it under supervisord on 127.0.0.1:9225, and expose a new public port 9224 for ChromeDriver access.

Introduces a Go ChromeDriver proxy that intercepts POST /session (and BiDi session.new) to inject goog:chromeOptions.debuggerAddress pointing at the DevTools proxy, rewrites returned capabilities.webSocketUrl to route BiDi back through the proxy, and otherwise reverse-proxies ChromeDriver HTTP/WebSocket traffic.

Refactors and hardens proxy plumbing. DevTools proxy ports become configurable (DEVTOOLS_PROXY_PORT, CHROMEDRIVER_PROXY_PORT, etc.), DevTools JSON discovery proxying is generalized to also cover /json/list with recursive URL rewriting, and the shared WebSocket pump is extracted into a reusable wsproxy package.

Testing/ops updates. Adds unit tests for JSON rewriting and ChromeDriver proxy behavior, new BiDi e2e tests plus Node-based validation scripts, and updates e2e container helpers to map/wait on the new ChromeDriver port.

Written by Cursor Bugbot for commit 62f0fc5. This will update automatically on new commits. Configure here.

@tnsardesai tnsardesai marked this pull request as ready for review February 25, 2026 18:46
@tnsardesai tnsardesai force-pushed the tanmay/kernel-915-validation-gate branch from 7941a36 to 433f3d1 Compare February 25, 2026 19:13
Comment thread server/lib/chromedriverproxy/proxy.go
Comment thread server/lib/chromedriverproxy/proxy.go
Comment thread server/lib/chromedriverproxy/proxy.go
Comment thread server/lib/wsproxy/wsproxy.go
tnsardesai and others added 4 commits March 6, 2026 15:56
Install ChromeDriver matching the Chromium version in both headful and
headless images, managed via supervisord on internal port 9225. A new
ChromeDriver proxy (exposed on port 9224) intercepts session creation to
inject goog:chromeOptions.debuggerAddress so ChromeDriver attaches to
the already-running browser, rewrites webSocketUrl in responses to route
BiDi traffic back through the proxy, and transparently proxies all other
HTTP and WebSocket traffic.

Extracts the shared WebSocket bidirectional pump logic from
devtoolsproxy into a reusable wsproxy package with a MessageTransform
hook, used by both the existing DevTools proxy and the new ChromeDriver
proxy. Expands CDP discovery endpoint proxying to include /json/list and
makes proxy ports configurable via environment variables.

Includes comprehensive unit tests for the ChromeDriver proxy and manual
BiDi validation scripts (raw WebSocket, Puppeteer, Selenium).

Co-authored-by: Cursor <cursoragent@cursor.com>
Make the chromedriver proxy runtime-configurable and more robust, then add explicit Vibium BiDi e2e coverage using dynamic session endpoints so remote WebDriver workflows are easier and less brittle.

Made-with: Cursor
Prevent parallel e2e tests from racing pnpm setup by making playwright dependency installation single-flight, avoiding intermittent ENOENT failures in CI.

Made-with: Cursor
@rgarcia rgarcia force-pushed the tanmay/kernel-915-validation-gate branch from 6b38b7c to d428c05 Compare March 6, 2026 21:32
Comment thread server/e2e/e2e_bidi_test.go
Comment thread server/cmd/api/main.go
rgarcia added 4 commits March 7, 2026 15:23
Serialize BiDi fixture dependency installation to avoid parallel npm races in e2e and recurse through nested Chrome JSON objects so URL rewriting is applied consistently.

Made-with: Cursor
Update the Vibium fixture to use the current start/stop API and exercise the ws://.../session path directly, so the e2e suite matches Vibium's documented remote-browser contract while keeping HTTP /session coverage for WebDriver clients.

Made-with: Cursor
Drop the newline-only difference in display.go so the PR no longer carries an unrelated formatting change.

Made-with: Cursor
Rename the ChromeDriver proxy setting to DevToolsProxyAddr so it reflects the CDP proxy endpoint it targets, and update the config surface to use DEVTOOLS_PROXY_ADDR consistently.

Made-with: Cursor
Comment thread images/chromium-headful/Dockerfile
Restore the checked-in server/api file so this PR stops carrying an unrelated binary deletion, and ignore local reference checkouts plus the server/api build artifact path.

Made-with: Cursor
Comment thread server/cmd/api/main.go Outdated

@hiroTamada hiroTamada left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-structured addition of ChromeDriver/BiDi support. The proxy design is minimal — only intercepts session creation and WebSocket URL rewriting. Good extraction of the shared wsproxy package, and solid test coverage across unit, integration, and real e2e with Puppeteer/Selenium/Vibium. Left two minor nits on the wrapper scripts.

Comment thread images/chromium-headless/image/wrapper.sh Outdated
Comment thread images/chromium-headful/wrapper.sh Outdated
Cancel upstream Chrome JSON fetches with the caller context and standardize headful/headless readiness checks so timeout warnings are visible and consistent across wrappers.

Made-with: Cursor

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment thread images/chromium-headful/wrapper.sh
@rgarcia rgarcia merged commit 9816e34 into main Mar 9, 2026
3 checks passed
@rgarcia rgarcia deleted the tanmay/kernel-915-validation-gate branch March 9, 2026 16:21
AbdulRashidReshamwala pushed a commit to reclaimprotocol/popcorn-images that referenced this pull request Mar 26, 2026
…ernel#164)

Install ChromeDriver matching the Chromium version in both headful and
headless images, managed via supervisord on internal port 9225. A new
ChromeDriver proxy (exposed on port 9224) intercepts session creation to
inject goog:chromeOptions.debuggerAddress so ChromeDriver attaches to
the already-running browser (at port 9222), rewrites webSocketUrl in
responses to route BiDi traffic back through the proxy, and
transparently proxies all other HTTP and WebSocket traffic.

Extracts the shared WebSocket bidirectional pump logic from
devtoolsproxy into a reusable wsproxy package with a MessageTransform
hook, used by both the existing DevTools proxy and the new ChromeDriver
proxy. Expands CDP discovery endpoint proxying to include /json/list and
makes proxy ports configurable via environment variables.

Includes comprehensive unit tests for the ChromeDriver proxy and manual
BiDi validation scripts (raw WebSocket, Puppeteer, Selenium).

- [ ] A link to a related issue in our repository
- [ ] A description of the changes proposed in the pull request.
- [ ] @mentions of the person or team responsible for reviewing proposed
changes.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Introduces a new network-facing ChromeDriver proxy with
request/response rewriting and adds new ports/services in the runtime
images, which could affect client connectivity and session creation
behavior if misconfigured.
>
> **Overview**
> **Adds ChromeDriver support end-to-end** by installing a matching
`chromedriver` into both headful/headless images, running it under
supervisord on `127.0.0.1:9225`, and exposing a new external proxy port
`9224` (docker + unikernel scripts updated accordingly).
>
> **Extends the Go API container** with a new ChromeDriver proxy server
that intercepts `POST /session` and BiDi `session.new` to inject
`goog:chromeOptions.debuggerAddress` (pointing at the DevTools proxy)
and rewrites returned `capabilities.webSocketUrl` so BiDi traffic routes
back through the proxy; proxy/DevTools ports and upstream addresses are
now configurable via env.
>
> **Refactors WebSocket proxying** by extracting shared bidirectional
pump logic into `lib/wsproxy` (with an optional message transform hook),
migrating the existing DevTools proxy to use it, and improving DevTools
discovery proxying to handle `/json/version` and `/json(/list)` with
recursive URL rewriting.
>
> **Adds test coverage and tooling**: unit tests for URL rewriting +
ChromeDriver proxy behavior, new BiDi e2e tests (raw WS +
Puppeteer/Selenium/Vibium scripts with npm deps), and minor `.gitignore`
updates.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
fd8e1b9. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Rafael Garcia <raf@kernel.sh>
IlyaasK added a commit that referenced this pull request Jun 26, 2026
## Summary

This is the public-repo equivalent of the private dead-payload cleanup
PR, with the additional public-only tracked `server/api` binary removed.

What changed:
- Deletes `images/chromium-headful/image-chromium/`, including old demo
HTML, Streamlit config, static-content files, tint2 config, and legacy
startup scripts.
- Removes the broad `COPY images/chromium-headful/image-chromium/ /`
from the public headful Dockerfile.
- Deletes the tracked `server/api` binary artifact.
- Adds `server/api` to `.dockerignore` so local rebuilt API binaries do
not get sent in Docker build context.
- Leaves the current supervised headful runtime path intact: the image
still copies Neko config, supervisor service definitions, WebRTC client
output, Envoy config, cert bootstrap, API binary from the builder stage,
Chromium launcher, wrapper, Playwright daemon bundle, and extensions via
current explicit Dockerfile steps.

## Why

The removed `image-chromium` directory was an old payload copied
directly into `/` during the headful image build. The current image no
longer uses that legacy startup/demo path, and the broad root copy makes
it easy for unrelated files under `image-chromium` to silently land in
the final runtime image.

The tracked `server/api` binary is a local build artifact. The
Dockerfile compiles/copies the runtime API binary from the build stages;
it does not need a checked-in executable under `server/api`. Keeping it
tracked increases repository size and can invalidate Docker build
context/layers when the binary changes.

## Git History / Removal Rationale

| removed item | likely reason it was added | why it is removed here |
| --- | --- | --- |
| `entrypoint.sh`, `start_all.sh`, `xvfb_startup.sh`,
`mutter_startup.sh`, `tint2_startup.sh` | Added with the WebRTC OSS
launch (`5c71470`, PR #13) from the old headful demo image. These
scripts manually started Xvfb, tint2, mutter, x11vnc/noVNC, then
launched a demo server. | The current headful image uses supervisor
service definitions, wrapper scripts, Neko/WebRTC components, and
explicit Dockerfile copies. These legacy scripts are not the active
process model. |
| `http_server.py`, `index.html`, `static_content/index.html`,
`.streamlit/config.toml` | Added with the same demo payload. The old
entrypoint started a Python static server and Streamlit demo app and
printed local demo instructions. | The current public runtime does not
launch that demo stack. Keeping this content in `/` only preserves stale
files that are not part of the current browser/session path. |
| `.config/tint2/*` | Added to support the old desktop panel
configuration used by the demo/Xvfb/tint2 startup scripts. | Current
headful runtime behavior is managed by the current image service/config
path; this tint2 payload is only reachable through the removed legacy
startup scripts. |
| `COPY images/chromium-headful/image-chromium/ /` | Added during the
save/reuse user-data and supervisor transition. It preserved the old
root payload while the image moved toward explicit supervised services.
| The broad copy is now the risky part: any file under the legacy
directory silently mutates the final root filesystem. Current-purpose
files are already copied explicitly elsewhere in the Dockerfile. |
| `server/api` | Added as a 14 MB executable in `0fba5a0` (PR #148),
alongside smooth mouse movement source changes. A later PR (`9816e34`,
PR #164) added `server/api` to `.gitignore`, which strongly suggests it
was recognized as a local build artifact but was already tracked. | The
image build compiles the API from source; the tracked binary is not
referenced by Dockerfiles or runtime code. This PR removes it from git
and adds `.dockerignore` coverage so local rebuilds do not pollute
Docker context. |

## Validation

Ran locally after merging current `main` into this branch:
- `git diff --check origin/main...HEAD`
- `DOCKER_BUILDKIT=1 docker build --check -f
images/chromium-headful/Dockerfile .`
- `cd server && go test ./e2e -run TestDoesNotExist -count=0`
- `~/.agents/skills/autoreview/scripts/autoreview --mode branch --base
origin/main`

Autoreview result:
- Clean, no accepted/actionable findings.


GitHub Actions on the current-main refresh:
- image builds: passed
- server test/e2e job: passed
- launcher test, scan, and Socket: passed

## Unknowns / Final Gates

- The deleted legacy payload did not have current references after
removal. Full image CI passed on the current-main refresh.
- Public does not have the private CapMonster e2e test file that changed
in private PR #200, so this PR does not include that private-only test
hardening.
- The tracked `server/api` binary is removed from git, but local
developers can still build a local `server/api`; `.gitignore` and now
`.dockerignore` keep it out of commits and Docker build context.

## Fast Docker Review

This PR follows the fast-build guidance by removing dead build context
and a broad root copy from the headful image.

Against the checklist:
- The old `image-chromium` root payload copy is removed, so files cannot
silently land in `/` just because they sit under a legacy directory.
- The tracked `server/api` binary is removed and added to
`.dockerignore`, so local binary rebuilds do not invalidate image build
context or layers.
- The current runtime files continue to be copied explicitly where the
Dockerfile needs them.
- No build tools are added to runtime images, and no source copies are
moved earlier in the Dockerfile.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Dead files and a broad root COPY are removed; the active supervised
headful image path is unchanged and CI validated the Dockerfile build.
> 
> **Overview**
> Removes legacy **Computer Use Demo** payload from the public headful
image and stops shipping a checked-in API binary.
> 
> The entire `images/chromium-headful/image-chromium/` tree is deleted
(Streamlit/static HTML, tint2 panel config, Xvfb/tint2/mutter startup
scripts, and related demo entrypoints). The headful **Dockerfile** no
longer does `COPY images/chromium-headful/image-chromium/ /`; runtime
files continue to be copied explicitly (Neko, supervisor, Envoy, built
API from `server-builder`, etc.).
> 
> The tracked **`server/api`** executable is removed from the repo, and
**`server/api`** is added to **`.dockerignore`** so local rebuilds do
not bloat Docker build context or invalidate layers.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
fb7e962. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants