Configure a Stealth Browser

View as Markdown

Stealth is mostly about not contradicting yourself. BCTRL runs Kameleo underneath, which picks a real, internally coherent fingerprint - your job in the config is to keep the parts you control (proxy geo, locale, WebRTC) telling the same story.

1import { Bctrl } from "@bctrl/sdk";
2import { chromium } from "playwright";
3
4const bctrl = new Bctrl({ apiKey: process.env.BCTRL_API_KEY! });
5
6const runtime = await bctrl.runtimes.create({
7type: "browser",
8name: "stealth-recipe",
9config: {
10stealth: "ultra",
11proxy: {
12type: "managed-rotating",
13country: "US",
14rotation: "sticky", // one exit IP for the whole session
15stickyKey: "stealth-recipe",
16preference: "quality",
17},
18fingerprint: {
19device: "desktop",
20os: "windows",
21browser: "chrome",
22locale: ["en-US", "en"], // agree with the proxy country
23},
24webRtcProxyOnly: true, // WebRTC can't leak your real IP around the proxy
25},
26});
27const { connectUrl } = await bctrl.runtimes.start(runtime.id);
28
29// See what websites see.
30const browser = await chromium.connectOverCDP(connectUrl);
31const context = browser.contexts()[0] ?? (await browser.newContext());
32const page = context.pages()[0] ?? (await context.newPage());
33
34await page.goto("https://pixelscan.net");
35await page.waitForTimeout(15_000); // let the checks finish
36await page.screenshot({ path: "pixelscan.png", fullPage: true });
37
38await browser.close();
39await bctrl.runtimes.stop(runtime.id);

📸 Content TODO: run this once and embed the resulting pixelscan screenshot (a clean “consistent” result). A CreepJS capture works too. Date-stamp it in the caption - detection checkers evolve and readers will re-test.

Why these knobs

  • stealth: "ultra" - the anti-detection hardening level. medium/high/ultra trade resource use for depth; start with high and move up if a target still challenges you.
  • Sticky rotating proxy - rotation between sessions, one exit IP within a session. An IP that changes mid-session is itself a signal.
  • fingerprint filters, not values - BCTRL defaults to a desktop Windows search and randomly selects Chrome, Edge, or Safari when browser is omitted. You can narrow Kameleo’s search further (desktop + windows + chrome), and it picks a real fingerprint where canvas, WebGL, fonts, and UA already agree. Never hand-build the parts yourself.
  • locale matches the proxy country - a US exit IP with a de-DE language stack is a contradiction detectors look for.
  • webRtcProxyOnly - WebRTC is the classic side channel that reveals the real IP behind a proxy; force it through the proxy.

Keep the identity across sessions

A fingerprint that’s brand new on every visit is also a signal. For flows that return to the same site, use a profile-backed runtime (config.profile: true): cookies, logins, and the fingerprint identity persist across starts, so you come back as the same browser - not a fresh stranger.

Next