tiliondev/fortress

Stealth Chromium engine that stops scrapers and browser agents from getting blocked, with one line of code change.

Python

706

94 commits

updated Sep 30, 2026

See the code

See what people are saying

README

Fortress

One browser engine to rule them all

Stealth Chromium engine · v3 (Chromium 153)

Chromium Docker pulls Discord
Copy for agent llms.txt MCP server npm tilion-mcp

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. With v3, every launch is a different coherent machine, so a fleet of sessions looks like a crowd of real users.

Headless, on datacenter IPs, with no proxies, Fortress served 86.4% of 92 protected pages; the next best stack (Camoufox) served 74.5%.

Blink · V8 · BoringSSL patched in-tree · ANGLE / D3D11-backed WebGL · JA3/JA4-coherent TLS · monthly upstream rebase · reproducible, gauntlet-gated releases

81

single-surface
C++ patches

0%

CreepJS
headless / stealth

48/48

distinct across a
64-clone fleet

[native code]

across every
realm

Fortress clearing a live Cloudflare challenge, then passing sannysoft and BrowserScan

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 parity

Every spoofed getter is a C++ getter: toString returns [native code], realm-invariant across main frame, iframes, and Web Workers.

Drop-in CDP

nodriver-style raw CDP on :9222, with no Runtime.enable leak. Keep Playwright, Puppeteer, or any CDP client; swap the browser, keep your code.

Clears the gauntlet

0% headless on CreepJS; Sannysoft, BrowserScan, and live Cloudflare Turnstile cleared, all as a stock Chrome install.

IPC persona graph

The persona reaches the renderer over IPC into a process-global config: zero command-line footprint, per-context isolation, thousands of coherent identities from one binary. --uxr-* switches stay as explicit overrides.

Fleet re-identity

on_restore re-keys the whole persona (canvas · audio · WebGL · GPU · screen · UA · TLS) from a snapshot with no relaunch. 64 clones come up 48/48 distinct.

Coherent by construction

Real V8, Blink, and BoringSSL keep engine, user-agent, and JA3/JA4 TLS shape in agreement, and WebGL / WebGPU agree with the persona GPU to the parameter level: limits, precision and extensions as well as the renderer string.


What's new: v3 · 153.0.8010.36 · Fleet Engine

v3 turns Fortress from a coherent single persona into a coherent fleet engine: 87 commits on patches/, a Chromium 151/152 to 153 rebase, and the changes below.

  • IPC persona graph, shipped. The persona is delivered to the renderer over IPC into a process-global config: zero command-line footprint and per-context identity isolation, so multiple sessions or accounts in one browser never collapse into a single linkable identity.
  • on_restore fleet re-identity. On snapshot resume the entire persona re-keys in place (RNG, canvas/audio, GPU/screen/UA, network and TLS state) with no relaunch, confirmed by a closed-loop per-surface ack. A 64-clone fleet comes up 48/48 distinct, stress-gated to 500 clones.
  • Byte-exact canvas and audio. Canvas noise is idempotent and byte-exact across getImageData / toDataURL / toBlob (and OffscreenCanvas), edge-gated to anti-aliased pixels only, with 64-bit domain-separated seeds and a fail-closed RNG (a zero or absent seed disables the noise; no golden constant can leak). WebGL/WebGL2 readback, including PBO readPixels, routes through the same edge-gated path. Audio farble is value-keyed: identical inputs give identical outputs, fresh per persona yet internally stable.
  • Coherent WebGL and WebGPU. Both agree with the persona GPU at the parameter level: limits, precision, extensions, and navigator.gpu adapter identity all match a real hardware GPU, never the software backend. Verified live.
  • A real font substrate. 154 distinct bundled font families with real metric clones and per-persona metrics; enumeration is gated to the persona so host fonts are never visible.
  • Coherence everywhere. About thirty specific tells closed, each with a coherence rule in place of a spoof: @media matches screen and DPR, color-gamut and dynamic-range, jsHeapSizeLimit matches deviceMemory, prefers-color-scheme per persona (about a third dark), Windows system fonts, Device-Memory client hint on the navigation request path, WebAuthn isUVPAA() per persona, macOS zero-width overlay scrollbars, fail-closed WebRTC (no real-IP leak on a bare launch), pointer/hover/maxTouchPoints pinned, OS-appropriate speechSynthesis voices, and coherent Mac personas (Apple arch, core and RAM SKUs, a MacBook that is not permanently plugged in).
  • Recorded-human mouse motion. An engine-native trajectory engine backed by a bank of ~10k recorded-human paths (SapiMouse) paces the cursor with organic velocity and micro-jitter, a ~16× more human motion signal than a raw driver (humanness gap ~4.3 vs ~70). Speed-proportional getCoalescedEvents batching and per-persona realtime AudioContext timing round out the behavioral seed.
  • Widevine EME, shipped. The real Widevine CDM is bundled and enabled, so requestMediaKeySystemAccess('com.widevine.alpha') resolves exactly as it does in a genuine Google Chrome. The DRM gap the roadmap flagged is closed.
  • Native on more platforms. Windows x64 ships native (.zip + tillion.cmd); Linux arm64, x86 and armhf tarballs are rolling out; the macOS .app builds from source.
pip install -U tilion-fortress       # or:  docker run --rm -p 9222:9222 tilion/fortress:latest

Full release notes

Contents

What it is · Quick startwhat it is, install, first script, native builds, AI-agent setup
Every session a distinct machine40 back-to-back launches, 40 different coherent machines
The Fortress MCP29 stealth-browser tools for AI agents (Beta)
Why patch the engine, not the pagethe self-revealing-JS thesis + the four detection layers
How Fortress comparesvs puppeteer-stealth · Camoufox · antidetect apps · v2 to v3
Proof: live-detector resultsv3 live results, CreepJS / Sannysoft / BrowserScan / WebGL / WebGPU, with screenshots
Benchmark: 92 protected sitesFortress vs seven stacks, all headless, 1,584 fresh machines, datacenter IPs
Configure the personathe IPC persona graph and the --uxr-* fingerprint surface
Works with your stackbrowser-use · Crawl4AI · Stagehand · LangChain
Build & verifybuild from source, platforms, verify provenance
Referencetroubleshooting · 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, WebGPU, audio, fonts, navigator, Client-Hints, and about forty 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.

With v3 the persona is no longer a single fixed identity. Each launch mints a fresh, internally coherent machine (GPU, screen, cores, timezone, language, fonts, all in agreement), delivered to the renderer over IPC, so nothing shows on the command line and every BrowserContext can hold its own identity.

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();

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.

Fortress extracting books.toscrape.com into typed JSON records live over CDP

Structured extraction: records build into typed JSON as each item is read.

Fortress auto-paginating across pages of quotes.toscrape.com
Auto-pagination: 30 quotes across 3 pages.
Fortress deep-crawling a product detail page
Deep detail crawl: UPC · price · tax · stock · reviews.

Clears real Akamai, before and after

Before: a stock browser is blocked by Akamai on aa.com with Access Denied. After: Fortress loads the real page and Akamai's sensor accepts it.

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 IP is the same in both runs, so the variable is the fingerprint.

The same before and after holds on lowes.com, macys.com and kohls.com, every run from the same residential IP.


Every session a distinct machine

40 back-to-back launches of the same v3 binary, headless. Each one is a different, internally coherent machine, and no two sessions shared a canvas or audio fingerprint:

Across 40 launchesDistinct
Canvas fingerprint40 / 40
Audio fingerprint40 / 40
GPU (WebGL renderer)25
Screen resolution14
Timezone (geo-coherent with language)23
Platform mix31 Windows · 9 macOS
#PlatformGPUScreenCoresTimezoneLangCanvas
1Win32Intel UHD Graphics1536×8642Europe/Warsawpl-PL78ae7500
2Win32Intel HD Graphics 46001920×10804Asia/Seoulko-KR6dcc5a20
3Win32Intel UHD Graphics 6201920×108012America/Buenos_Aireses-ARdeddd100
4Win32Intel UHD Graphics1920×10806Europe/Parisfr-FR643e08d8
5Win32Intel UHD Graphics2560×14408America/New_Yorken-US56e37548
6Win32AMD Radeon 860M1707×9608Asia/Makassarid-IDbbaacff0
7Win32Intel UHD Graphics1536×8648America/Sao_Paulopt-BRb1d2ec50
8Win32Intel Iris Xe Graphics2560×144012Asia/Calcuttahi-IN89d61698
9MacIntelApple M1 Pro (Metal)1728×111710America/Chicagoen-USa975f300
10MacIntelApple M4 (Metal)1512×98210America/Denveren-US9f03f6f8

…30 more, all distinct. Every row is a coherent machine: GPU, screen, core count, timezone and language agree (Windows with Intel/AMD/D3D11, macOS with Apple/Metal; timezone, language and region matched). Reproduce with tools/gauntlet.py --runs 40.


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
docker run --rm -p 9222:9222 tilion/fortress:latest

# Portable tarball (Linux x64 / arm64 / x86 / armhf): use it like a Chromium snapshot
tar xzf fortress-v153-linux-x64.tar.gz
./fortress-v153/tilion https://example.com
./fortress-v153/tilion --headless=new --remote-debugging-port=9222 --user-data-dir=/tmp/p

# Native Windows x64: unzip and launch via the .cmd (raw CDP, same --uxr-* overrides)
#   fortress-v153-win-x64\tillion.cmd --headless=new --remote-debugging-port=9222 --user-data-dir=C:\tmp\p

# Debian / Ubuntu
sudo apt install ./tilion-fortress_153.0.8010.36_amd64.deb && tilion https://example.com

[!TIP] The SDK ships the compiled v3 engine, ready to run, and downloads are SHA-256-verified against the release SHA256SUMS automatically. The first-generation patch series is public to read and rebuild.

Free for developers, unlock in one command

Fortress v3 is free for developers, with no machine, session, or concurrency caps. One ten-second step turns a fresh install into the full engine: sign in with GitHub, Google, or email so we know who our developers are.

tilion activate      # opens your browser, sign in, done. Key saved to ~/.tilion/license.jwt

The key is verified offline after that; Fortress never phones home or reports usage. Skip activation and Fortress still runs: it prints one line and falls back to the public first-generation engine until you activate.

DevelopersFree and unlimited. Each developer gets a unique key that auto-refreshes, so you activate once.
CI, Docker, AI agents (no browser)Set TILION_LICENSE_KEY=<key> in the environment, or run tilion activate --token <key>. One key covers a whole fleet.
EnterpriseA production key from tilion.com/pricing for your org's fleet. Drop it in TILION_LICENSE_KEY.

The tilion CLI

pip install tilion-fortress (or npm install tilion-fortress) puts the tilion command on your PATH. The portable tarball and the Docker image bundle the same activator, so it works everywhere with no extra install.

CommandWhat it does
tilion activateUnlock v3 with a one-click device flow (browser sign-in)
tilion activate --headlessSame flow, prints the URL and code (SSH or remote hosts)
tilion activate --token <jwt>Save a key with no browser (CI, agents)
tilion license status [--json]Show the saved key's tier and expiry (--json for scripts)
tilion license refreshRe-issue the key before it expires
tilion license logoutRemove the saved key and drop back to v1
tilion get [platform]Download the build for this host. tilion get list shows all: linux-x64, arm64, armhf, x86, win-x64, mac-arm64, mac-x64
tilion mcpRun the Fortress MCP server, stealth browsing as agent tools
tilion [--port N]Launch the engine and print the CDP endpoint

Native builds: one engine, every platform

The same v3 engine ships as a native binary per platform, no container hop, from Releases. Each launch mints a fresh, coherent machine for that OS: a Windows build draws Windows personas (Win32 · D3D11 GPUs), verified 10/10.

PlatformPackageWidevineStatus
Linux x64portable tarball · Docker · pip / npmyesyes shipping
Windows x64portable .zip + tillion.cmd launcheryesyes shipping
Linux arm64portable tarball (Graviton, dense cloud fleets)noyes shipping
Linux x86 · armhfportable tarball (32-bit x86 / ARM)noyes shipping
macOS (arm64 / x64).app, built from sourceyesbuild from source

All four Linux builds (x64 · arm64 · armhf · x86) and Windows x64 carry the same license gate; the 32-bit and arm64 cross-builds ship without the bundled Widevine CDM. macOS builds from source with Widevine on.

Why native matters: a Windows persona on a real Windows host emits a genuine Windows TLS/JA4T and OS story for free (DirectWrite fonts, real Widevine DRM), and a Mac persona on real macOS gets HEVC decode, Apple Color Emoji, and the right DRM: the last substrate leaks a Linux host cannot fully paper over. One engine, every OS, the same 48/48-distinct fleet.

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:

Ask ChatGPT Ask Claude Ask Gemini Copy for agent

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, a STEALTH Chromium engine, for browser automation.
It corrects the browser fingerprint (canvas, WebGL, WebGPU, audio, fonts, navigator, +40 more) in
Chromium's C++ and exposes raw CDP on http://localhost:9222, a drop-in for Playwright/Puppeteer.
Every launch is a fresh, coherent machine. 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; the default is a fresh coherent identity per launch, delivered over IPC).
Pin or 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.

Same site, same prompt: a vanilla browser is blocked by PerimeterX while an agent with the Fortress MCP returns clean JSON

Real, dated run against stockx.com (PerimeterX). A stock browser gets HTTP 403, “Access denied”; an agent with the Fortress MCP returns clean JSON from the same site and the 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" } } }

For 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 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 pagesfetch_protected_page · read_page · get_page_html · search_web
Structured dataextract_page (schema-aware) · extract_document (PDF/DOCX/XLSX)
Whole sitescrawl_site (auto-SPA) · recon_site_apis (find the private JSON API)
Drive a pagepage_elements · click_button · fill_field · press_key · wait_for · evaluate_js
Multi-step flowsrun_browser_task (login, paginate, infinite-scroll, checkout, …)
Capture / authscreenshot_page · save_page · download_file · save_profile / load_profile
Bring your ownget_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, Tilion Cloud, is on a waitlist at tilion.com.

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: 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 tellWhy it catches a JS spoof
toString self-revealA native method stringifies to function get vendor() { [native code] }; an override stringifies to its own source, so one .toString() catches it.
Descriptor and hasOwnPropertygetOwnPropertyDescriptor exposes redefined props, and hasOwnProperty('toString') returns true on a tampered function where a native one returns false.
failsTypeErrorNative 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, and, unlike a Firefox fork, emits a Chromium/V8 fingerprint that matches a Chrome user-agent by construction.

The four layers of bot detection, and where Fortress fits

Modern anti-bots (Cloudflare, DataDome, Kasada, HUMAN, Akamai) read structurally different surfaces in separate places. One tool rarely fixes all of them:

LayerThe tellsWhere the fix livesFortress
A: driver / binary artifactscdc_ ChromeDriver vars, WebDriver protocol surfaceDrive raw CDP, skip chromedriveryes built to be driven this way
B: CDP side-effectsRuntime.enable leaks via sourceURL + init-script footprints, however clean the binary isThe control / CDP-client layer: hold back Runtime.enable, use Runtime.addBinding + isolated worldsyes no leak (verified on rebrowser)
C: fingerprint surfacecanvas, WebGL, WebGPU, audio, fonts, navigator, across main frame, iframes, workersThe engine (C++), because JS overrides self-revealyes this is Fortress
D: network / IP egressdatacenter ASN, IP reputation, geo-vs-persona mismatchYour proxies (residential / mobile), or the Tilion hosted versionpartial bring your own with the self-hosted engine; available on the Tilion hosted version, waitlist at tilion.com

Fortress is the Layer-C engine, built to be driven so A and B hold too, and to stay geo-coherent (timezone, locale and WebRTC follow the egress) once you supply a Layer-D IP. The binary alone leaves the CDP channel open and the IP question unanswered.


How Fortress compares

Stock Playwrightpuppeteer-extra-stealthundetected-chromedriverCamoufoxAntidetect apps¹Fortress v3
Spoof layernoneJS injectionCDP/config patchC++ engine (Firefox)C++ engineC++ engine (Chromium)
toString yields [native code]n/anon/ayesyesyes
Survives realm re-acquisition (iframe/worker)nononoyesyesyes
No Runtime.enable leaknonopartialyesyesyes
Engine = Chrome / V8 (majority traffic)yesyesyesno Firefoxyesyes
Coherent Chromium TLS shapeyesyesyesno Firefoxyesyes
Parameter-level WebGL + WebGPU coherencen/anonopartial no WebGPUvariesyes
Per-launch fleet dynamism (distinct machine)nononoyesno persistent by designyes 48/48
Full-persona re-key on snapshot resumenononononoyes on_restore
Native single-surface C++ patch enginen/an/an/ayesno closedyes 81 patches
States its own limitsn/an/an/ayesnoyes

¹ Multilogin / GoLogin / Dolphin / Nstbrowser: closed-source engine patches, persistent identity by design, bundled residential egress (their Layer-D advantage over Fortress).

Fortress builds on real prior art: fingerprint-chromium, ChromiumFish, and CloakBrowser came first, and commercial vendors recompile Chromium behind closed source. Native per-surface control is not unique to Fortress: Camoufox, Multilogin, GoLogin and Dolphin all patch engine C++.

Honest positioning. Fortress's real edge is the combination: the broadest native per-surface set on a Chromium base, fleet dynamism plus on_restore re-identity that no competitor matches, and coherence discipline (gated, byte-exact, parameter-level). Where Fortress is behind: IP egress (bring your own; the #1 real-world blocker) and behavioral humanization (mouse motion shipped; keystroke and scroll variance next).

v2 to v3 at a glance

v2 is the published Chromium-151/152 engine: a coherent single persona, command-line personas, renderer-string-only WebGL. v3 is the current build. The difference is 87 commits:

Axisv2v3
Chromium base151 / 152153.0.8010.36
Single-surface C++ patches~3481
Persona transportcommand line, per launch, host-readableIPC-delivered, in-process, per-context isolated
Multi-identity in one binaryone persona per processper-BrowserContext isolation
Snapshot resumerelaunch to change identityon_restore full-persona re-key, no relaunch
Fleet distinctnessnot measured48/48 across 64 clones (500-clone stress-gated)
Canvas noiseseededidempotent, byte-exact cross-path, 64-bit seeds, fail-closed
WebGLrenderer string only (incoherent numbers)parameter-level limits + extensions + string, per backend
WebGPUabsent / software-backend tellcoherent with the persona GPU + per-clone variation
Fontsmetric substitutes (~33)154-family substrate, enumeration gated to the persona
Client-HintsUA basicsfull Sec-CH-UA + Device-Memory on the navigation path
prefers-color-schemefleet-uniformper persona (about a third dark)
Mouse motionraw driverrecorded-human trajectories (~10k paths, ~16× more human)
DRMno Widevinereal Widevine CDM bundled, EME verified

Proof: live-detector results

Reproduce any row with tools/gauntlet.py --bundle ./fortress-v153. Verified against live detectors; re-run dated in docs/GAUNTLET_RESULTS.md.

Real capture of the v3 binary (Chromium 153) run headless from a datacenter IP. Persona on this launch: Win32 · Chrome/153 · WebGL ANGLE (Intel HD Graphics Family, Direct3D11) · Europe/Rome · it-IT · 2-core / 16 GB, and a different coherent machine on the next launch (NVIDIA/Copenhagen, AMD, Apple…). That per-launch distinctness is the point; see Every session a distinct machine.

SuiteStock ChromiumFortress v3
CreepJSflagged headless0% headless · 0% stealth, worker signals coherent
bot.sannysoft.comred rows0 failed · every intoli + PHANTOM_/HEADCHR_ check green · navigator.webdriver undefined
browserscan.netbot detected“Normal: No bots detected, could be a human” · WebDriver / Selenium / PhantomJS / Headless / CDP / DevTool all Normal
BrowserLeaks · WebGLSwiftShader leakUnmasked ANGLE (Intel HD Graphics, Direct3D11) with fully coherent D3D11 parameters
WebGPU (secure context)software-backend tellnavigator.gpu coherent with the persona GPU: adapter identity, limits, subgroup sizes (verified)
AmIUnique · pixelscan · ipheyautomation flagsconsistent, no automation flags
rebrowser bot-detectorRuntime.enable LEAKno leak · webdriver=false · clean init-scripts (raw CDP)
64-clone fleetone shared identity48/48 distinct canvas / audio / Math.random (fleet gate)
Cloudflare Turnstileblockedcleared: a human click cleared a live challenge (headed, datacenter IP)
   
Detection results, v2 to v3

The v3 column is live-verified on the current binary. The v2 column is the prior Chromium-151/152 build, characterized from the v2 to v3 change set; it is not a re-run of the retired binary.

Suite / surfaceFortress v2 (prior)Fortress v3 (current)
bot.sannysoft.compassed the base panel0 failed (verified)
CreepJScoherence tells to deep lie-checks0% headless, worker signals coherent (verified)
browserscan.netnot run“Normal: No bots detected” (verified)
WebGLrenderer string only; the numbers did not match the claimed GPUparameter-level coherent with the persona GPU
WebGPUabsent / software-backend tellcoherent with the persona GPU (verified)
Canvasdouble-noise, cross-path hash mismatchbyte-exact across getImageData / toDataURL / toBlob
measureText / fontsstable metrics; ~33 fontsper-persona metrics; 154-family substrate
64-clone fleet~1 shared identity48/48 distinct canvas / audio / Math.random (gate)
Substrate leaksseveral host/OS leaks across GPU, fonts, memory and displayall closed
on_restore resumerelaunch to re-identifyfull-persona re-key, no relaunch (500-clone stress-gated)
Persona transportcommand line, host-readable, one per launchIPC-delivered, per-context isolated
rebrowser bot-detectornot runno Runtime.enable leak · webdriver=false · clean init-scripts

Benchmark: 92 protected sites, eight stacks

Method, target list and checker probes in docs/BENCHMARK.md. Run on 2026-09-28 from US-East cloud machines on datacenter IPs with no proxies. Every tool ran headless.

Fortress (build v153.0.8010.36-linux-3) and seven other browser stacks loaded 92 protected sites: Cloudflare, DataDome, Akamai, HUMAN, Kasada, Imperva, AWS WAF, Arkose, reCAPTCHA, twenty login pages and five controls. Every tool loaded every target twice, each time on a new cloud machine that was destroyed afterwards (1,584 machines, 1,472 scored runs). A run counts as served only if the real page loaded with no challenge or deny page showing. No clicks, no typing, no captcha solver. Fortress was driven through Tilion.fetch, the same call the MCP tool uses.

StackServedn95% range
Fortress86.4%159/18481 to 91
Camoufox74.5%137/18468 to 80
puppeteer-extra + stealth72.3%133/18465 to 78
Brave36.4%67/18430 to 44
nodriver35.9%66/18429 to 43
stock Chrome34.2%63/18428 to 41
patchright34.2%63/18428 to 41
undetected-chromedriver33.7%62/18427 to 41

