Drive stealth Chromium to bypass bot detection in web automation
Fortress is an open-source stealth Chromium engine that spoofs the browser fingerprint in C++ so scrapers and agents read as a real Chrome install.
150.0.7871.114Add to Favorites
Why it matters
Fortress enables developers to run web scrapers and browser automation agents without getting blocked by bot detectors. It patches Chromium at the C++ level to present a genuine browser fingerprint that passes CreepJS, Sannysoft, BrowserScan, and live Cloudflare challenges, so your Playwright or Puppeteer scripts reach their target pages and complete their runs.
Outcomes
What it gets done
Spoof browser fingerprints (canvas, WebGL, audio, navigator) inside Chromium's C++ engine so detection tools read the browser as human
Expose a CDP endpoint that works as a drop-in replacement for standard Chromium with Playwright, Puppeteer, or any CDP client
Pass bot-detection gauntlets including 0% headless score on CreepJS and live Cloudflare Turnstile challenges
Configure per-launch personas with coherent GPU, timezone, language, keyboard layout, and TLS fingerprint using command-line switches
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/tiliondev-fortress | bash Overview
Fortress
Fortress is an open-source Chromium fork that corrects the browser fingerprint (canvas, WebGL, audio, navigator, and more) inside the engine's C++ rather than via a self-revealing JavaScript patch, so scrapers and browser agents connecting over CDP clear detectors like CreepJS, Cloudflare Turnstile, and Akamai Bot Manager as a stock Chrome install. Use it for legitimate scraping and browser-agent automation blocked by fingerprint-based bot detection, connecting existing Playwright/Puppeteer code over CDP with no code changes; it doesn't fix IP-based blocking on its own, and the project explicitly avoids claiming permanent undetectability.
What it does
Fortress is a Chromium fork that spoofs the browser fingerprint from inside the engine's C++, rather than patching it from JavaScript on top of a stock browser. Bot detectors read surfaces like canvas, WebGL, audio, fonts, and navigator properties, and the usual stealth approach overrides those getters from script - but that leaves a native-vs-JS-function tell any detector can find: .toString() reveals the override's own source instead of [native code], and a detector can re-grab a pristine primitive from an iframe or Web Worker (a different JS realm than the main-world patch) to catch it that way regardless. Fortress corrects the getters in Chromium's C++ itself, so navigator.vendor and the roughly thirty other spoofed surfaces resolve to real native code, identical across every realm - main frame, iframes, and workers - because there's no JavaScript hijacking layer to detect in the first place.
It ships as an ordinary browser binary exposing a CDP endpoint (:9222), so existing Playwright or Puppeteer automation connects over CDP with no other code changes. The source reports 0% headless and stealth detection on CreepJS, a clean pass on Sannysoft and BrowserScan, no Runtime.enable CDP leak, and clearing live Cloudflare Turnstile and Akamai Bot Manager challenges on retailer sites (aa.com, lowes.com, macys.com, kohls.com) from the same residential IP that a stock browser gets blocked on - the variable being the fingerprint, not the IP. The engine ships on two Chromium bases: a stable channel (149, matching the version most real users run, for best blend-in) and a latest channel (151, newest engine). Persona details (platform, WebGL vendor and renderer, canvas and audio seeds, timezone, languages, hardware concurrency, screen size, WebRTC policy) are configurable via --uxr-* command-line switches, defaulting to a coherent Windows identity; the source notes plainly that as of today these switches are readable via /proc/<pid>/cmdline on the host (one persona per process), with an IPC-delivered MaskConfig runtime that removes this limitation on the v2 roadmap.
The project frames bot detection as three structurally separate layers - driver and binary artifacts, CDP side-effects (like the Runtime.enable leak), and the fingerprint surface itself - and is explicit that Fortress only solves the third: it's built to be driven over raw CDP so the other two layers hold too, but a badly-configured control layer (e.g. leaving Runtime.enable on, or using chromedriver instead of raw CDP) can still expose the automation regardless of engine-level fingerprint correction.
When to use - and when NOT to
Use Fortress when scrapers or browser agents built on Playwright, Puppeteer, or raw CDP are getting blocked by fingerprint-based bot detection (Cloudflare, DataDome, Kasada, HUMAN, Akamai) and you want a drop-in engine swap rather than maintaining a stack of JavaScript stealth patches that self-reveal under scrutiny. The source frames it explicitly for legitimate automation, testing, and scraping of publicly available data, and asks users to respect each site's terms of service and applicable law - it's an engineering project, not a tool marketed for evading site policy.
Fortress alone doesn't fix an IP-based block: the source's own troubleshooting notes that most persistent blocking after switching to Fortress traces to a datacenter IP range being flagged before any page script runs, not the fingerprint - route egress through a residential or mobile proxy first. It also doesn't claim permanence: detection techniques keep moving, so the project ships a dated, reproducible detection gauntlet and a monthly Chromium rebase rather than promising to "pass everything forever" - the word "undetectable" is deliberately kept out of the repo.
Inputs and outputs
Install via pip, npm, Docker, or a portable extract-and-run bundle:
pip install tilion-fortress
# or: docker run --rm -p 9222:9222 tilion/fortress:latest
from tilion_fortress import Fortress
from playwright.sync_api import sync_playwright
with Fortress() as f:
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(f.cdp_url)
page = browser.new_page()
page.goto("https://bot.sannysoft.com")
Input is your existing Playwright, Puppeteer, or CDP automation code, unchanged aside from pointing at Fortress's CDP URL, plus optional --uxr-* persona flags. Output is a running stealth Chromium instance your code drives exactly as it would a normal browser - page loads, screenshots, extracted data - reproducible pass and fail results against the detector suite via tools/gauntlet.py, and all downloads SHA-256-verified against the release manifest automatically.
Integrations
Fortress works with anything speaking Playwright, Puppeteer, or raw CDP, including named integrations for browser-use, Crawl4AI, Stagehand, and LangChain's Playwright toolkit. A separate, beta Model Context Protocol (MCP) server (tilion-mcp) exposes 29 stealth-browsing tools - fetch_protected_page, extract_page, crawl_site, recon_site_apis, run_browser_task, screenshot_page, save_profile/load_profile, and more - directly to AI agents in Claude Desktop, Claude Code, Cursor, Cline, or Windsurf, installable with a one-line config addition or claude mcp add fortress -- tilion-mcp. The engine's own source (34 auditable single-purpose C++ patches in patches/) rebuilds from a stock Chromium checkout with one script, and the project publishes SHA-256 checksums and Docker image digests across four official distribution channels (GitHub, Docker Hub, PyPI, npm) for provenance verification.
Who it's for
Developers running scrapers or browser-driven AI agents against sites protected by fingerprint-based bot detection, who need a drop-in stealth engine that survives realm-based detection tricks a JavaScript patch layer can't, and who want an open, auditable, self-rebuildable engine rather than a closed-source commercial vendor's binary. It is licensed under BSD-3-Clause.
Source README
One browser engine to rule them all
Stealth Chromium engine
Fortress is a stealth Chromium engine that stops your scrapers and browser agents from getting blocked, with one line of code change. Bot detectors flag automation by reading the browser fingerprint; Fortress corrects that fingerprint inside Chromium's C++, so the browser presents as an ordinary Chrome install. Scrapers finish their runs, agents reach the pages they were sent to, and CreepJS, Sannysoft, BrowserScan, and live Cloudflare Turnstile all read it as human. Point your existing Playwright or Puppeteer at Fortress over CDP, and nothing else in your code changes.
Blink · V8 · BoringSSL patched in-tree · ANGLE / D3D11-backed WebGL · JA3/JA4-coherent TLS · monthly upstream rebase · reproducible, gauntlet-gated releases
34single-surfaceC++ patches |
0%CreepJSheadless / stealth |
across every |
BSD-3open engine,rebuild it yourself |

