Dual-mode anti-detect browser framework — CDP-native stealth for local machines, binary-level patching for production servers. No WebDriver, no chromedriver.
Project description
A dual-mode anti-detect browser framework for Python.
CDP-native stealth for local machines. Binary-level patching for production servers.
No WebDriver, no Selenium, no chromedriver.
How it works
mithwire is an anti-detect browser automation framework with two stealth modes that cover different deployment scenarios.
CDP mode (default)
The default. mithwire launches a normal Chromium-based browser (Chrome, Brave, Edge) and controls it directly over the Chrome DevTools Protocol. There is no automation driver bolted on the side — the tell-tale signals anti-bot systems use to spot WebDriver/Selenium simply aren't there.
CDP Emulation commands handle timezone, locale, geolocation, user agent, device metrics, and more. This is lightweight, works with any Chromium browser on any OS, and is the right choice when the browser runs on a real desktop where the host machine's fingerprint aligns with the browser profile — no deep lies to maintain.
Best for: local development, macOS/Windows workstations, scripts where the machine's real GPU/fonts/hardware match the presented identity.
Stealth mode (patched binary)
For production Linux/VPS environments where the host machine's fingerprint (SwiftShader GPU, minimal fonts, headless signals) would be trivially detected. Stealth mode swaps in a patched Chromium binary (CloakBrowser) that embeds anti-fingerprinting at the C++ level: canvas, WebGL, AudioContext, fonts, GPU strings, screen dimensions, TLS fingerprint, and CDP detection are all modified in the Chromium source code before compilation.
The browser's JavaScript sees only native values — there are no runtime overrides to detect. mithwire still applies CDP timezone, locale, geolocation, and Accept-Language on top (these complement rather than conflict with the binary's patches).
Best for: headless Linux servers, VPS production workloads, advanced anti-bot targets, situations requiring spoofed fingerprints that can't rely on the host machine's real hardware.
Which mode to use
| Scenario | Mode | Why |
|---|---|---|
| Scripting on your Mac/Windows laptop | CDP | Host hardware is real — no need to lie about GPU, fonts, or canvas |
| Headless server, no spoofing needed | CDP | CDP stealth is sufficient when the profile matches the machine |
| Production VPS scraping/automation | Stealth | Linux servers expose SwiftShader, minimal fonts, headless signals — the binary patches these at the source |
| Advanced anti-bot targets (CreepJS, DAB) | Stealth | Deep fingerprint surfaces (canvas hash, WebGL renderer, audio context) need native-level randomization |
| AI agent fleet via mithwire-mcp | Either | CDP for local agents, stealth for cloud/VPS agents — mithwire-mcp supports both via engine parameter |
Stealth mode requires the
cloakbrowserpackage and is currently Linux-only for the latest free builds. See mithwire-mcp for the full stealth integration (session management, proxy alignment, profile persistence, and fleet orchestration).
Features
Anti-detect by design — speaks raw CDP: no navigator.webdriver, no chromedriver binary, no Selenium surface to fingerprint
Dual stealth engine — CDP mode for local machines (CDP/JS overrides), stealth mode for production servers (C++ binary-level patches on canvas, WebGL, audio, fonts, GPU, TLS)
Real Chromium browsers — works with Chrome, Chromium, Brave, Edge, and patched CloakBrowser binaries
Headful or headless — visible window or invisible server mode; pairs with Xvfb for headed-on-headless Linux deployments
Identity & fingerprint control — timezone, locale, geolocation, user agent, device metrics, screen dimensions, and more via CDP Emulation
Proxy support — HTTP/HTTPS/SOCKS upstream proxies with automatic authenticating relay for credential-bearing HTTP proxies
Cloudflare Turnstile bypass — one call (tab.verify_cf()) solves the checkbox challenge, light and dark mode
Smart DOM access — find elements by text, CSS selector, or XPath, including inside iframes; lookups double as wait conditions
Full network & event access — the entire CDP surface is available: requests, responses, events, interception
Stateful when you want it — cookies, localStorage, multi-tab & multi-window, screenshots, external-debugger attach
Tiny, async API — up and running in ~2 lines, with best-practice defaults and automatic profile cleanup
mithwire-mcp — give your AI agents a browser fleet
One mithwire browser is great for a script. But what about an AI agent that needs to run dozens of them? That's mithwire-mcp — a Model Context Protocol server that turns mithwire into a tool your LLM can drive directly.
- Agents launch & control their own browsers —
session_start, navigate, click, type, screenshot, evaluate — all as MCP tools - Fleet management — spin up many isolated sessions at once, each its own browser process, with full lifecycle control
- Durable identities — persistent profiles with their own cookies, proxies, and fingerprints that survive across runs
- Consistent stealth at scale — proxy-aligned timezone/locale/geo, fingerprint spoofing, and WebRTC leak protection kept in sync per worker
- Dual engine support —
engine=cdporengine=stealthper session, mixing modes across a fleet
Install
pip install mithwire
You'll need a Chromium-based browser (Chrome/Brave/Edge) installed, ideally in the
default location. On a headless server, run under Xvfb
or use headless mode. To upgrade:
pip install -U mithwire
Quick start
import mithwire as uc
async def main():
browser = await uc.start()
page = await browser.get("https://www.nowsecure.nl")
await page.save_screenshot()
uc.loop().run_until_complete(main())
Use
uc.loop().run_until_complete(...)instead ofasyncio.run(...)for reliable event-loop handling.
Usage
Custom launch options
from mithwire import start
browser = await start(
headless=False,
user_data_dir="/path/to/profile", # pass one and it won't be auto-cleaned on exit
browser_executable_path="/path/to/some/other/browser",
browser_args=["--some-flag=true"],
lang="en-US",
)
tab = await browser.get("https://example.com")
Or configure via a Config object:
from mithwire import Config
config = Config()
config.headless = False
config.user_data_dir = "/path/to/profile"
config.browser_args = ["--some-flag=true"]
Finding things on the page
# by text — returns the closest match by text length, not the first hit
accept = await tab.find("accept all", best_match=True)
await accept.click()
# by CSS selector (retries until found or <timeout>, so it doubles as a wait)
email = await tab.select("input[type=email]")
imgs = await tab.select_all("a[href] > div > img")
# by XPath
node = await tab.xpath("//button[contains(., 'Next')]", timeout=2.5)
Solving Cloudflare Turnstile
page = await browser.get("https://site-behind-turnstile.example")
await page.verify_cf(max_retries=3, timeout=20, retry_interval=2)
Bundled with light- and dark-mode widget templates and HiDPI-correct coordinates.
Requires opencv-python (pip install opencv-python).
Handy tab helpers
| Method | Does |
|---|---|
tab.get_content() |
current page HTML |
tab.save_screenshot() |
screenshot to a temp file |
tab.scroll_down(n) / tab.scroll_up(n) |
scroll the page |
tab.get_local_storage() / tab.set_local_storage(dict) |
read/write localStorage |
tab.add_handler(event, cb) |
subscribe to CDP events (cb(event) or cb(event, tab)) |
tab.bypass_insecure_connection_warning() |
click through invalid-cert warnings |
tab.open_external_debugger() |
inspect a tab without breaking your connection |
A fuller, runnable script (automating account creation end to end) lives in the
example/ folder.
How it compares
Most "stealth" tools either drive the browser through WebDriver/Selenium (which leaks an obvious automation surface) or patch over it with injected JavaScript (which is itself detectable). mithwire skips both:
- CDP mode speaks the browser's own debug protocol directly — no driver to fingerprint, no injected shim to catch.
- Stealth mode goes further with a patched Chromium binary that modifies fingerprint surfaces at the C++ level — canvas hashes, WebGL renderers, audio contexts, and more are natively randomized, invisible to any JavaScript introspection.
No other open-source Python framework combines both approaches under a single API.
Credits & license
mithwire is a maintained fork of nodriver
by UltrafunkAmsterdam, itself the successor to undetected-chromedriver. It's
distributed under the GNU AGPL-3.0, the same license as upstream. Original
copyright and license are preserved in LICENSE.txt; attribution
details are in NOTICE. mithwire is not affiliated with or endorsed by
the original author.
Stealth mode uses CloakBrowser, a third-party
patched Chromium binary (free to use, not redistributable). The cloakbrowser Python
wrapper is MIT-licensed. See the CloakBrowser project
for binary license details.
This fork addresses real-world limitations hit when running against modern anti-bot systems; fixes land as they're discovered during active use.
Use responsibly. Only automate sites and accounts you're authorized to use, respect Terms of Service and local law, and avoid abusive request rates.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mithwire-0.51.2.tar.gz.
File metadata
- Download URL: mithwire-0.51.2.tar.gz
- Upload date:
- Size: 416.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3a67d2897d492b82bafbdd0d343bbff8e8cb331c29bb1ab64474446632ce140
|
|
| MD5 |
e85330bc2f1d874df6cad737e13a2b91
|
|
| BLAKE2b-256 |
cdad480bfd32377319dee27c3b31f7457bb74eb5d6a646085115cad4c4f514ac
|
Provenance
The following attestation bundles were made for mithwire-0.51.2.tar.gz:
Publisher:
release.yml on codeisalifestyle/mithwire
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mithwire-0.51.2.tar.gz -
Subject digest:
e3a67d2897d492b82bafbdd0d343bbff8e8cb331c29bb1ab64474446632ce140 - Sigstore transparency entry: 2207596804
- Sigstore integration time:
-
Permalink:
codeisalifestyle/mithwire@6aa455034bec91eb9317ed6ada8ece0b04c63eb5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/codeisalifestyle
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6aa455034bec91eb9317ed6ada8ece0b04c63eb5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file mithwire-0.51.2-py3-none-any.whl.
File metadata
- Download URL: mithwire-0.51.2-py3-none-any.whl
- Upload date:
- Size: 438.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c250581699b724a1ba1278ac087d856f1f16d430a96db6b120beb211bc7eea51
|
|
| MD5 |
db26f67162094678f4b9932226a85d9f
|
|
| BLAKE2b-256 |
7cf0b828d260d10bee7a15e5f250c14d2214dd930c869715e4cfba9f9f88af62
|
Provenance
The following attestation bundles were made for mithwire-0.51.2-py3-none-any.whl:
Publisher:
release.yml on codeisalifestyle/mithwire
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mithwire-0.51.2-py3-none-any.whl -
Subject digest:
c250581699b724a1ba1278ac087d856f1f16d430a96db6b120beb211bc7eea51 - Sigstore transparency entry: 2207596834
- Sigstore integration time:
-
Permalink:
codeisalifestyle/mithwire@6aa455034bec91eb9317ed6ada8ece0b04c63eb5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/codeisalifestyle
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6aa455034bec91eb9317ed6ada8ece0b04c63eb5 -
Trigger Event:
push
-
Statement type: