feat: optional patchright stealth backend (GSTACK_STEALTH=patchright)#969
Open
ramazanayyildiz wants to merge 1 commit intogarrytan:mainfrom
Open
feat: optional patchright stealth backend (GSTACK_STEALTH=patchright)#969ramazanayyildiz wants to merge 1 commit intogarrytan:mainfrom
ramazanayyildiz wants to merge 1 commit intogarrytan:mainfrom
Conversation
Adds patchright as an optionalDependency and a dynamic chromium loader in BrowserManager. When GSTACK_STEALTH=patchright is set, gstack's Chromium driver is swapped for patchright's CDP-leak-free build. Falls back to vanilla Playwright with a warning if not installed. Default behavior unchanged. Why: gstack's existing JS-shim stealth patches (navigator.plugins, cdc_, etc.) are L4 — JavaScript API surface. Modern bot detectors check 40+ surfaces and inspect CDP traffic directly. Patchright is an L2 patch: it rewrites Playwright's driver at build time via ts-morph AST patches to eliminate the Runtime.Enable CDP call that Cloudflare, DataDome, Imperva, and X all fingerprint on. See https://github.com/Kaliiiiiiiiii-Vinyzu/patchright for the project. Measured delta (same machine, same fingerprint probe, no other changes): gstack default gstack + patchright navigator.webdriver true FAIL false PASS UA string HeadlessChrome Chrome/146 navigator.plugins.length 0 FAIL 5 PASS navigator.mimeTypes.length 0 FAIL 2 PASS WebGL renderer null FAIL real GPU Screen dimensions 1280x720 fixed real display Custom stealth score 4/7 6/7 The HeadlessChrome UA alone is enough to be blocked on any Cloudflare- protected site. Patchright fixes it with a module swap, no code changes. Scope: minimal and opt-in. - package.json: patchright@^1.59.0 under optionalDependencies (+3 lines) - browser-manager.ts: getChromium() helper + 3 call site updates (+41/-4) - BROWSER.md: new "Stealth mode" section with install, usage, caveats (+60) Caveats documented in BROWSER.md: - Max stealth requires headed mode. This PR does NOT change default launch options. Follow-up work can add GSTACK_HEADLESS=false support. - patchright disables page.on('console') by design (part of the Runtime.Enable patch). Most workflows don't rely on this. - One-time bunx patchright install chrome downloads a patched Chrome binary (~300 MB) into ~/Library/Caches/ms-playwright/. - /connect headed-mode path's JS-shim patches are unchanged; patchright is additive in that path. Does not change default behavior for existing users. Turn it on with GSTACK_STEALTH=patchright when you hit sites that fingerprint browser automation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
patchrightas anoptionalDependencyand a dynamic chromium loader inBrowserManager. WhenGSTACK_STEALTH=patchrightis set AND the package is installed, gstack's Chromium driver is swapped for patchright's CDP-leak-free build. Falls back to vanilla Playwright with a warning if not installed. Default behavior unchanged.Why this matters
gstack's existing stealth (
addInitScriptpatches fornavigator.plugins,cdc_, languages, etc.) is L4 — the JavaScript API surface. Modern bot detectors (Cloudflare, DataDome, Imperva, Kasada) check 40+ surfaces and, critically, inspect the CDP protocol directly for theRuntime.Enablecall that every vanilla Playwrightpage.evaluate()issues.Patchright is an L2 patch: it uses
ts-morphto rewrite upstream Playwright's driver source at build time, replacingupdateRequestInterceptionwith a tagged init-script + isolated-worldevaluateExpressionloop. The result: JavaScript runs without ever callingRuntime.Enable. Detectors can't see the pattern they're looking for because it literally doesn't happen.Measured delta
Independent fingerprint probe, same M4 Pro Mac, same test JS, same page, only difference is the chromium driver:
navigator.webdrivertrue❌false✅HeadlessChrome/145.0.7632.6❌Chrome/146.0.0.0✅navigator.plugins.length0❌5✅navigator.mimeTypes.length0❌2✅null(disabled in headless) ❌1280×720fixed ❌cdc_/\$cdc_leaksThe
HeadlessChromeUA alone is sufficient to be blocked by any Cloudflare-protected site. Patchright fixes it with zero code changes — just a module swap.Scope
Minimal and opt-in. 107 lines across 4 files.
package.json—patchright@^1.59.0underoptionalDependencies(+3)browse/src/browser-manager.ts—getChromium()helper + 3 call site updates (+41/-4)bun.lock— updated (+7)BROWSER.md— new "Stealth mode" section with install, usage, measured delta, caveats (+60)Usage
Caveats (documented in BROWSER.md)
GSTACK_HEADLESS=falsesupport. On Linux servers, wrap in Xvfb:xvfb-run \$B goto ....page.on('console', ...)is disabled by design as part of the Runtime.Enable patch. Most workflows don't rely on this.bunx patchright install chromepulls a patched Chrome binary (~300 MB) into~/Library/Caches/ms-playwright/(macOS). One-time cost./connectheaded path unchanged — gstack's existing JS-shim stealth patches there still apply. Patchright is additive.Why opt-in, not default?
Patchright is Apache-2.0, actively maintained, and tracks Playwright releases lockstep (patchright@1.59.4 ships within days of playwright@1.59). But:
Keeping it opt-in means the default gstack experience stays fast and small. Turn it on when you need it.
Testing
bun install— patchright installs as optional dep (93 packages total, +1 from main)bun build --compile browse/src/cli.ts— clean build, 57ms compilebun test browse/test/— existing tests still pass (the dynamic loader falls back to vanilla Playwright whenGSTACK_STEALTHis not set)Runtime stealth delta validated independently via
~/.claude/scripts/patchright-eval.sh— numbers in the table above.Test plan
bun installsucceeds (patchright pulled as optional dep)bun run buildcompiles without errorsbun test browse/test/passes (default path unchanged)GSTACK_STEALTH=patchright \$B goto https://example.comlaunches with patchright's chromiumbunx patchright install chromepulls the patched Chrome binary (one-time)🤖 Generated with Claude Code