Stealth is a consistency problem
TL;DR — Most cloud browsers approach stealth the same way: take Chromium, patch the obvious tells, rotate some fingerprint values, repeat every release. We made a different bet. BCTRL runs Kameleo — a commercial antidetect browser engine whose entire business depends on fingerprints that hold up — underneath an API. You get stealth: "medium" | "high" | "ultra" on runtime creation, fingerprint filters instead of fingerprint values, and a set of knobs whose real job is keeping your browser’s story consistent. Because that’s what detection actually tests.
What detectors actually check
Anti-bot systems work in four layers: network reputation (is this IP a datacenter, has it been seen misbehaving), fingerprinting (do the browser’s hundreds of observable properties describe a real device), behavior (does the input move like a hand), and challenges (captchas, when the first three are inconclusive).
The fingerprinting layer is widely misunderstood. It isn’t a blocklist of known-bot values — it’s a coherence check. A real Windows machine running Chrome has a canvas render, a WebGL vendor string, a font list, an audio stack, a timezone, and a language header that all agree with each other, because they’re all consequences of the same physical machine. Spoof any one of them in isolation and you haven’t hidden; you’ve created a contradiction, and contradictions are precisely what the detector is scoring. The classic failure isn’t “your user agent looked fake.” It’s “your user agent said Windows while your canvas said Linux, your timezone said Berlin while your IP said Virginia, and no real machine does that.”
The trouble with patched Chromium
You can absolutely patch Chromium to pass today’s checks — hide the automation flags, noise the canvas, fake the plugin list. The problem is structural: every Chrome release moves the surface, every detector update probes a new property, and your patch set is a diff you maintain forever against both. Worse, the patches themselves are detectable when they’re inconsistent — noise the canvas but forget the offscreen canvas, and you’ve handed the detector a brand-new signal that only patched browsers emit.
This is a game where the defender ships weekly and your stealth is a side project. We didn’t want our stealth to be a side project, so we put a company whose only product is winning this game underneath ours.
Filters, not values
The design consequence shows up in the API. You don’t hand BCTRL a fingerprint — you narrow the search:
const runtime = await bctrl.runtimes.create({
type: "browser",
name: "stealth-post",
config: {
stealth: "ultra",
proxy: {
type: "managed-rotating",
country: "US",
rotation: "sticky", // one exit IP for the whole session
stickyKey: "demo",
},
fingerprint: {
device: "desktop",
os: "windows",
browser: "chrome",
locale: ["en-US", "en"], // agrees with the proxy country
},
webRtcProxyOnly: true,
},
});fingerprint takes device, os, browser, a version constraint, and a language stack — and Kameleo selects a real, internally coherent fingerprint matching those filters. Omit the block entirely and it still picks a coherent one. What you cannot do through this API is hand-assemble a fingerprint from parts, because hand-assembled fingerprints are how you manufacture contradictions.
The knobs you do control are the consistency-critical ones. The proxy is sticky because an exit IP that changes mid-session is a signal by itself. The locale matches the proxy country because a German language stack on a Virginia IP is a contradiction. WebRTC is forced through the proxy because it’s the classic side channel that leaks the real IP around an otherwise perfect setup. And autoUpgrade keeps the browser version current while preserving the fingerprint identity — a browser frozen at a three-month-old Chrome version is, you guessed it, a contradiction.
Identity over time
There’s a fourth dimension detectors score: persistence. A returning session cookie attached to a never-before-seen fingerprint looks exactly like what it is — state copied into a fresh bot. Profile-backed runtimes (config.profile: true) keep the whole identity together across restarts: cookies, storage, logins, and the fingerprint itself, managed as one unit for the life of the profile. You come back as the same browser, not a stranger holding the same cookies.
The behavioral layer rounds it out. Input synthesized through BCTRL’s interactive paths — including the captcha solver’s drags — plays back with human motion characteristics: minimum-jerk velocity profiles, slight tremor, corrective settles at the end of movements. We wrote about the motion model in the captcha post; the same principle applies everywhere input is generated, because behavioral scoring doesn’t care which feature moved the mouse.
Evidence
📊 Content TODO: the pass-rate table this post needs to earn its title. Suggested setup: each stealth level (
medium/high/ultra) × N≥50 runs against CreepJS, Pixelscan, BrowserScan, SannySoft, and fingerprint.com’s bot test, plus one real Cloudflare-challenged and one DataDome-protected page. Disclose proxy type, region, and date. Include defaults from two or three competitors if you’re willing to name them — comparisons get cited. A clean green CreepJS screenshot belongs right here.
A date matters more in this section than anywhere else on this blog. Detection checkers evolve, results rot, and readers will re-run them — so we will re-run them too and update the table rather than let it age quietly.
What this doesn’t mean
None of this makes a browser “undetectable,” and you should distrust anyone who uses that word. It means the cheap, automatic detection layers — the ones responsible for most blocks and most captchas — see a coherent, residential, behaviorally plausible browser, and the challenges that still fire land in front of a solver built for them. Stealth, proxies, and solving are one system behind one runtime config, which is the actual point: you shouldn’t have to be a fingerprinting expert to not contradict yourself.
The stealth recipe is the five-minute version — launch an ultra runtime with a geo-pinned proxy, point it at a checker, screenshot what websites see.