Unedited capture of the Fortress binary in a real window: it clears a live Cloudflare challenge, turns bot.sannysoft.com all green, then reads BrowserScan “Normal”. Reproduce with tools/gauntlet.py.
Native-code parityEvery spoofed getter is a C++ getter: |
Drop-in CDPnodriver-style raw CDP on |
Clears the gauntlet0% headless on CreepJS; Sannysoft, BrowserScan, and live Cloudflare Turnstile cleared, all as a stock Chrome install. |
Auditable patches34 small single-purpose diffs in |
Coherent by constructionReal V8, Blink, and BoringSSL keep engine, user-agent, and JA3/JA4 TLS shape in agreement: a Windows persona on a matching stack. |
Tunable personaOne binary, a coherence-checked Windows identity; |
What's new - 151.0.7908.0 · Engine Refresh
Released. Per-launch coherent personas, hardened.
- Per-locale keyboard (QWERTY / QWERTZ / AZERTY) · per-persona media devices · a full delivery-parity coherence pass.
- Platform ↔ GPU ↔ timezone ↔ language ↔ voices ↔ keyboard move as one coherent real device - on every launch, from a single binary.
- Native-code parity (
toString()stays[native code]), realm-invariant (main / worker / iframe) - coherence compiled into the browser, not patched in JavaScript.
pip install -U tilion-fortress # or: docker run --rm -p 9222:9222 tilion/fortress:latest
Contents
| What it is · Quick start | what it is, install, first script, AI-agent setup |
| The Fortress MCP | 29 stealth-browser tools for AI agents (Beta) |
| Why patch the engine, not the page | the self-revealing-JS thesis + the three detection layers |
| How Fortress compares | vs puppeteer-stealth · Camoufox · CloakBrowser · closed vendors |
| Proof: live-detector results | CreepJS / Sannysoft / BrowserScan / Cloudflare, with screenshots |
| Configure the persona | the --uxr-* fingerprint surface |
| Works with your stack | browser-use · Crawl4AI · Stagehand · LangChain |
| Build & verify | reproduce from source, verify provenance |
| Reference | troubleshooting · FAQ · roadmap · repo layout |
What it is
Fortress is a Chromium fork that spoofs the browser fingerprint from inside the engine. The surfaces bot detectors read (canvas, WebGL, audio, fonts, navigator, and about thirty more) are corrected in Chromium's C++, with no JavaScript patch layer sitting on top for a page to catch.
It ships as an ordinary browser binary that exposes a CDP endpoint. Point Playwright, Puppeteer, or any CDP client at it and your existing automation runs unchanged.
A JavaScript stealth patch leaves an extra layer the page can find: .toString() shows the override's source, and re-grabbing the same primitive from an iframe or worker reaches past it. Fortress corrects the surface in the engine instead, so navigator.vendor resolves to the real C++ getter, reports [native code], and reads the same from every realm. A page inspecting itself sees stock Chromium. That is why your automation gets through where it used to get flagged, and whatever blocking is left traces to your proxies and behavior rather than the browser. Why patch the engine, not the page covers the detection mechanics in full.
from tilion_fortress import Fortress
from playwright.sync_api import sync_playwright
with Fortress() as f: # launches the stealth engine on a CDP endpoint
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(f.cdp_url)
page = browser.new_page()
page.goto("https://bot.sannysoft.com")
page.screenshot(path="all-green.png")
import { Fortress } from "tilion-fortress";
import { chromium } from "playwright";
const f = await Fortress.launch(); // stealth engine on a CDP endpoint
const browser = await chromium.connectOverCDP(f.cdpUrl);
const page = await browser.newPage();
await page.goto("https://browserscan.net");
await browser.close();
await f.close();
The 12-second tour
One loop, all real captures: passes CreepJS / Sannysoft / BrowserScan / rebrowser → scrapes structured data over CDP → clears Akamai on aa.com · lowes · macys · kohls (same residential IP).
Real scraping, fully headless
Unedited captures of the Fortress engine driven over CDP. No stealth plugins, no JS patches: the fingerprint is corrected in the binary. Reproduce any of these with examples/scrape_demos.py.
Structured extraction: records build into typed JSON as each item is read.
![]() Auto-pagination: 30 quotes across 3 pages. |
![]() Deep detail crawl: UPC · price · tax · stock · reviews. |
Clears real Akamai - before / after
Same residential IP, same site (aa.com · Akamai Bot Manager). A stock/headless browser gets Access Denied (Reference #); Fortress loads the real page and Akamai issues its _abck sensor cookie - the Bot Manager accepts it as a real browser. The variable is the fingerprint, not the IP.
![]() lowes.com · blocked → cleared |
![]() macys.com · blocked → cleared |
![]() kohls.com · blocked → cleared |
Not a one-site fluke - same before/after on major Akamai-protected retailers, every run from the same residential IP.
Quick start
# Python / Node: prebuilt native binary auto-fetched (Linux x64 & Windows x64), SHA-256 verified
pip install tilion-fortress
npm install tilion-fortress
# Any OS via Docker: raw CDP on :9222 (~302 MB pull / 851 MB on disk, stripped single-layer)
docker run --rm -p 9222:9222 tilion/fortress:latest
# Portable bundle (extract-and-run, like a Chromium snapshot)
tar xzf tilion-fortress-linux-x64.tar.gz # Linux
./tilion-fortress/tilion --headless=new --remote-debugging-port=9222 --user-data-dir=/tmp/p
# Debian / Ubuntu
sudo apt install ./tilion-fortress_151.0.7908.0_amd64.deb && tilion https://example.com
Versions
Fortress ships on two Chromium bases - pick your trade-off between blend-in and currency:
| Channel | Chromium | When to use |
|---|---|---|
stable (default) |
149 | Recommended - matches the Chrome version the mass of real users run, so it blends in best |
latest |
151 | Newest engine (reports a version slightly ahead of stable) |
Fortress().start() # Python: stable (149) by default
Fortress(channel="latest").start() # opt into 151
await Fortress.launch(); // Node: stable (149) by default
await Fortress.launch({ channel: "latest" });
docker run --rm -p 9222:9222 tilion/fortress:149 # or :151
# or set FORTRESS_CHANNEL=latest for either SDK
Native binaries: Linux x64 (both versions) + Windows x64 (151); Windows-149 and macOS run via the Docker image.
Drop it into your AI agent
Fortress is the browser your agent drives: raw CDP on :9222, no stealth plugins to wire up. There are two ways in.
Option 1: open it pre-loaded in a chat assistant. One click; it reads our AGENTS.md and walks you through the whole setup:
Option 2: Copy for agent (everything, to your clipboard). Hit the copy icon at the top-right of the box below. It puts the entire setup context on your clipboard: what it is, install, connect, persona, and rules, all of AGENTS.md condensed. Paste it into Cursor, Claude Code, Copilot, ChatGPT, or any agent and it takes it from there:
You're setting up Fortress, an open-source STEALTH Chromium engine, for browser automation.
It corrects the browser fingerprint (canvas, WebGL, audio, fonts, navigator, +30 more) in Chromium's
C++ and exposes raw CDP on http://localhost:9222, a drop-in for Playwright/Puppeteer. Do NOT add
puppeteer-stealth or any JS fingerprint patching (it self-reveals and undoes Fortress).
LAUNCH (pick one; all expose CDP on http://localhost:9222):
Docker: docker run -d --rm -p 9222:9222 tilion/fortress:latest
Python: pip install tilion-fortress then from tilion_fortress import Fortress; f=Fortress(); f.start()
Node: npm install tilion-fortress then import {Fortress} from "tilion-fortress"; const f=await Fortress.launch()
CONNECT (keep my existing automation code):
Playwright(py): browser = p.chromium.connect_over_cdp("http://localhost:9222")
Playwright(js): const browser = await chromium.connectOverCDP("http://localhost:9222")
Puppeteer(js): const browser = await puppeteer.connect({ browserURL: "http://localhost:9222" })
browser-use / Crawl4AI / Stagehand / LangChain: point their CDP endpoint at http://localhost:9222
PERSONA (optional; default is a coherent Windows identity). Override any surface with --uxr-* flags:
--uxr-timezone=America/New_York --uxr-hw-concurrency=16 --uxr-languages=en-US,en
RULES:
1) Drive over raw CDP (:9222); don't spawn chromedriver.
2) Never pass --user-agent (use --uxr-ua-*); it desyncs UA vs UA-Client-Hints.
3) No puppeteer-stealth / undetected-chromedriver / JS fingerprint patches.
4) Blocked ~90% = my IP (datacenter), not the fingerprint. Use a residential/mobile proxy, then retry.
Now walk me through launching Fortress and wiring my automation to it.
Full guide: https://github.com/tiliondev/fortress/blob/main/AGENTS.md
The Fortress MCP - stealth browsing as agent tools Beta
Raw CDP is for code you write. The Fortress MCP is for agents that call tools: a Model Context Protocol server that hands Claude, Cursor, or any MCP client a stealth browser, so the moment a fetch is blocked it just calls a tool and gets the page. 29 tools, local and free - fetch_protected_page, extract_page, crawl_site, recon_site_apis, search_web, run_browser_task, save_profile, get_stealth_cdp_endpoint, and more.