The range is the 95% Wilson interval. Fortress's interval does not overlap the next stack's. Fortress got 2/2 on ten sites where neither Camoufox nor puppeteer-stealth got more than 1/2 (WSJ, Marriott, Macy's, easyJet, PayPal sign-in, Tripadvisor, Monster, Zoopla, datadome.co, AutoZone); there is no site where Fortress got 0/2 and either of them got 2/2. nodriver, undetected-chromedriver and patchright served 62 to 66 pages, the same range as stock Chrome; run headless, all three send HeadlessChrome/153 in the user agent. Fortress's failure rate (13.6%) is about half of Camoufox's (25.5%) and one fifth of the Chrome-based tools' (64 to 66%).

Repeat runThe same headless design was run twice on the same day. Fortress served 85.3% and 86.4%; stock Chrome and Brave, identical in both runs, moved by 1 to 3 points.
Fingerprint checkersFortress and Camoufox had zero failures on bot.sannysoft.com, an all-green rebrowser bot detector, 0% headless on CreepJS and "Normal" on BrowserScan. Stock Chrome and Brave read "Robot" on BrowserScan and 67% headless on CreepJS.

Configure the persona

The binary carries zero brand strings. The launcher mints a coherent persona and delivers it to the renderer over IPC: no command-line footprint, per-context isolation, a different coherent machine on every launch. Any surface can still be pinned or overridden 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-webgpu-vendor
--uxr-canvas-seed / --uxr-audio-seed / --uxr-timezone / --uxr-languages / --uxr-color-scheme
--uxr-screen-width / --uxr-screen-height / --uxr-webrtc-policy=disable_non_proxied_udp
Env varPurpose
TILION_NO_DEFAULTS=1Skip the default persona (bare launch)
TILION_TZ / TILION_LANGQuick timezone / language override

Full config reference: docs/UXR_CONFIG.md, every --uxr-* flag, every env var, and the coherence rules the engine enforces.


Works with your stack

Fortress exposes raw CDP on :9222, so it drops in under anything that speaks Playwright, Puppeteer, or CDP. Keep your framework, swap the browser.

FrameworkConnect via
browser-use (~70k stars)cdp_url="http://localhost:9222"
Crawl4AI (~58k stars)CDP endpoint
Stagehand (~21k stars)connectOverCDP
LangChain Playwright toolkitPlaywright CDP
Playwright / Puppeteer (Python & JS)connect_over_cdp / connect
Fortress MCP + pip install tilion facadetools for agents, raw CDP underneath
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

Build from source

export CHROMIUM_VERSION=$(cat CHROMIUM_VERSION)   # 153.0.8010.36
build/build.sh                         # depot_tools, sync the tag, apply patches, gn gen, ninja
build/rebase-monthly.sh 154.0.XXXX.0   # bump + 3-way apply + rebuild + gauntlet-gate

Output: out/Fortress/chrome. The fork is 81 single-surface patches; the gauntlet gates every release on any regression. Cross-platform builds cross-compile from the same patched tree; v8 and boringssl carry their own sub-repo patches, applied separately. The first-generation patch series is public and rebuilds with the same script.

PlatformStatus
Linux x64 (native) · Windows x64 (native .zip + launcher) · any OS via Dockeryes shipping
Linux arm64 · x86 · armhf (native tarball)rolling out
macOS .app (arm64 / x64)build from source
Code-signed installersin progress

Verify it is ours

Fortress ships from four official channels. Treat anything else as untrusted:

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/v153.0.8010.36
curl -LO $BASE/fortress-v153-linux-x64.tar.gz
curl -Ls $BASE/SHA256SUMS | sha256sum -c --ignore-missing     # -> OK

Verify the Docker image by digest; a tag alone proves nothing:

docker pull tilion/fortress:153.0.8010.36
docker inspect --format '{{index .RepoDigests 0}}' tilion/fortress:153.0.8010.36
# compare the printed sha256:... against the digest in the GitHub Release notes

Reference

Troubleshooting

Still blocked on Cloudflare, DataDome, or Kasada. Most of the time this is your IP: 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. Tilion Cloud runs Fortress on residential egress with the geo-coherence already wired, so this step disappears; join the waitlist at tilion.com.

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. Or run the native Windows build, where the OS story is real.

macOS runs Docker or builds from source. Native Linux and Windows binaries ship today; on macOS run the official Docker image (tilion/fortress) or build the .app from source.

A detector flags something the gauntlet passes. Detection moves. Confirm you're on the current Chromium rebase, then email team@tilion.dev with the test page. That page becomes the next patch.

FAQ

What is Fortress? A Chromium fork whose browser fingerprint is corrected in the engine's C++ and delivered as a normal browser binary with a CDP endpoint. Scrapers and AI agents connect to it with Playwright, Puppeteer or any CDP client and are read as an ordinary Chrome install by bot detectors.

Does Fortress work with Playwright, Puppeteer and Selenium? Playwright and Puppeteer connect over CDP (connect_over_cdp / connectOverCDP / puppeteer.connect) with no other code change. Selenium users should drive Fortress over CDP too; chromedriver adds the Layer-A artifacts Fortress is built to avoid.

Does Fortress work with browser-use, Crawl4AI, Stagehand, LangChain, Claude, Cursor? Yes. The frameworks take a CDP endpoint (http://localhost:9222). Claude, Cursor, Cline and Windsurf get Fortress as tools through the Fortress MCP server (tilion-mcp).

Does Fortress pass Cloudflare, DataDome, Akamai, HUMAN (PerimeterX), Kasada? In the 92-site benchmark, headless on datacenter IPs with no proxies, Fortress served 86.4% of pages across those vendors and others, including 2/2 on WSJ, Marriott, Macy's, easyJet, PayPal sign-in, Tripadvisor, Monster, Zoopla, datadome.co and AutoZone. Some sites block every datacenter IP before any page script runs; see "Do I still need proxies".

How does Fortress compare with puppeteer-extra-plugin-stealth, undetected-chromedriver, nodriver and patchright? Those tools patch the JavaScript or CDP layer after the page can inspect the browser, so toString and realm re-acquisition reveal them, and run headless they send HeadlessChrome in the user agent. In the benchmark, headless, they served 34 to 36% of pages (puppeteer-stealth 72.3%) against Fortress's 86.4%. Fortress moves the correction into C++, where the page finds native code.

How is Fortress different from Camoufox? Same C++-interception idea, and Camoufox is the closest analog. Camoufox forks Firefox (~3% of traffic, and cannot emit a Chromium/V8 fingerprint); Fortress forks Chromium/V8 (the majority engine), and adds WebGPU coherence and fleet on_restore re-identity that Camoufox does not have. Headless in the benchmark: Fortress 86.4%, Camoufox 74.5%. Mouse motion is now on par; keystroke and scroll variance are next on the roadmap.

Is Fortress an alternative to anti-detect browsers such as Multilogin, GoLogin, Kameleo, AdsPower and Dolphin? Those are closed-source Chromium builds sold as persistent profiles, usually with bundled proxies. Fortress is an engine you run yourself: a fresh coherent identity per launch, per-context isolation, published first-generation patches, a signed and checksummed binary, and no account or telemetry. For bundled residential egress, the Tilion hosted version is on a waitlist.

Does Fortress run headless? Yes, and the benchmark numbers above are headless numbers. Headless and headed launches present the same fingerprint.

Which platforms does Fortress run on? Native Linux x64 and Windows x64, any OS through the Docker image, Linux arm64 / x86 / armhf tarballs rolling out, and a macOS .app built from source. See Native builds.

Do I still need proxies? With the self-hosted engine, yes, for sites that block datacenter ranges: a residential or mobile exit lifts most remaining blocks, and Fortress keeps timezone, locale and WebRTC coherent with the exit. The Tilion hosted version runs Fortress on residential egress with that wiring done.

How does Fortress change the fingerprint? 81 single-surface C++ patches on Chromium 153 correct canvas, WebGL and WebGPU parameters, audio, fonts, navigator, Client-Hints, screen, timezone and about forty other surfaces. The persona is generated per launch and delivered to the renderer over IPC, so nothing shows on the command line and every BrowserContext can hold its own identity. --uxr-* flags pin any surface.

Is Fortress open source? How much does it cost? Fortress is source-available under the Fortress Source Available License 1.1: read, modify, rebuild and redistribute it freely, and use it for development, testing and evaluation at any company size. Production use is free for individuals and for organizations under US$500,000 in cumulative funding and US$300,000 or less in ARR; above either line one flat subscription covers the whole organization, bought self-serve at tilion.com/pricing. Every install uses a free licence key obtained by signing in; the key is checked offline and nothing reports usage.

How is v3 different from v2? See v2 to v3 at a glance. In one line: v2 was a coherent single Windows persona on Chromium 151/152; v3 is a coherent fleet engine on Chromium 153, with an IPC persona graph, per-context isolation, on_restore full-persona re-key, parameter-level WebGL/WebGPU, and thirty-plus substrate tells closed.

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.

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 IPC persona (one binary, many coherent fingerprints, nothing on the command line) shipped in v3, with per-context isolation
  • on_restore full-persona re-key on snapshot resume shipped in v3
  • Parameter-level WebGL + WebGPU coherence shipped in v3, verified on live WebGPU
  • Behavioral humanization: recorded-human mouse-trajectory engine shipped; keystroke and scroll variance next
  • Bundle the Widevine CDM (a Google Chrome persona should have DRM) shipped, EME verified
  • Native Windows build shipped (native .zip + tillion.cmd)
  • First-party MCP server plus Puppeteer / raw-CDP SDKs shipped (Beta)
  • First-party residential / mobile egress, auto-wired to the existing geo-coherence (the #1 real-world blocker)
  • Linux arm64 / x86 / armhf native tarballs (rolling out) · macOS .app
  • Add TLS ClientHello (JA3/JA4) to the persona surface set
  • Code-signed Windows .exe and macOS .app
  • Published reCAPTCHA v3 / DataDome / Kasada benchmark rows (dated, reproducible) shipped: 92-site benchmark, eight stacks, all headless
Repo layout
patches/     the first-generation C++ patch series (Fortress Source Available License 1.1)
build/       args.gn, build.sh, apply-patches.sh, rebase-monthly.sh, windows/, macos/
packaging/   tilion launcher, fonts.conf, Dockerfile, .deb + bundle builders
fonts/       222 metric-compatible OS-named font files (154 distinct families, incl. color emoji)
sdk/         python + node (tilion-fortress) prebuilt-binary SDKs
mcp/         the Fortress MCP server (29 tools) and the agent skill
tools/       gauntlet.py, the CreepJS / Sannysoft / BrowserScan CI gate
docs/        UXR_CONFIG (the --uxr-* reference), GAUNTLET_RESULTS, BENCHMARK

Contributing

Fortress does not accept outside pull requests; see CONTRIBUTING.md. If you find a detection vector or a leak we missed, email team@tilion.dev with a reproducible test page; it becomes the next patch.

License

Fortress Source Available License 1.1. The Fortress patches, SDK, MCP server, launcher and tooling are source-available: free to read, modify, rebuild, redistribute, and to use for development, testing and evaluation at any size. Production use is free for individuals and for organizations under US$500,000 in cumulative funding and US$300,000 or less in ARR; other organizations need one flat subscription, bought self-serve (see LICENSE and SUBSCRIPTION-TERMS.md). Earlier BSD-licensed releases keep their BSD permissions (LICENSE-BSD-LEGACY). Chromium and the bundled fonts retain their own licenses; see NOTICE.


Staying current

Fortress tracks the latest Chromium monthly, re-runs the full gauntlet, and ships a patch whenever a detector finds a new tell. Watch the releases to follow the v3 work.

anti-bot
automation
bot-detection
browser-automation
chromium
crawler
data-scraping
fingerprinting
headless-browser
headless-chrome
playwright
puppeteer
python
scraping
selenium
stealth
testing
web-crawling
web-scraping
webscraping

Significant stargazers

Dan Guido

964 followers · starred Jul 2026

zerone0x

151 followers · starred Aug 2026

Xe Iaso

4,166 followers · starred Jul 2026

yetone

8,222 followers · starred Aug 2026

tiliondev/fortress

Stealth Chromium engine that stops scrapers and browser agents from getting blocked, with one line of code change.

Python

706

94 commits

updated Sep 30, 2026

See the code

See what people are saying

README

Fortress

One browser engine to rule them all

Stealth Chromium engine · v3 (Chromium 153)

Chromium Docker pulls Discord
Copy for agent llms.txt MCP server npm tilion-mcp

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. With v3, every launch is a different coherent machine, so a fleet of sessions looks like a crowd of real users.

Headless, on datacenter IPs, with no proxies, Fortress served 86.4% of 92 protected pages; the next best stack (Camoufox) served 74.5%.

Blink · V8 · BoringSSL patched in-tree · ANGLE / D3D11-backed WebGL · JA3/JA4-coherent TLS · monthly upstream rebase · reproducible, gauntlet-gated releases

81

single-surface
C++ patches

0%

CreepJS
headless / stealth

48/48

distinct across a
64-clone fleet

[native code]

across every
realm

Fortress clearing a live Cloudflare challenge, then passing sannysoft and BrowserScan

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 parity

Every spoofed getter is a C++ getter: toString returns [native code], realm-invariant across main frame, iframes, and Web Workers.

Drop-in CDP

nodriver-style raw CDP on :9222, with no Runtime.enable leak. Keep Playwright, Puppeteer, or any CDP client; swap the browser, keep your code.

Clears the gauntlet

0% headless on CreepJS; Sannysoft, BrowserScan, and live Cloudflare Turnstile cleared, all as a stock Chrome install.

IPC persona graph

The persona reaches the renderer over IPC into a process-global config: zero command-line footprint, per-context isolation, thousands of coherent identities from one binary. --uxr-* switches stay as explicit overrides.

Fleet re-identity

on_restore re-keys the whole persona (canvas · audio · WebGL · GPU · screen · UA · TLS) from a snapshot with no relaunch. 64 clones come up 48/48 distinct.

Coherent by construction

Real V8, Blink, and BoringSSL keep engine, user-agent, and JA3/JA4 TLS shape in agreement, and WebGL / WebGPU agree with the persona GPU to the parameter level: limits, precision and extensions as well as the renderer string.


What's new: v3 · 153.0.8010.36 · Fleet Engine

v3 turns Fortress from a coherent single persona into a coherent fleet engine: 87 commits on patches/, a Chromium 151/152 to 153 rebase, and the changes below.

  • IPC persona graph, shipped. The persona is delivered to the renderer over IPC into a process-global config: zero command-line footprint and per-context identity isolation, so multiple sessions or accounts in one browser never collapse into a single linkable identity.
  • on_restore fleet re-identity. On snapshot resume the entire persona re-keys in place (RNG, canvas/audio, GPU/screen/UA, network and TLS state) with no relaunch, confirmed by a closed-loop per-surface ack. A 64-clone fleet comes up 48/48 distinct, stress-gated to 500 clones.
  • Byte-exact canvas and audio. Canvas noise is idempotent and byte-exact across getImageData / toDataURL / toBlob (and OffscreenCanvas), edge-gated to anti-aliased pixels only, with 64-bit domain-separated seeds and a fail-closed RNG (a zero or absent seed disables the noise; no golden constant can leak). WebGL/WebGL2 readback, including PBO readPixels, routes through the same edge-gated path. Audio farble is value-keyed: identical inputs give identical outputs, fresh per persona yet internally stable.
  • Coherent WebGL and WebGPU. Both agree with the persona GPU at the parameter level: limits, precision, extensions, and navigator.gpu adapter identity all match a real hardware GPU, never the software backend. Verified live.
  • A real font substrate. 154 distinct bundled font families with real metric clones and per-persona metrics; enumeration is gated to the persona so host fonts are never visible.
  • Coherence everywhere. About thirty specific tells closed, each with a coherence rule in place of a spoof: @media matches screen and DPR, color-gamut and dynamic-range, jsHeapSizeLimit matches deviceMemory, prefers-color-scheme per persona (about a third dark), Windows system fonts, Device-Memory client hint on the navigation request path, WebAuthn isUVPAA() per persona, macOS zero-width overlay scrollbars, fail-closed WebRTC (no real-IP leak on a bare launch), pointer/hover/maxTouchPoints pinned, OS-appropriate speechSynthesis voices, and coherent Mac personas (Apple arch, core and RAM SKUs, a MacBook that is not permanently plugged in).
  • Recorded-human mouse motion. An engine-native trajectory engine backed by a bank of ~10k recorded-human paths (SapiMouse) paces the cursor with organic velocity and micro-jitter, a ~16× more human motion signal than a raw driver (humanness gap ~4.3 vs ~70). Speed-proportional getCoalescedEvents batching and per-persona realtime AudioContext timing round out the behavioral seed.
  • Widevine EME, shipped. The real Widevine CDM is bundled and enabled, so requestMediaKeySystemAccess('com.widevine.alpha') resolves exactly as it does in a genuine Google Chrome. The DRM gap the roadmap flagged is closed.
  • Native on more platforms. Windows x64 ships native (.zip + tillion.cmd); Linux arm64, x86 and armhf tarballs are rolling out; the macOS .app builds from source.
pip install -U tilion-fortress       # or:  docker run --rm -p 9222:9222 tilion/fortress:latest

Full release notes

Contents

What it is · Quick startwhat it is, install, first script, native builds, AI-agent setup
Every session a distinct machine40 back-to-back launches, 40 different coherent machines
The Fortress MCP29 stealth-browser tools for AI agents (Beta)
Why patch the engine, not the pagethe self-revealing-JS thesis + the four detection layers
How Fortress comparesvs puppeteer-stealth · Camoufox · antidetect apps · v2 to v3
Proof: live-detector resultsv3 live results, CreepJS / Sannysoft / BrowserScan / WebGL / WebGPU, with screenshots
Benchmark: 92 protected sitesFortress vs seven stacks, all headless, 1,584 fresh machines, datacenter IPs
Configure the personathe IPC persona graph and the --uxr-* fingerprint surface
Works with your stackbrowser-use · Crawl4AI · Stagehand · LangChain
Build & verifybuild from source, platforms, verify provenance
Referencetroubleshooting · 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, WebGPU, audio, fonts, navigator, Client-Hints, and about forty 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.

With v3 the persona is no longer a single fixed identity. Each launch mints a fresh, internally coherent machine (GPU, screen, cores, timezone, language, fonts, all in agreement), delivered to the renderer over IPC, so nothing shows on the command line and every BrowserContext can hold its own identity.

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();

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.

Fortress extracting books.toscrape.com into typed JSON records live over CDP

Structured extraction: records build into typed JSON as each item is read.

Fortress auto-paginating across pages of quotes.toscrape.com
Auto-pagination: 30 quotes across 3 pages.
Fortress deep-crawling a product detail page
Deep detail crawl: UPC · price · tax · stock · reviews.

Clears real Akamai, before and after

Before: a stock browser is blocked by Akamai on aa.com with Access Denied. After: Fortress loads the real page and Akamai's sensor accepts it.

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 IP is the same in both runs, so the variable is the fingerprint.

The same before and after holds on lowes.com, macys.com and kohls.com, every run from the same residential IP.


Every session a distinct machine

40 back-to-back launches of the same v3 binary, headless. Each one is a different, internally coherent machine, and no two sessions shared a canvas or audio fingerprint:

Across 40 launchesDistinct
Canvas fingerprint40 / 40
Audio fingerprint40 / 40
GPU (WebGL renderer)25
Screen resolution14
Timezone (geo-coherent with language)23
Platform mix31 Windows · 9 macOS
#PlatformGPUScreenCoresTimezoneLangCanvas
1Win32Intel UHD Graphics1536×8642Europe/Warsawpl-PL78ae7500
2Win32Intel HD Graphics 46001920×10804Asia/Seoulko-KR6dcc5a20
3Win32Intel UHD Graphics 6201920×108012America/Buenos_Aireses-ARdeddd100
4Win32Intel UHD Graphics1920×10806Europe/Parisfr-FR643e08d8
5Win32Intel UHD Graphics2560×14408America/New_Yorken-US56e37548
6Win32AMD Radeon 860M1707×9608Asia/Makassarid-IDbbaacff0
7Win32Intel UHD Graphics1536×8648America/Sao_Paulopt-BRb1d2ec50
8Win32Intel Iris Xe Graphics2560×144012Asia/Calcuttahi-IN89d61698
9MacIntelApple M1 Pro (Metal)1728×111710America/Chicagoen-USa975f300
10MacIntelApple M4 (Metal)1512×98210America/Denveren-US9f03f6f8

…30 more, all distinct. Every row is a coherent machine: GPU, screen, core count, timezone and language agree (Windows with Intel/AMD/D3D11, macOS with Apple/Metal; timezone, language and region matched). Reproduce with tools/gauntlet.py --runs 40.


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
docker run --rm -p 9222:9222 tilion/fortress:latest

# Portable tarball (Linux x64 / arm64 / x86 / armhf): use it like a Chromium snapshot
tar xzf fortress-v153-linux-x64.tar.gz
./fortress-v153/tilion https://example.com
./fortress-v153/tilion --headless=new --remote-debugging-port=9222 --user-data-dir=/tmp/p

# Native Windows x64: unzip and launch via the .cmd (raw CDP, same --uxr-* overrides)
#   fortress-v153-win-x64\tillion.cmd --headless=new --remote-debugging-port=9222 --user-data-dir=C:\tmp\p

# Debian / Ubuntu
sudo apt install ./tilion-fortress_153.0.8010.36_amd64.deb && tilion https://example.com

[!TIP] The SDK ships the compiled v3 engine, ready to run, and downloads are SHA-256-verified against the release SHA256SUMS automatically. The first-generation patch series is public to read and rebuild.

Free for developers, unlock in one command

Fortress v3 is free for developers, with no machine, session, or concurrency caps. One ten-second step turns a fresh install into the full engine: sign in with GitHub, Google, or email so we know who our developers are.

tilion activate      # opens your browser, sign in, done. Key saved to ~/.tilion/license.jwt

The key is verified offline after that; Fortress never phones home or reports usage. Skip activation and Fortress still runs: it prints one line and falls back to the public first-generation engine until you activate.

DevelopersFree and unlimited. Each developer gets a unique key that auto-refreshes, so you activate once.
CI, Docker, AI agents (no browser)Set TILION_LICENSE_KEY=<key> in the environment, or run tilion activate --token <key>. One key covers a whole fleet.
EnterpriseA production key from tilion.com/pricing for your org's fleet. Drop it in TILION_LICENSE_KEY.

The tilion CLI

pip install tilion-fortress (or npm install tilion-fortress) puts the tilion command on your PATH. The portable tarball and the Docker image bundle the same activator, so it works everywhere with no extra install.

CommandWhat it does
tilion activateUnlock v3 with a one-click device flow (browser sign-in)
tilion activate --headlessSame flow, prints the URL and code (SSH or remote hosts)
tilion activate --token <jwt>Save a key with no browser (CI, agents)
tilion license status [--json]Show the saved key's tier and expiry (--json for scripts)
tilion license refreshRe-issue the key before it expires
tilion license logoutRemove the saved key and drop back to v1
tilion get [platform]Download the build for this host. tilion get list shows all: linux-x64, arm64, armhf, x86, win-x64, mac-arm64, mac-x64
tilion mcpRun the Fortress MCP server, stealth browsing as agent tools
tilion [--port N]Launch the engine and print the CDP endpoint

Native builds: one engine, every platform

The same v3 engine ships as a native binary per platform, no container hop, from Releases. Each launch mints a fresh, coherent machine for that OS: a Windows build draws Windows personas (Win32 · D3D11 GPUs), verified 10/10.

PlatformPackageWidevineStatus
Linux x64portable tarball · Docker · pip / npmyesyes shipping
Windows x64portable .zip + tillion.cmd launcheryesyes shipping
Linux arm64portable tarball (Graviton, dense cloud fleets)noyes shipping
Linux x86 · armhfportable tarball (32-bit x86 / ARM)noyes shipping
macOS (arm64 / x64).app, built from sourceyesbuild from source

All four Linux builds (x64 · arm64 · armhf · x86) and Windows x64 carry the same license gate; the 32-bit and arm64 cross-builds ship without the bundled Widevine CDM. macOS builds from source with Widevine on.

Why native matters: a Windows persona on a real Windows host emits a genuine Windows TLS/JA4T and OS story for free (DirectWrite fonts, real Widevine DRM), and a Mac persona on real macOS gets HEVC decode, Apple Color Emoji, and the right DRM: the last substrate leaks a Linux host cannot fully paper over. One engine, every OS, the same 48/48-distinct fleet.

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:

Ask ChatGPT Ask Claude Ask Gemini Copy for agent

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, a STEALTH Chromium engine, for browser automation.
It corrects the browser fingerprint (canvas, WebGL, WebGPU, audio, fonts, navigator, +40 more) in
Chromium's C++ and exposes raw CDP on http://localhost:9222, a drop-in for Playwright/Puppeteer.
Every launch is a fresh, coherent machine. 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; the default is a fresh coherent identity per launch, delivered over IPC).
Pin or 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.

Same site, same prompt: a vanilla browser is blocked by PerimeterX while an agent with the Fortress MCP returns clean JSON

Real, dated run against stockx.com (PerimeterX). A stock browser gets HTTP 403, “Access denied”; an agent with the Fortress MCP returns clean JSON from the same site and the 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" } } }

For 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 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 pagesfetch_protected_page · read_page · get_page_html · search_web
Structured dataextract_page (schema-aware) · extract_document (PDF/DOCX/XLSX)
Whole sitescrawl_site (auto-SPA) · recon_site_apis (find the private JSON API)
Drive a pagepage_elements · click_button · fill_field · press_key · wait_for · evaluate_js
Multi-step flowsrun_browser_task (login, paginate, infinite-scroll, checkout, …)
Capture / authscreenshot_page · save_page · download_file · save_profile / load_profile
Bring your ownget_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, Tilion Cloud, is on a waitlist at tilion.com.

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: 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 tellWhy it catches a JS spoof
toString self-revealA native method stringifies to function get vendor() { [native code] }; an override stringifies to its own source, so one .toString() catches it.
Descriptor and hasOwnPropertygetOwnPropertyDescriptor exposes redefined props, and hasOwnProperty('toString') returns true on a tampered function where a native one returns false.
failsTypeErrorNative 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, and, unlike a Firefox fork, emits a Chromium/V8 fingerprint that matches a Chrome user-agent by construction.

The four layers of bot detection, and where Fortress fits

Modern anti-bots (Cloudflare, DataDome, Kasada, HUMAN, Akamai) read structurally different surfaces in separate places. One tool rarely fixes all of them:

LayerThe tellsWhere the fix livesFortress
A: driver / binary artifactscdc_ ChromeDriver vars, WebDriver protocol surfaceDrive raw CDP, skip chromedriveryes built to be driven this way
B: CDP side-effectsRuntime.enable leaks via sourceURL + init-script footprints, however clean the binary isThe control / CDP-client layer: hold back Runtime.enable, use Runtime.addBinding + isolated worldsyes no leak (verified on rebrowser)
C: fingerprint surfacecanvas, WebGL, WebGPU, audio, fonts, navigator, across main frame, iframes, workersThe engine (C++), because JS overrides self-revealyes this is Fortress
D: network / IP egressdatacenter ASN, IP reputation, geo-vs-persona mismatchYour proxies (residential / mobile), or the Tilion hosted versionpartial bring your own with the self-hosted engine; available on the Tilion hosted version, waitlist at tilion.com

Fortress is the Layer-C engine, built to be driven so A and B hold too, and to stay geo-coherent (timezone, locale and WebRTC follow the egress) once you supply a Layer-D IP. The binary alone leaves the CDP channel open and the IP question unanswered.


How Fortress compares

Stock Playwrightpuppeteer-extra-stealthundetected-chromedriverCamoufoxAntidetect apps¹Fortress v3
Spoof layernoneJS injectionCDP/config patchC++ engine (Firefox)C++ engineC++ engine (Chromium)
toString yields [native code]n/anon/ayesyesyes
Survives realm re-acquisition (iframe/worker)nononoyesyesyes
No Runtime.enable leaknonopartialyesyesyes
Engine = Chrome / V8 (majority traffic)yesyesyesno Firefoxyesyes
Coherent Chromium TLS shapeyesyesyesno Firefoxyesyes
Parameter-level WebGL + WebGPU coherencen/anonopartial no WebGPUvariesyes
Per-launch fleet dynamism (distinct machine)nononoyesno persistent by designyes 48/48
Full-persona re-key on snapshot resumenononononoyes on_restore
Native single-surface C++ patch enginen/an/an/ayesno closedyes 81 patches
States its own limitsn/an/an/ayesnoyes

¹ Multilogin / GoLogin / Dolphin / Nstbrowser: closed-source engine patches, persistent identity by design, bundled residential egress (their Layer-D advantage over Fortress).

Fortress builds on real prior art: fingerprint-chromium, ChromiumFish, and CloakBrowser came first, and commercial vendors recompile Chromium behind closed source. Native per-surface control is not unique to Fortress: Camoufox, Multilogin, GoLogin and Dolphin all patch engine C++.

Honest positioning. Fortress's real edge is the combination: the broadest native per-surface set on a Chromium base, fleet dynamism plus on_restore re-identity that no competitor matches, and coherence discipline (gated, byte-exact, parameter-level). Where Fortress is behind: IP egress (bring your own; the #1 real-world blocker) and behavioral humanization (mouse motion shipped; keystroke and scroll variance next).

v2 to v3 at a glance

v2 is the published Chromium-151/152 engine: a coherent single persona, command-line personas, renderer-string-only WebGL. v3 is the current build. The difference is 87 commits:

Axisv2v3
Chromium base151 / 152153.0.8010.36
Single-surface C++ patches~3481
Persona transportcommand line, per launch, host-readableIPC-delivered, in-process, per-context isolated
Multi-identity in one binaryone persona per processper-BrowserContext isolation
Snapshot resumerelaunch to change identityon_restore full-persona re-key, no relaunch
Fleet distinctnessnot measured48/48 across 64 clones (500-clone stress-gated)
Canvas noiseseededidempotent, byte-exact cross-path, 64-bit seeds, fail-closed
WebGLrenderer string only (incoherent numbers)parameter-level limits + extensions + string, per backend
WebGPUabsent / software-backend tellcoherent with the persona GPU + per-clone variation
Fontsmetric substitutes (~33)154-family substrate, enumeration gated to the persona
Client-HintsUA basicsfull Sec-CH-UA + Device-Memory on the navigation path
prefers-color-schemefleet-uniformper persona (about a third dark)
Mouse motionraw driverrecorded-human trajectories (~10k paths, ~16× more human)
DRMno Widevinereal Widevine CDM bundled, EME verified

Proof: live-detector results

Reproduce any row with tools/gauntlet.py --bundle ./fortress-v153. Verified against live detectors; re-run dated in docs/GAUNTLET_RESULTS.md.

Real capture of the v3 binary (Chromium 153) run headless from a datacenter IP. Persona on this launch: Win32 · Chrome/153 · WebGL ANGLE (Intel HD Graphics Family, Direct3D11) · Europe/Rome · it-IT · 2-core / 16 GB, and a different coherent machine on the next launch (NVIDIA/Copenhagen, AMD, Apple…). That per-launch distinctness is the point; see Every session a distinct machine.

SuiteStock ChromiumFortress v3
CreepJSflagged headless0% headless · 0% stealth, worker signals coherent
bot.sannysoft.comred rows0 failed · every intoli + PHANTOM_/HEADCHR_ check green · navigator.webdriver undefined
browserscan.netbot detected“Normal: No bots detected, could be a human” · WebDriver / Selenium / PhantomJS / Headless / CDP / DevTool all Normal
BrowserLeaks · WebGLSwiftShader leakUnmasked ANGLE (Intel HD Graphics, Direct3D11) with fully coherent D3D11 parameters
WebGPU (secure context)software-backend tellnavigator.gpu coherent with the persona GPU: adapter identity, limits, subgroup sizes (verified)
AmIUnique · pixelscan · ipheyautomation flagsconsistent, no automation flags
rebrowser bot-detectorRuntime.enable LEAKno leak · webdriver=false · clean init-scripts (raw CDP)
64-clone fleetone shared identity48/48 distinct canvas / audio / Math.random (fleet gate)
Cloudflare Turnstileblockedcleared: a human click cleared a live challenge (headed, datacenter IP)
   
Detection results, v2 to v3

The v3 column is live-verified on the current binary. The v2 column is the prior Chromium-151/152 build, characterized from the v2 to v3 change set; it is not a re-run of the retired binary.

Suite / surfaceFortress v2 (prior)Fortress v3 (current)
bot.sannysoft.compassed the base panel0 failed (verified)
CreepJScoherence tells to deep lie-checks0% headless, worker signals coherent (verified)
browserscan.netnot run“Normal: No bots detected” (verified)
WebGLrenderer string only; the numbers did not match the claimed GPUparameter-level coherent with the persona GPU
WebGPUabsent / software-backend tellcoherent with the persona GPU (verified)
Canvasdouble-noise, cross-path hash mismatchbyte-exact across getImageData / toDataURL / toBlob
measureText / fontsstable metrics; ~33 fontsper-persona metrics; 154-family substrate
64-clone fleet~1 shared identity48/48 distinct canvas / audio / Math.random (gate)
Substrate leaksseveral host/OS leaks across GPU, fonts, memory and displayall closed
on_restore resumerelaunch to re-identifyfull-persona re-key, no relaunch (500-clone stress-gated)
Persona transportcommand line, host-readable, one per launchIPC-delivered, per-context isolated
rebrowser bot-detectornot runno Runtime.enable leak · webdriver=false · clean init-scripts

Benchmark: 92 protected sites, eight stacks

Method, target list and checker probes in docs/BENCHMARK.md. Run on 2026-09-28 from US-East cloud machines on datacenter IPs with no proxies. Every tool ran headless.

Fortress (build v153.0.8010.36-linux-3) and seven other browser stacks loaded 92 protected sites: Cloudflare, DataDome, Akamai, HUMAN, Kasada, Imperva, AWS WAF, Arkose, reCAPTCHA, twenty login pages and five controls. Every tool loaded every target twice, each time on a new cloud machine that was destroyed afterwards (1,584 machines, 1,472 scored runs). A run counts as served only if the real page loaded with no challenge or deny page showing. No clicks, no typing, no captcha solver. Fortress was driven through Tilion.fetch, the same call the MCP tool uses.

StackServedn95% range
Fortress86.4%159/18481 to 91
Camoufox74.5%137/18468 to 80
puppeteer-extra + stealth72.3%133/18465 to 78
Brave36.4%67/18430 to 44
nodriver35.9%66/18429 to 43
stock Chrome34.2%63/18428 to 41
patchright34.2%63/18428 to 41
undetected-chromedriver33.7%62/18427 to 41

The range is the 95% Wilson interval. Fortress's interval does not overlap the next stack's. Fortress got 2/2 on ten sites where neither Camoufox nor puppeteer-stealth got more than 1/2 (WSJ, Marriott, Macy's, easyJet, PayPal sign-in, Tripadvisor, Monster, Zoopla, datadome.co, AutoZone); there is no site where Fortress got 0/2 and either of them got 2/2. nodriver, undetected-chromedriver and patchright served 62 to 66 pages, the same range as stock Chrome; run headless, all three send HeadlessChrome/153 in the user agent. Fortress's failure rate (13.6%) is about half of Camoufox's (25.5%) and one fifth of the Chrome-based tools' (64 to 66%).

Repeat runThe same headless design was run twice on the same day. Fortress served 85.3% and 86.4%; stock Chrome and Brave, identical in both runs, moved by 1 to 3 points.
Fingerprint checkersFortress and Camoufox had zero failures on bot.sannysoft.com, an all-green rebrowser bot detector, 0% headless on CreepJS and "Normal" on BrowserScan. Stock Chrome and Brave read "Robot" on BrowserScan and 67% headless on CreepJS.

Configure the persona

The binary carries zero brand strings. The launcher mints a coherent persona and delivers it to the renderer over IPC: no command-line footprint, per-context isolation, a different coherent machine on every launch. Any surface can still be pinned or overridden 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-webgpu-vendor
--uxr-canvas-seed / --uxr-audio-seed / --uxr-timezone / --uxr-languages / --uxr-color-scheme
--uxr-screen-width / --uxr-screen-height / --uxr-webrtc-policy=disable_non_proxied_udp
Env varPurpose
TILION_NO_DEFAULTS=1Skip the default persona (bare launch)
TILION_TZ / TILION_LANGQuick timezone / language override

Full config reference: docs/UXR_CONFIG.md, every --uxr-* flag, every env var, and the coherence rules the engine enforces.


Works with your stack

Fortress exposes raw CDP on :9222, so it drops in under anything that speaks Playwright, Puppeteer, or CDP. Keep your framework, swap the browser.

FrameworkConnect via
browser-use (~70k stars)cdp_url="http://localhost:9222"
Crawl4AI (~58k stars)CDP endpoint
Stagehand (~21k stars)connectOverCDP
LangChain Playwright toolkitPlaywright CDP
Playwright / Puppeteer (Python & JS)connect_over_cdp / connect
Fortress MCP + pip install tilion facadetools for agents, raw CDP underneath
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

Build from source

export CHROMIUM_VERSION=$(cat CHROMIUM_VERSION)   # 153.0.8010.36
build/build.sh                         # depot_tools, sync the tag, apply patches, gn gen, ninja
build/rebase-monthly.sh 154.0.XXXX.0   # bump + 3-way apply + rebuild + gauntlet-gate

Output: out/Fortress/chrome. The fork is 81 single-surface patches; the gauntlet gates every release on any regression. Cross-platform builds cross-compile from the same patched tree; v8 and boringssl carry their own sub-repo patches, applied separately. The first-generation patch series is public and rebuilds with the same script.

PlatformStatus
Linux x64 (native) · Windows x64 (native .zip + launcher) · any OS via Dockeryes shipping
Linux arm64 · x86 · armhf (native tarball)rolling out
macOS .app (arm64 / x64)build from source
Code-signed installersin progress

Verify it is ours

Fortress ships from four official channels. Treat anything else as untrusted:

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/v153.0.8010.36
curl -LO $BASE/fortress-v153-linux-x64.tar.gz
curl -Ls $BASE/SHA256SUMS | sha256sum -c --ignore-missing     # -> OK

Verify the Docker image by digest; a tag alone proves nothing:

docker pull tilion/fortress:153.0.8010.36
docker inspect --format '{{index .RepoDigests 0}}' tilion/fortress:153.0.8010.36
# compare the printed sha256:... against the digest in the GitHub Release notes

Reference

Troubleshooting

Still blocked on Cloudflare, DataDome, or Kasada. Most of the time this is your IP: 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. Tilion Cloud runs Fortress on residential egress with the geo-coherence already wired, so this step disappears; join the waitlist at tilion.com.

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. Or run the native Windows build, where the OS story is real.

macOS runs Docker or builds from source. Native Linux and Windows binaries ship today; on macOS run the official Docker image (tilion/fortress) or build the .app from source.

A detector flags something the gauntlet passes. Detection moves. Confirm you're on the current Chromium rebase, then email team@tilion.dev with the test page. That page becomes the next patch.

FAQ

What is Fortress? A Chromium fork whose browser fingerprint is corrected in the engine's C++ and delivered as a normal browser binary with a CDP endpoint. Scrapers and AI agents connect to it with Playwright, Puppeteer or any CDP client and are read as an ordinary Chrome install by bot detectors.

Does Fortress work with Playwright, Puppeteer and Selenium? Playwright and Puppeteer connect over CDP (connect_over_cdp / connectOverCDP / puppeteer.connect) with no other code change. Selenium users should drive Fortress over CDP too; chromedriver adds the Layer-A artifacts Fortress is built to avoid.

Does Fortress work with browser-use, Crawl4AI, Stagehand, LangChain, Claude, Cursor? Yes. The frameworks take a CDP endpoint (http://localhost:9222). Claude, Cursor, Cline and Windsurf get Fortress as tools through the Fortress MCP server (tilion-mcp).

Does Fortress pass Cloudflare, DataDome, Akamai, HUMAN (PerimeterX), Kasada? In the 92-site benchmark, headless on datacenter IPs with no proxies, Fortress served 86.4% of pages across those vendors and others, including 2/2 on WSJ, Marriott, Macy's, easyJet, PayPal sign-in, Tripadvisor, Monster, Zoopla, datadome.co and AutoZone. Some sites block every datacenter IP before any page script runs; see "Do I still need proxies".

How does Fortress compare with puppeteer-extra-plugin-stealth, undetected-chromedriver, nodriver and patchright? Those tools patch the JavaScript or CDP layer after the page can inspect the browser, so toString and realm re-acquisition reveal them, and run headless they send HeadlessChrome in the user agent. In the benchmark, headless, they served 34 to 36% of pages (puppeteer-stealth 72.3%) against Fortress's 86.4%. Fortress moves the correction into C++, where the page finds native code.

How is Fortress different from Camoufox? Same C++-interception idea, and Camoufox is the closest analog. Camoufox forks Firefox (~3% of traffic, and cannot emit a Chromium/V8 fingerprint); Fortress forks Chromium/V8 (the majority engine), and adds WebGPU coherence and fleet on_restore re-identity that Camoufox does not have. Headless in the benchmark: Fortress 86.4%, Camoufox 74.5%. Mouse motion is now on par; keystroke and scroll variance are next on the roadmap.

Is Fortress an alternative to anti-detect browsers such as Multilogin, GoLogin, Kameleo, AdsPower and Dolphin? Those are closed-source Chromium builds sold as persistent profiles, usually with bundled proxies. Fortress is an engine you run yourself: a fresh coherent identity per launch, per-context isolation, published first-generation patches, a signed and checksummed binary, and no account or telemetry. For bundled residential egress, the Tilion hosted version is on a waitlist.

Does Fortress run headless? Yes, and the benchmark numbers above are headless numbers. Headless and headed launches present the same fingerprint.

Which platforms does Fortress run on? Native Linux x64 and Windows x64, any OS through the Docker image, Linux arm64 / x86 / armhf tarballs rolling out, and a macOS .app built from source. See Native builds.

Do I still need proxies? With the self-hosted engine, yes, for sites that block datacenter ranges: a residential or mobile exit lifts most remaining blocks, and Fortress keeps timezone, locale and WebRTC coherent with the exit. The Tilion hosted version runs Fortress on residential egress with that wiring done.

How does Fortress change the fingerprint? 81 single-surface C++ patches on Chromium 153 correct canvas, WebGL and WebGPU parameters, audio, fonts, navigator, Client-Hints, screen, timezone and about forty other surfaces. The persona is generated per launch and delivered to the renderer over IPC, so nothing shows on the command line and every BrowserContext can hold its own identity. --uxr-* flags pin any surface.

Is Fortress open source? How much does it cost? Fortress is source-available under the Fortress Source Available License 1.1: read, modify, rebuild and redistribute it freely, and use it for development, testing and evaluation at any company size. Production use is free for individuals and for organizations under US$500,000 in cumulative funding and US$300,000 or less in ARR; above either line one flat subscription covers the whole organization, bought self-serve at tilion.com/pricing. Every install uses a free licence key obtained by signing in; the key is checked offline and nothing reports usage.

How is v3 different from v2? See v2 to v3 at a glance. In one line: v2 was a coherent single Windows persona on Chromium 151/152; v3 is a coherent fleet engine on Chromium 153, with an IPC persona graph, per-context isolation, on_restore full-persona re-key, parameter-level WebGL/WebGPU, and thirty-plus substrate tells closed.

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.

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 IPC persona (one binary, many coherent fingerprints, nothing on the command line) shipped in v3, with per-context isolation
  • on_restore full-persona re-key on snapshot resume shipped in v3
  • Parameter-level WebGL + WebGPU coherence shipped in v3, verified on live WebGPU
  • Behavioral humanization: recorded-human mouse-trajectory engine shipped; keystroke and scroll variance next
  • Bundle the Widevine CDM (a Google Chrome persona should have DRM) shipped, EME verified
  • Native Windows build shipped (native .zip + tillion.cmd)
  • First-party MCP server plus Puppeteer / raw-CDP SDKs shipped (Beta)
  • First-party residential / mobile egress, auto-wired to the existing geo-coherence (the #1 real-world blocker)
  • Linux arm64 / x86 / armhf native tarballs (rolling out) · macOS .app
  • Add TLS ClientHello (JA3/JA4) to the persona surface set
  • Code-signed Windows .exe and macOS .app
  • Published reCAPTCHA v3 / DataDome / Kasada benchmark rows (dated, reproducible) shipped: 92-site benchmark, eight stacks, all headless
Repo layout
patches/     the first-generation C++ patch series (Fortress Source Available License 1.1)
build/       args.gn, build.sh, apply-patches.sh, rebase-monthly.sh, windows/, macos/
packaging/   tilion launcher, fonts.conf, Dockerfile, .deb + bundle builders
fonts/       222 metric-compatible OS-named font files (154 distinct families, incl. color emoji)
sdk/         python + node (tilion-fortress) prebuilt-binary SDKs
mcp/         the Fortress MCP server (29 tools) and the agent skill
tools/       gauntlet.py, the CreepJS / Sannysoft / BrowserScan CI gate
docs/        UXR_CONFIG (the --uxr-* reference), GAUNTLET_RESULTS, BENCHMARK

Contributing

Fortress does not accept outside pull requests; see CONTRIBUTING.md. If you find a detection vector or a leak we missed, email team@tilion.dev with a reproducible test page; it becomes the next patch.

License

Fortress Source Available License 1.1. The Fortress patches, SDK, MCP server, launcher and tooling are source-available: free to read, modify, rebuild, redistribute, and to use for development, testing and evaluation at any size. Production use is free for individuals and for organizations under US$500,000 in cumulative funding and US$300,000 or less in ARR; other organizations need one flat subscription, bought self-serve (see LICENSE and SUBSCRIPTION-TERMS.md). Earlier BSD-licensed releases keep their BSD permissions (LICENSE-BSD-LEGACY). Chromium and the bundled fonts retain their own licenses; see NOTICE.


Staying current

Fortress tracks the latest Chromium monthly, re-runs the full gauntlet, and ships a patch whenever a detector finds a new tell. Watch the releases to follow the v3 work.

anti-bot
automation
bot-detection
browser-automation
chromium
crawler
data-scraping
fingerprinting
headless-browser
headless-chrome
playwright
puppeteer
python
scraping
selenium
stealth
testing
web-crawling
web-scraping
webscraping

Significant stargazers

Dan Guido

964 followers · starred Jul 2026

zerone0x

151 followers · starred Aug 2026

Xe Iaso

4,166 followers · starred Jul 2026

yetone

8,222 followers · starred Aug 2026

Languages

Python

68.4%

JavaScript

12.5%

Shell

11.7%

PowerShell

3.6%

Dockerfile

1.8%

Batchfile

1.0%

Makefile

1.0%