Real, dated run against stockx.com (PerimeterX). A stock browser gets HTTP 403 - “Access denied”; an agent with the Fortress MCP returns clean JSON - same site, same prompt. Reproduce it from the framework repo.
Set it up in 30 seconds
Two runners - pick one. npx needs Python on PATH; pip installs it directly:
pip install "tilion[mcp]" # command: tilion-mcp
# —or, zero-install—
npx -y tilion-mcp # auto-runs the server via uv (no global install)
Claude Desktop - Settings → Developer → Edit Config (claude_desktop_config.json):
{ "mcpServers": { "fortress": { "command": "tilion-mcp" } } }
Prefer npx? Use "command": "npx", "args": ["-y", "tilion-mcp"]. Restart Claude, and the fortress tools appear.
Claude Code (CLI) - one line:
claude mcp add fortress -- tilion-mcp # or: claude mcp add fortress -- npx -y tilion-mcp
Cursor (~/.cursor/mcp.json) · Cline / Windsurf (VS Code → MCP servers) - same block:
{ "mcpServers": { "fortress": { "command": "tilion-mcp" } } }
Then just ask your agent - “get the price off this StockX page” - and it calls fetch_protected_page on its own.
What the agent gets
| tools | |
|---|---|
| Get blocked pages | fetch_protected_page · read_page · get_page_html · search_web |
| Structured data | extract_page (schema-aware) · extract_document (PDF/DOCX/XLSX) |
| Whole sites | crawl_site (auto-SPA) · recon_site_apis (find the private JSON API) |
| Drive a page | page_elements · click_button · fill_field · press_key · wait_for · evaluate_js |
| Multi-step flows | run_browser_task (login, paginate, infinite-scroll, checkout, …) |
| Capture / auth | screenshot_page · save_page · download_file · save_profile / load_profile |
| Bring your own | get_stealth_cdp_endpoint → a CDP url for Playwright / Puppeteer / browser-use |
Tools are annotated (reads auto-approve, writes gate), pre-warmed on startup (~100 ms first call), concurrency-safe, and timeout- and SSRF-guarded. A hosted endpoint with residential egress is coming soon.
→ Full 29-tool table, benchmarks, and the agent skill: mcp/
Why patch the engine, not the page
The usual approach patches navigator.webdriver, spoofs the WebGL vendor, and overrides navigator.plugins from script. CreepJS and similar detectors still flag it, and the reason is structural, not one more property left uncovered. A JavaScript spoof is a function standing where a native one belongs. Detectors set the returned value aside and interrogate whether the thing returning it is native:
| The tell | Why it catches a JS spoof |
|---|---|
toString self-reveal |
A native method stringifies to function get vendor() { [native code] }; an override stringifies to its own source, so one .toString() catches it. |
Descriptor and hasOwnProperty |
getOwnPropertyDescriptor exposes redefined props, and hasOwnProperty('toString') returns true on a tampered function where a native one returns false. |
failsTypeError |
Native getters throw a specific TypeError on the wrong this; a naive shim stays quiet, and the silence is the signal. |
Realm re-acquisition is the one that defeats every main-world patch. A detector grabs a pristine primitive from another realm and turns it on your function:
const iframe = document.createElement('iframe'); document.body.appendChild(iframe);
const realToString = iframe.contentWindow.Function.prototype.toString;
realToString.call(navigator.__lookupGetter__('vendor')); // returns your source code. Caught.
Your main-world patch lives in a different realm from that iframe. The same trap fires from a Web Worker, a thread your main-thread shim runs beside rather than inside.
Fortress has no such layer. The getter for navigator.vendor is the C++ getter: it reports [native code] because it is native code, identical across every realm. Camoufox puts it well: "there is no JavaScript hijacking to be detected." Fortress applies the same idea to V8 and Blink in place of Gecko.
The three layers of bot detection, and where Fortress fits
Modern anti-bots (Cloudflare, DataDome, Kasada, HUMAN, Akamai) read three structurally different surfaces, in three separate places. One tool rarely fixes all three:
| Layer | The tells | Where the fix lives | Fortress |
|---|---|---|---|
| A: driver / binary artifacts | cdc_ ChromeDriver vars, WebDriver protocol surface |
Drive raw CDP, skip chromedriver | |
| B: CDP side-effects | Runtime.enable leaks via sourceURL + init-script footprints, however clean the binary is |
The control / CDP-client layer: hold back Runtime.enable, use Runtime.addBinding + isolated worlds |
|
| C: fingerprint surface | canvas, WebGL, audio, fonts, navigator, across main frame, iframes, workers | The engine (C++), because JS overrides self-reveal |
Fortress is the Layer-C engine, built to be driven so A and B hold too. The binary alone leaves the CDP channel open. That part is on the control layer, and pretending otherwise is how you get caught.
How Fortress compares
| Stock Playwright | puppeteer-extra-stealth | undetected-chromedriver | Camoufox | CloakBrowser | Fortress | |
|---|---|---|---|---|---|---|
| Spoof layer | none | JS injection | CDP/config patch | C++ engine | C++ engine | C++ engine |
toString yields [native code] |
n/a | n/a | ||||
| Survives realm re-acquisition (iframe/worker) | ||||||
No Runtime.enable leak |
||||||
| Engine = Chrome / V8 (majority traffic) | ||||||
| Coherent Chromium TLS shape | ||||||
| Fully open-source engine | ||||||
| Published, auditable patch series | n/a | n/a | n/a | |||
| Reproducible from-source build | n/a | n/a | n/a | |||
| States its own limits | n/a | n/a | n/a |
Fortress builds on real prior art: fingerprint-chromium, ChromiumFish, and CloakBrowser came first, and commercial vendors (Multilogin, Kameleo, GoLogin, AdsPower, Browserbase, Surfsky) recompile Chromium behind closed source. Most of that work stays closed: the paywalled forks hand you a binary and ask you to trust it, and the vendors keep their patches in-house.
Fortress goes the other way, because a stealth engine only stays useful when the people relying on it can see how it works. Every surface correction lives in patches/ as a small, single-purpose diff you can read in a minute, and the whole engine rebuilds from source with one script. When a detector finds a new tell, you trace the fix, patch it, and send it back. That feedback loop is the point, and it only works while the engine stays open enough to read, extend, and rebuild.
Proof: live-detector results
Reproduce any row with tools/gauntlet.py --bundle ./tilion-fortress. Verified against live detectors; re-run dated in docs/GAUNTLET_RESULTS.md.
| Suite | Stock Chromium | Fortress |
|---|---|---|
| CreepJS | flagged headless | 0% headless · 0% stealth, worker signals coherent |
| bot.sannysoft.com | red rows | 0 failed · WebDriver Advanced passed · WebGL = NVIDIA RTX 3060 / ANGLE D3D11 |
| browserscan.net | bot detected | “No bots detected, could be a human” |
| rebrowser bot-detector | Runtime.enable LEAK |
no leak · webdriver=false · clean init-scripts (raw CDP) |
| Cloudflare Turnstile | blocked | bypassed: a human click cleared a live challenge (headed, datacenter IP) |
Proof: real, unedited screenshots
| BrowserScan | CreepJS | Cloudflare |
|---|---|---|
![]() |
![]() |
![]() |
Configure the persona
The binary carries zero brand strings; the launcher applies a coherent default Windows persona. Override any surface with --uxr-* switches, or set TILION_NO_DEFAULTS=1 for a bare launch.
--uxr-platform / --uxr-ua-platform / --uxr-ua-os / --uxr-ua-arch / --uxr-ua-bitness
--uxr-ua-platform-version / --uxr-ua-brand / --uxr-hw-concurrency / --uxr-device-memory
--uxr-webgl-vendor / --uxr-webgl-renderer / --uxr-webgl-fullparams
--uxr-canvas-seed / --uxr-audio-seed / --uxr-timezone / --uxr-languages
--uxr-screen-width / --uxr-screen-height / --uxr-webrtc-policy=disable_non_proxied_udp
| Env var | Purpose |
|---|---|
TILION_NO_DEFAULTS=1 |
Skip the default persona (bare launch) |
TILION_TZ / TILION_LANG |
Quick timezone / language override |
Works with your stack
Fortress exposes raw CDP on :9222, so it drops in under anything that speaks Playwright, Puppeteer, or CDP.
| Framework | Connect via |
|---|---|
| browser-use (~70k stars) | cdp_url="http://localhost:9222" |
| Crawl4AI (~58k stars) | CDP endpoint |
| Stagehand (~21k stars) | connectOverCDP |
| LangChain Playwright toolkit | Playwright CDP |
| Playwright / Puppeteer (Python & JS) | connect_over_cdp / connect |
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp("http://localhost:9222") # Fortress under the hood
Build & verify
Reproduce from source
export CHROMIUM_VERSION=$(cat CHROMIUM_VERSION)
build/build.sh # depot_tools, sync the tag, apply patches, gn gen, ninja
build/rebase-monthly.sh 152.0.XXXX.0 # bump + 3-way apply + rebuild + gauntlet-gate
Output: out/Fortress/chrome. The fork is 34 small single-surface patches (patches/), so most re-apply cleanly across upstream releases; the gauntlet then gates the release on any regression.
| Platform | Status |
|---|---|
| Linux x64 (native) · Windows x64 (native) · any OS via Docker | |
Code-signed installers · macOS .app · linux/arm64 |
in progress |
Verify it's really ours
Fortress ships from four official channels. Treat anything else as untrusted:
| Official source | |
|---|---|
| Source | github.com/tiliondev/fortress |
| Docker | tilion/fortress |
| Python | tilion-fortress |
| Node | tilion-fortress |
Verify a download. Every release ships SHA256SUMS, and the pip/npm SDKs run this for you on install:
BASE=https://github.com/tiliondev/fortress/releases/download/v151.0.7908.0
curl -LO $BASE/tilion-fortress-linux-x64.tar.gz
curl -Ls $BASE/SHA256SUMS | sha256sum -c --ignore-missing # -> OK
Verify the Docker image by digest (not just the tag):
docker pull tilion/fortress:151.0.7908.0
docker inspect --format '{{index .RepoDigests 0}}' tilion/fortress:151.0.7908.0
# compare the printed sha256:... against the digest in the GitHub Release notes
Or trust nothing and rebuild it. The whole fork is 34 readable patches in patches/; build/build.sh reproduces the binary from Chromium source, so you can diff what you built against what we ship.
Reference
Troubleshooting
Still blocked on Cloudflare, DataDome, or Kasada. Most of the time this is your IP, not your fingerprint: a datacenter range gets flagged before any page script runs. Route egress through residential or mobile proxies and retry; if it clears, the fingerprint was fine.
The fingerprint looks off on a Linux host. The default persona is Windows, but the TLS shape and some OS-facing signals follow the machine underneath. Match the persona to your egress OS, or set the relevant --uxr-* flags so the OS story agrees with where the traffic leaves from.
macOS pulls a Docker image. Native Linux + Windows binaries ship today; macOS still runs Fortress through the official Docker image (tilion/fortress). Install Docker Desktop, or run on Linux/Windows x64 for the native binary.
The persona shows up in /proc/<pid>/cmdline. The --uxr-* flags are readable by other processes on the host, one persona per launch. Until the runtime MaskConfig lands, keep one persona per process and avoid sharing the host with untrusted code.
A detector flags something the gauntlet passes. Detection moves. Confirm you're on the current Chromium rebase, then open an issue with the test page. That page becomes the next patch.
FAQ
Is this legal? Fortress is a browser engineering project for legitimate automation, testing, and scraping of publicly available data. Respect each site's ToS and the law in your jurisdiction.
Is it really free? Yes. BSD-3, fully open, and self-hostable. The patch series is published, so you can build the current engine from source yourself.
Why not just use puppeteer-stealth or undetected-chromedriver? They patch the JS/CDP layer after the page can inspect the browser, so they self-reveal via toString and realm re-acquisition. Fortress moves the spoof into C++, where the page finds native code. (See "Why patch the engine, not the page.")
How is this different from Camoufox? Same C++-interception idea. Camoufox forks Firefox (~3% of traffic, a standing anomaly) while Fortress forks Chromium and V8 (the majority engine), so a Chrome user-agent is coherent by construction.
Will it pass everything forever? No. Detection keeps moving, so we ship a dated, reproducible gauntlet and a monthly Chromium rebase; you can always see exactly what passes today.
Roadmap
- Runtime JSON config into a C++
MaskConfig(one binary, many coherent fingerprints, nothing on the command line) - First-party MCP server plus Puppeteer / raw-CDP SDKs (drop-in for AI agents)
- Code-signed Windows
.exeand macOS.app -
linux/arm64Docker image - Migrate
patches/to Brave-stylechromium_src/overrides - Published reCAPTCHA v3 / DataDome / Kasada benchmark rows (dated, reproducible)
Repo layout
patches/ 34 per-surface C++ patches (+ series), the source of truth for the fork
build/ args.gn, build.sh, apply-patches.sh, rebase-monthly.sh, windows/, macos/
packaging/ tilion launcher, fonts.conf, Dockerfile, .deb + bundle builders
fonts/ 33 metric-compatible Windows-named fonts (incl. color emoji)
sdk/ python + node (tilion-fortress) prebuilt-binary SDKs
tools/ gauntlet.py, the CreepJS / Sannysoft / BrowserScan CI gate
docs/ GAUNTLET_RESULTS, BUILD_NATIVE, BENCHMARK
What's next
Staying current
Detection keeps moving, so a stealth engine is only as good as its last rebase. Fortress tracks the latest Chromium monthly, re-runs the full gauntlet, and ships a patch whenever a detector finds a new tell, so what you run keeps matching what a real Chrome install looks like. Watch the releases to follow the v2 MaskConfig work, or star the repo if it's useful to you.
Stealth you can read, rebuild, and run yourself.
FAQ
Common questions
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.







