mithwire 0.51.0__tar.gz → 0.51.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. mithwire-0.51.2/MANIFEST.in +5 -0
  2. {mithwire-0.51.0/mithwire.egg-info → mithwire-0.51.2}/PKG-INFO +83 -44
  3. mithwire-0.51.2/README.md +230 -0
  4. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/__init__.py +1 -1
  5. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/browser.py +1 -1
  6. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/config.py +2 -1
  7. mithwire-0.51.2/mithwire/core/virtual_display.py +123 -0
  8. mithwire-0.51.2/mithwire/fingerprint_gen.py +171 -0
  9. mithwire-0.51.2/mithwire/proxy/__init__.py +27 -0
  10. mithwire-0.51.2/mithwire/proxy/config.py +308 -0
  11. mithwire-0.51.2/mithwire/proxy/health.py +333 -0
  12. mithwire-0.51.2/mithwire/proxy/relay.py +191 -0
  13. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/stealth/__init__.py +46 -38
  14. mithwire-0.51.2/mithwire/stealth/cloakbrowser.py +200 -0
  15. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/stealth/controller.py +22 -11
  16. {mithwire-0.51.0 → mithwire-0.51.2/mithwire.egg-info}/PKG-INFO +83 -44
  17. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire.egg-info/SOURCES.txt +8 -0
  18. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire.egg-info/requires.txt +6 -0
  19. {mithwire-0.51.0 → mithwire-0.51.2}/pyproject.toml +4 -2
  20. {mithwire-0.51.0 → mithwire-0.51.2}/tests/test_compute_launch_args.py +28 -0
  21. mithwire-0.51.2/tests/test_proxy.py +167 -0
  22. mithwire-0.51.0/MANIFEST.in +0 -6
  23. mithwire-0.51.0/README.md +0 -195
  24. {mithwire-0.51.0 → mithwire-0.51.2}/LICENSE.txt +0 -0
  25. {mithwire-0.51.0 → mithwire-0.51.2}/NOTICE +0 -0
  26. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/__main__.py +0 -0
  27. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/README.md +0 -0
  28. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/__init__.py +0 -0
  29. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/accessibility.py +0 -0
  30. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/animation.py +0 -0
  31. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/audits.py +0 -0
  32. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/autofill.py +0 -0
  33. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/background_service.py +0 -0
  34. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/bluetooth_emulation.py +0 -0
  35. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/browser.py +0 -0
  36. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/cache_storage.py +0 -0
  37. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/cast.py +0 -0
  38. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/console.py +0 -0
  39. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/crash_report_context.py +0 -0
  40. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/css.py +0 -0
  41. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/database.py +0 -0
  42. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/debugger.py +0 -0
  43. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/device_access.py +0 -0
  44. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/device_orientation.py +0 -0
  45. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/dom.py +0 -0
  46. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/dom_debugger.py +0 -0
  47. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/dom_snapshot.py +0 -0
  48. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/dom_storage.py +0 -0
  49. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/emulation.py +0 -0
  50. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/event_breakpoints.py +0 -0
  51. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/extensions.py +0 -0
  52. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/fed_cm.py +0 -0
  53. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/fetch.py +0 -0
  54. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/file_system.py +0 -0
  55. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/headless_experimental.py +0 -0
  56. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/heap_profiler.py +0 -0
  57. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/indexed_db.py +0 -0
  58. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/input_.py +0 -0
  59. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/inspector.py +0 -0
  60. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/io.py +0 -0
  61. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/layer_tree.py +0 -0
  62. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/log.py +0 -0
  63. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/media.py +0 -0
  64. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/memory.py +0 -0
  65. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/network.py +0 -0
  66. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/overlay.py +0 -0
  67. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/page.py +0 -0
  68. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/performance.py +0 -0
  69. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/performance_timeline.py +0 -0
  70. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/preload.py +0 -0
  71. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/profiler.py +0 -0
  72. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/pwa.py +0 -0
  73. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/py.typed +0 -0
  74. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/runtime.py +0 -0
  75. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/schema.py +0 -0
  76. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/security.py +0 -0
  77. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/service_worker.py +0 -0
  78. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/smart_card_emulation.py +0 -0
  79. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/storage.py +0 -0
  80. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/system_info.py +0 -0
  81. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/target.py +0 -0
  82. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/tethering.py +0 -0
  83. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/tracing.py +0 -0
  84. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/util.py +0 -0
  85. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/web_audio.py +0 -0
  86. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/web_authn.py +0 -0
  87. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/cdp/web_mcp.py +0 -0
  88. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/_contradict.py +0 -0
  89. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/cf_templates/cf_dark_checkbox.png +0 -0
  90. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/cf_templates/cf_light_checkbox.png +0 -0
  91. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/connection.py +0 -0
  92. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/element.py +0 -0
  93. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/tab.py +0 -0
  94. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/core/util.py +0 -0
  95. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/stealth/fingerprint.py +0 -0
  96. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/stealth_diagnostic/__init__.py +0 -0
  97. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/stealth_diagnostic/probes.py +0 -0
  98. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/stealth_diagnostic/report.py +0 -0
  99. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire/stealth_diagnostic/runner.py +0 -0
  100. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire.egg-info/dependency_links.txt +0 -0
  101. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire.egg-info/entry_points.txt +0 -0
  102. {mithwire-0.51.0 → mithwire-0.51.2}/mithwire.egg-info/top_level.txt +0 -0
  103. {mithwire-0.51.0 → mithwire-0.51.2}/setup.cfg +0 -0
  104. {mithwire-0.51.0 → mithwire-0.51.2}/tests/test_connect_retry.py +0 -0
  105. {mithwire-0.51.0 → mithwire-0.51.2}/tests/test_stealth_diagnostic_cli.py +0 -0
  106. {mithwire-0.51.0 → mithwire-0.51.2}/tests/test_stealth_diagnostic_report.py +0 -0
@@ -0,0 +1,5 @@
1
+ include LICENSE.txt
2
+ include NOTICE
3
+ recursive-include mithwire *
4
+ global-exclude */__pycache__/*
5
+ global-exclude *.pyc
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mithwire
3
- Version: 0.51.0
4
- Summary: Stealth browser automation framework that talks directly to Chrome via CDP (no webdriver, no chromedriver). A maintained fork of nodriver.
3
+ Version: 0.51.2
4
+ Summary: Dual-mode anti-detect browser framework CDP-native stealth for local machines, binary-level patching for production servers. No WebDriver, no chromedriver.
5
5
  Author: Elendil Technologies
6
6
  Maintainer: Elendil Technologies
7
7
  License: GNU AFFERO GENERAL PUBLIC LICENSE
@@ -643,6 +643,10 @@ License-File: LICENSE.txt
643
643
  Requires-Dist: mss
644
644
  Requires-Dist: websockets>=14
645
645
  Requires-Dist: deprecated
646
+ Provides-Extra: fingerprints
647
+ Requires-Dist: browserforge; extra == "fingerprints"
648
+ Provides-Extra: stealth
649
+ Requires-Dist: cloakbrowser; extra == "stealth"
646
650
  Provides-Extra: dev
647
651
  Requires-Dist: black; extra == "dev"
648
652
  Requires-Dist: build; extra == "dev"
@@ -660,8 +664,9 @@ Dynamic: license-file
660
664
  </p>
661
665
 
662
666
  <p align="center">
663
- <b>🥷 An anti-detect browser for Python.</b><br>
664
- Drive real Chrome straight over the DevTools Protocol <b>no WebDriver, no Selenium, no chromedriver</b> — with stealth and Cloudflare Turnstile solving built in.
667
+ <b>A dual-mode anti-detect browser framework for Python.</b><br>
668
+ CDP-native stealth for local machines. Binary-level patching for production servers.<br>
669
+ <b>No WebDriver, no Selenium, no chromedriver.</b>
665
670
  </p>
666
671
 
667
672
  <p align="center">
@@ -673,42 +678,67 @@ Dynamic: license-file
673
678
 
674
679
  ---
675
680
 
676
- ## 🤔 What is mithwire?
681
+ ## How it works
677
682
 
678
- **mithwire is an anti-detect browser automation framework.** It launches a normal
679
- Chromium-based browser (Chrome, Brave, Edge…) and controls it by talking directly
680
- to the [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) (CDP).
683
+ mithwire is an anti-detect browser automation framework with **two stealth modes** that cover different deployment scenarios.
681
684
 
682
- There's no automation driver bolted on the side — so the tell-tale signals
683
- anti-bot systems look for to spot WebDriver/Selenium simply **aren't there**. You
684
- get a browser that behaves like a real one, a short and pleasant async API, and
685
- the practical stealth fixes you actually need against modern anti-bot stacks.
685
+ ### CDP mode (default)
686
686
 
687
- ## Features
687
+ The default. mithwire launches a normal Chromium-based browser (Chrome, Brave, Edge) and controls it directly over the [Chrome DevTools Protocol](https://chromedevtools.github.io/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.
688
688
 
689
- 🥷 **Anti-detect by design** speaks raw CDP, so there's **no `navigator.webdriver`, no chromedriver binary, and no Selenium surface** to fingerprint
689
+ 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.
690
690
 
691
- 🌐 **Real Chromium browsers** works with Chrome, Chromium, Brave, and Edge
691
+ **Best for:** local development, macOS/Windows workstations, scripts where the machine's real GPU/fonts/hardware match the presented identity.
692
692
 
693
- 👁️ **Headful or headless** — run with a visible window, or invisibly on a server (pair with `Xvfb` when you need a real display)
693
+ ### Stealth mode (patched binary)
694
694
 
695
- 🎭 **Identity & fingerprint control** full CDP `Emulation` access to shape what the browser presents: timezone, locale, geolocation, user agent, device metrics, and more
695
+ 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](https://github.com/CloakHQ/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.
696
696
 
697
- 🌍 **Proxy support**route traffic through upstream HTTP/SOCKS proxies
697
+ 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).
698
698
 
699
- 🛡️ **Cloudflare Turnstile bypass** one call (`tab.verify_cf()`) solves the checkbox challenge, light *and* dark mode
699
+ **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.
700
700
 
701
- 🔎 **Smart DOM access** — find elements by **text, CSS selector, or XPath**, including inside iframes — lookups double as wait conditions
701
+ ### Which mode to use
702
702
 
703
- 📡 **Full network & event access** — the entire CDP surface is yours: requests, responses, events, interception
703
+ | Scenario | Mode | Why |
704
+ | --- | --- | --- |
705
+ | Scripting on your Mac/Windows laptop | **CDP** | Host hardware is real — no need to lie about GPU, fonts, or canvas |
706
+ | Headless server, no spoofing needed | **CDP** | CDP stealth is sufficient when the profile matches the machine |
707
+ | Production VPS scraping/automation | **Stealth** | Linux servers expose SwiftShader, minimal fonts, headless signals — the binary patches these at the source |
708
+ | Advanced anti-bot targets (CreepJS, DAB) | **Stealth** | Deep fingerprint surfaces (canvas hash, WebGL renderer, audio context) need native-level randomization |
709
+ | AI agent fleet via mithwire-mcp | **Either** | CDP for local agents, stealth for cloud/VPS agents — [mithwire-mcp](https://github.com/codeisalifestyle/mithwire-mcp) supports both via `engine` parameter |
704
710
 
705
- 🍪 **Stateful when you want it** cookies, localStorage, multi-tab & multi-window, screenshots, and external-debugger attach
711
+ > Stealth mode requires the `cloakbrowser` package and is currently Linux-only for the latest free builds. See [mithwire-mcp](https://github.com/codeisalifestyle/mithwire-mcp) for the full stealth integration (session management, proxy alignment, profile persistence, and fleet orchestration).
706
712
 
707
- ⚡ **Tiny, async API** — up and running in ~2 lines, with best-practice defaults and automatic profile cleanup
713
+ ---
714
+
715
+ ## Features
716
+
717
+ **Anti-detect by design** — speaks raw CDP: no `navigator.webdriver`, no chromedriver binary, no Selenium surface to fingerprint
718
+
719
+ **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)
720
+
721
+ **Real Chromium browsers** — works with Chrome, Chromium, Brave, Edge, and patched CloakBrowser binaries
722
+
723
+ **Headful or headless** — visible window or invisible server mode; pairs with `Xvfb` for headed-on-headless Linux deployments
724
+
725
+ **Identity & fingerprint control** — timezone, locale, geolocation, user agent, device metrics, screen dimensions, and more via CDP Emulation
726
+
727
+ **Proxy support** — HTTP/HTTPS/SOCKS upstream proxies with automatic authenticating relay for credential-bearing HTTP proxies
728
+
729
+ **Cloudflare Turnstile bypass** — one call (`tab.verify_cf()`) solves the checkbox challenge, light and dark mode
730
+
731
+ **Smart DOM access** — find elements by text, CSS selector, or XPath, including inside iframes; lookups double as wait conditions
732
+
733
+ **Full network & event access** — the entire CDP surface is available: requests, responses, events, interception
734
+
735
+ **Stateful when you want it** — cookies, localStorage, multi-tab & multi-window, screenshots, external-debugger attach
736
+
737
+ **Tiny, async API** — up and running in ~2 lines, with best-practice defaults and automatic profile cleanup
708
738
 
709
739
  ---
710
740
 
711
- ## 🤖 mithwire-mcp — give your AI agents a browser fleet
741
+ ## mithwire-mcp — give your AI agents a browser fleet
712
742
 
713
743
  <p align="center">
714
744
  <a href="https://github.com/codeisalifestyle/mithwire-mcp">
@@ -721,16 +751,17 @@ needs to run dozens of them**? That's [**mithwire-mcp**](https://github.com/code
721
751
  a [Model Context Protocol](https://modelcontextprotocol.io) server that turns
722
752
  mithwire into a tool your LLM can drive directly.
723
753
 
724
- - 🧠 **Agents launch & control their own browsers** — `session_start`, navigate, click, type, screenshot, evaluate — all as MCP tools
725
- - 🚀 **Fleet management** — spin up many isolated sessions at once, each its own browser process, with full lifecycle control
726
- - 👤 **Durable identities** — persistent profiles with their own cookies, proxies, and fingerprints that survive across runs
727
- - 🕵️ **Consistent stealth at scale** — proxy-aligned timezone/locale/geo, fingerprint spoofing, and WebRTC leak protection kept in sync per worker
754
+ - **Agents launch & control their own browsers** — `session_start`, navigate, click, type, screenshot, evaluate — all as MCP tools
755
+ - **Fleet management** — spin up many isolated sessions at once, each its own browser process, with full lifecycle control
756
+ - **Durable identities** — persistent profiles with their own cookies, proxies, and fingerprints that survive across runs
757
+ - **Consistent stealth at scale** — proxy-aligned timezone/locale/geo, fingerprint spoofing, and WebRTC leak protection kept in sync per worker
758
+ - **Dual engine support** — `engine=cdp` or `engine=stealth` per session, mixing modes across a fleet
728
759
 
729
- ➡️ **[Get started with mithwire-mcp](https://github.com/codeisalifestyle/mithwire-mcp)**
760
+ [**Get started with mithwire-mcp**](https://github.com/codeisalifestyle/mithwire-mcp)
730
761
 
731
762
  ---
732
763
 
733
- ## 📦 Install
764
+ ## Install
734
765
 
735
766
  ```bash
736
767
  pip install mithwire
@@ -744,7 +775,7 @@ or use headless mode. To upgrade:
744
775
  pip install -U mithwire
745
776
  ```
746
777
 
747
- ## 🚀 Quick start
778
+ ## Quick start
748
779
 
749
780
  ```python
750
781
  import mithwire as uc
@@ -757,12 +788,12 @@ async def main():
757
788
  uc.loop().run_until_complete(main())
758
789
  ```
759
790
 
760
- > 💡 Use `uc.loop().run_until_complete(...)` instead of `asyncio.run(...)` for
791
+ > Use `uc.loop().run_until_complete(...)` instead of `asyncio.run(...)` for
761
792
  > reliable event-loop handling.
762
793
 
763
- ## 🧭 Usage
794
+ ## Usage
764
795
 
765
- ### ⚙️ Custom launch options
796
+ ### Custom launch options
766
797
 
767
798
  ```python
768
799
  from mithwire import start
@@ -788,7 +819,7 @@ config.user_data_dir = "/path/to/profile"
788
819
  config.browser_args = ["--some-flag=true"]
789
820
  ```
790
821
 
791
- ### 🔎 Finding things on the page
822
+ ### Finding things on the page
792
823
 
793
824
  ```python
794
825
  # by text — returns the closest match by text length, not the first hit
@@ -803,7 +834,7 @@ imgs = await tab.select_all("a[href] > div > img")
803
834
  node = await tab.xpath("//button[contains(., 'Next')]", timeout=2.5)
804
835
  ```
805
836
 
806
- ### 🛡️ Solving Cloudflare Turnstile
837
+ ### Solving Cloudflare Turnstile
807
838
 
808
839
  ```python
809
840
  page = await browser.get("https://site-behind-turnstile.example")
@@ -813,7 +844,7 @@ await page.verify_cf(max_retries=3, timeout=20, retry_interval=2)
813
844
  Bundled with light- and dark-mode widget templates and HiDPI-correct coordinates.
814
845
  Requires `opencv-python` (`pip install opencv-python`).
815
846
 
816
- ### 🧰 Handy tab helpers
847
+ ### Handy tab helpers
817
848
 
818
849
  | Method | Does |
819
850
  | --- | --- |
@@ -826,17 +857,20 @@ Requires `opencv-python` (`pip install opencv-python`).
826
857
  | `tab.open_external_debugger()` | inspect a tab without breaking your connection |
827
858
 
828
859
  A fuller, runnable script (automating account creation end to end) lives in the
829
- [`example/`](example/) folder. 📂
860
+ [`example/`](example/) folder.
830
861
 
831
- ## ⚖️ How it compares
862
+ ## How it compares
832
863
 
833
864
  Most "stealth" tools either drive the browser through WebDriver/Selenium (which
834
865
  leaks an obvious automation surface) or patch over it with injected JavaScript
835
- (which is itself detectable). mithwire skips both: it speaks the browser's own
836
- debug protocol directly, so there's **no driver to fingerprint and no injected
837
- shim to catch** while still giving you the full power of CDP for low-level control.
866
+ (which is itself detectable). mithwire skips both:
867
+
868
+ - **CDP mode** speaks the browser's own debug protocol directly no driver to fingerprint, no injected shim to catch.
869
+ - **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.
838
870
 
839
- ## 🙏 Credits & license
871
+ No other open-source Python framework combines both approaches under a single API.
872
+
873
+ ## Credits & license
840
874
 
841
875
  mithwire is a maintained fork of [**nodriver**](https://github.com/UltrafunkAmsterdam/nodriver)
842
876
  by UltrafunkAmsterdam, itself the successor to undetected-chromedriver. It's
@@ -845,8 +879,13 @@ copyright and license are preserved in [`LICENSE.txt`](LICENSE.txt); attribution
845
879
  details are in [`NOTICE`](NOTICE). mithwire is not affiliated with or endorsed by
846
880
  the original author.
847
881
 
882
+ Stealth mode uses [CloakBrowser](https://github.com/CloakHQ/CloakBrowser), a third-party
883
+ patched Chromium binary (free to use, not redistributable). The `cloakbrowser` Python
884
+ wrapper is MIT-licensed. See the [CloakBrowser project](https://github.com/CloakHQ/CloakBrowser)
885
+ for binary license details.
886
+
848
887
  This fork addresses real-world limitations hit when running against modern
849
888
  anti-bot systems; fixes land as they're discovered during active use.
850
889
 
851
- > ⚠️ **Use responsibly.** Only automate sites and accounts you're authorized to use,
890
+ > **Use responsibly.** Only automate sites and accounts you're authorized to use,
852
891
  > respect Terms of Service and local law, and avoid abusive request rates.
@@ -0,0 +1,230 @@
1
+ <p align="center">
2
+ <img src="assets/mithwire-banner.png" alt="mithwire" width="760">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <b>A dual-mode anti-detect browser framework for Python.</b><br>
7
+ CDP-native stealth for local machines. Binary-level patching for production servers.<br>
8
+ <b>No WebDriver, no Selenium, no chromedriver.</b>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://pypi.org/project/mithwire/"><img src="https://img.shields.io/pypi/v/mithwire?style=for-the-badge&color=d62839&label=pip%20install%20mithwire" alt="PyPI"></a>
13
+ <a href="https://pypi.org/project/mithwire/"><img src="https://img.shields.io/pypi/pyversions/mithwire?style=for-the-badge&color=3776ab&logo=python&logoColor=white" alt="Python versions"></a>
14
+ <a href="LICENSE.txt"><img src="https://img.shields.io/badge/license-AGPL--3.0-2ea44f?style=for-the-badge" alt="License"></a>
15
+ <a href="https://github.com/codeisalifestyle/mithwire-mcp"><img src="https://img.shields.io/badge/🤖_agents-mithwire--mcp-d62839?style=for-the-badge" alt="mithwire-mcp"></a>
16
+ </p>
17
+
18
+ ---
19
+
20
+ ## How it works
21
+
22
+ mithwire is an anti-detect browser automation framework with **two stealth modes** that cover different deployment scenarios.
23
+
24
+ ### CDP mode (default)
25
+
26
+ The default. mithwire launches a normal Chromium-based browser (Chrome, Brave, Edge) and controls it directly over the [Chrome DevTools Protocol](https://chromedevtools.github.io/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.
27
+
28
+ 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.
29
+
30
+ **Best for:** local development, macOS/Windows workstations, scripts where the machine's real GPU/fonts/hardware match the presented identity.
31
+
32
+ ### Stealth mode (patched binary)
33
+
34
+ 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](https://github.com/CloakHQ/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.
35
+
36
+ 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).
37
+
38
+ **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.
39
+
40
+ ### Which mode to use
41
+
42
+ | Scenario | Mode | Why |
43
+ | --- | --- | --- |
44
+ | Scripting on your Mac/Windows laptop | **CDP** | Host hardware is real — no need to lie about GPU, fonts, or canvas |
45
+ | Headless server, no spoofing needed | **CDP** | CDP stealth is sufficient when the profile matches the machine |
46
+ | Production VPS scraping/automation | **Stealth** | Linux servers expose SwiftShader, minimal fonts, headless signals — the binary patches these at the source |
47
+ | Advanced anti-bot targets (CreepJS, DAB) | **Stealth** | Deep fingerprint surfaces (canvas hash, WebGL renderer, audio context) need native-level randomization |
48
+ | AI agent fleet via mithwire-mcp | **Either** | CDP for local agents, stealth for cloud/VPS agents — [mithwire-mcp](https://github.com/codeisalifestyle/mithwire-mcp) supports both via `engine` parameter |
49
+
50
+ > Stealth mode requires the `cloakbrowser` package and is currently Linux-only for the latest free builds. See [mithwire-mcp](https://github.com/codeisalifestyle/mithwire-mcp) for the full stealth integration (session management, proxy alignment, profile persistence, and fleet orchestration).
51
+
52
+ ---
53
+
54
+ ## Features
55
+
56
+ **Anti-detect by design** — speaks raw CDP: no `navigator.webdriver`, no chromedriver binary, no Selenium surface to fingerprint
57
+
58
+ **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)
59
+
60
+ **Real Chromium browsers** — works with Chrome, Chromium, Brave, Edge, and patched CloakBrowser binaries
61
+
62
+ **Headful or headless** — visible window or invisible server mode; pairs with `Xvfb` for headed-on-headless Linux deployments
63
+
64
+ **Identity & fingerprint control** — timezone, locale, geolocation, user agent, device metrics, screen dimensions, and more via CDP Emulation
65
+
66
+ **Proxy support** — HTTP/HTTPS/SOCKS upstream proxies with automatic authenticating relay for credential-bearing HTTP proxies
67
+
68
+ **Cloudflare Turnstile bypass** — one call (`tab.verify_cf()`) solves the checkbox challenge, light and dark mode
69
+
70
+ **Smart DOM access** — find elements by text, CSS selector, or XPath, including inside iframes; lookups double as wait conditions
71
+
72
+ **Full network & event access** — the entire CDP surface is available: requests, responses, events, interception
73
+
74
+ **Stateful when you want it** — cookies, localStorage, multi-tab & multi-window, screenshots, external-debugger attach
75
+
76
+ **Tiny, async API** — up and running in ~2 lines, with best-practice defaults and automatic profile cleanup
77
+
78
+ ---
79
+
80
+ ## mithwire-mcp — give your AI agents a browser fleet
81
+
82
+ <p align="center">
83
+ <a href="https://github.com/codeisalifestyle/mithwire-mcp">
84
+ <img src="assets/mithwire-mcp-banner.jpg" alt="mithwire-mcp" width="640">
85
+ </a>
86
+ </p>
87
+
88
+ One mithwire browser is great for a script. But what about an **AI agent that
89
+ needs to run dozens of them**? That's [**mithwire-mcp**](https://github.com/codeisalifestyle/mithwire-mcp) —
90
+ a [Model Context Protocol](https://modelcontextprotocol.io) server that turns
91
+ mithwire into a tool your LLM can drive directly.
92
+
93
+ - **Agents launch & control their own browsers** — `session_start`, navigate, click, type, screenshot, evaluate — all as MCP tools
94
+ - **Fleet management** — spin up many isolated sessions at once, each its own browser process, with full lifecycle control
95
+ - **Durable identities** — persistent profiles with their own cookies, proxies, and fingerprints that survive across runs
96
+ - **Consistent stealth at scale** — proxy-aligned timezone/locale/geo, fingerprint spoofing, and WebRTC leak protection kept in sync per worker
97
+ - **Dual engine support** — `engine=cdp` or `engine=stealth` per session, mixing modes across a fleet
98
+
99
+ [**Get started with mithwire-mcp**](https://github.com/codeisalifestyle/mithwire-mcp)
100
+
101
+ ---
102
+
103
+ ## Install
104
+
105
+ ```bash
106
+ pip install mithwire
107
+ ```
108
+
109
+ You'll need a Chromium-based browser (Chrome/Brave/Edge) installed, ideally in the
110
+ default location. On a headless server, run under [`Xvfb`](https://en.wikipedia.org/wiki/Xvfb)
111
+ or use headless mode. To upgrade:
112
+
113
+ ```bash
114
+ pip install -U mithwire
115
+ ```
116
+
117
+ ## Quick start
118
+
119
+ ```python
120
+ import mithwire as uc
121
+
122
+ async def main():
123
+ browser = await uc.start()
124
+ page = await browser.get("https://www.nowsecure.nl")
125
+ await page.save_screenshot()
126
+
127
+ uc.loop().run_until_complete(main())
128
+ ```
129
+
130
+ > Use `uc.loop().run_until_complete(...)` instead of `asyncio.run(...)` for
131
+ > reliable event-loop handling.
132
+
133
+ ## Usage
134
+
135
+ ### Custom launch options
136
+
137
+ ```python
138
+ from mithwire import start
139
+
140
+ browser = await start(
141
+ headless=False,
142
+ user_data_dir="/path/to/profile", # pass one and it won't be auto-cleaned on exit
143
+ browser_executable_path="/path/to/some/other/browser",
144
+ browser_args=["--some-flag=true"],
145
+ lang="en-US",
146
+ )
147
+ tab = await browser.get("https://example.com")
148
+ ```
149
+
150
+ Or configure via a `Config` object:
151
+
152
+ ```python
153
+ from mithwire import Config
154
+
155
+ config = Config()
156
+ config.headless = False
157
+ config.user_data_dir = "/path/to/profile"
158
+ config.browser_args = ["--some-flag=true"]
159
+ ```
160
+
161
+ ### Finding things on the page
162
+
163
+ ```python
164
+ # by text — returns the closest match by text length, not the first hit
165
+ accept = await tab.find("accept all", best_match=True)
166
+ await accept.click()
167
+
168
+ # by CSS selector (retries until found or <timeout>, so it doubles as a wait)
169
+ email = await tab.select("input[type=email]")
170
+ imgs = await tab.select_all("a[href] > div > img")
171
+
172
+ # by XPath
173
+ node = await tab.xpath("//button[contains(., 'Next')]", timeout=2.5)
174
+ ```
175
+
176
+ ### Solving Cloudflare Turnstile
177
+
178
+ ```python
179
+ page = await browser.get("https://site-behind-turnstile.example")
180
+ await page.verify_cf(max_retries=3, timeout=20, retry_interval=2)
181
+ ```
182
+
183
+ Bundled with light- and dark-mode widget templates and HiDPI-correct coordinates.
184
+ Requires `opencv-python` (`pip install opencv-python`).
185
+
186
+ ### Handy tab helpers
187
+
188
+ | Method | Does |
189
+ | --- | --- |
190
+ | `tab.get_content()` | current page HTML |
191
+ | `tab.save_screenshot()` | screenshot to a temp file |
192
+ | `tab.scroll_down(n)` / `tab.scroll_up(n)` | scroll the page |
193
+ | `tab.get_local_storage()` / `tab.set_local_storage(dict)` | read/write localStorage |
194
+ | `tab.add_handler(event, cb)` | subscribe to CDP events (`cb(event)` or `cb(event, tab)`) |
195
+ | `tab.bypass_insecure_connection_warning()` | click through invalid-cert warnings |
196
+ | `tab.open_external_debugger()` | inspect a tab without breaking your connection |
197
+
198
+ A fuller, runnable script (automating account creation end to end) lives in the
199
+ [`example/`](example/) folder.
200
+
201
+ ## How it compares
202
+
203
+ Most "stealth" tools either drive the browser through WebDriver/Selenium (which
204
+ leaks an obvious automation surface) or patch over it with injected JavaScript
205
+ (which is itself detectable). mithwire skips both:
206
+
207
+ - **CDP mode** speaks the browser's own debug protocol directly — no driver to fingerprint, no injected shim to catch.
208
+ - **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.
209
+
210
+ No other open-source Python framework combines both approaches under a single API.
211
+
212
+ ## Credits & license
213
+
214
+ mithwire is a maintained fork of [**nodriver**](https://github.com/UltrafunkAmsterdam/nodriver)
215
+ by UltrafunkAmsterdam, itself the successor to undetected-chromedriver. It's
216
+ distributed under the **GNU AGPL-3.0**, the same license as upstream. Original
217
+ copyright and license are preserved in [`LICENSE.txt`](LICENSE.txt); attribution
218
+ details are in [`NOTICE`](NOTICE). mithwire is not affiliated with or endorsed by
219
+ the original author.
220
+
221
+ Stealth mode uses [CloakBrowser](https://github.com/CloakHQ/CloakBrowser), a third-party
222
+ patched Chromium binary (free to use, not redistributable). The `cloakbrowser` Python
223
+ wrapper is MIT-licensed. See the [CloakBrowser project](https://github.com/CloakHQ/CloakBrowser)
224
+ for binary license details.
225
+
226
+ This fork addresses real-world limitations hit when running against modern
227
+ anti-bot systems; fixes land as they're discovered during active use.
228
+
229
+ > **Use responsibly.** Only automate sites and accounts you're authorized to use,
230
+ > respect Terms of Service and local law, and avoid abusive request rates.
@@ -36,4 +36,4 @@ __all__ = [
36
36
  "run_stealth_diagnostic",
37
37
  ]
38
38
 
39
- __version__ = "0.50.8"
39
+ __version__ = "0.51.2"
@@ -440,7 +440,7 @@ class Browser(Connection):
440
440
  webrtc_leak_protection=getattr(config, "webrtc_leak_protection", "auto"),
441
441
  headless=bool(getattr(config, "headless", False)),
442
442
  proxied=proxied,
443
- engine=getattr(config, "engine", "stock"),
443
+ engine=getattr(config, "engine", "cdp"),
444
444
  )
445
445
  # A freshly attached tab needs a brief moment before CDP overrides and
446
446
  # new-document scripts reliably register on the about:blank target.
@@ -122,7 +122,7 @@ class Config:
122
122
  else:
123
123
  self.fingerprint = FingerprintConfig.from_dict(fingerprint)
124
124
  self.webrtc_leak_protection = webrtc_leak_protection
125
- self.engine = kwargs.pop("engine", "stock")
125
+ self.engine = kwargs.pop("engine", "cdp")
126
126
 
127
127
  # other keyword args will be accessible by attribute
128
128
  self.__dict__.update(kwargs)
@@ -151,6 +151,7 @@ class Config:
151
151
  fingerprint=self.fingerprint,
152
152
  headless=self.headless,
153
153
  browser_executable_path=self.browser_executable_path,
154
+ engine=self.engine,
154
155
  ):
155
156
  if arg not in self._browser_args:
156
157
  self._browser_args.append(arg)
@@ -0,0 +1,123 @@
1
+ """Automatic Xvfb (virtual display) management for headed browser mode.
2
+
3
+ On a headless Linux server, Chrome cannot run in headed mode without a display.
4
+ Xvfb provides a virtual X11 framebuffer that makes Chrome behave exactly like a
5
+ real desktop browser — natural toolbar height, proper window chrome, and correct
6
+ storage/permission API behaviour — while consuming minimal resources.
7
+
8
+ Usage::
9
+
10
+ display = ensure_virtual_display()
11
+ # display is ":99" and DISPLAY env var is set
12
+
13
+ The module is idempotent: calling ``ensure_virtual_display`` multiple times
14
+ reuses the existing Xvfb process.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import atexit
20
+ import logging
21
+ import os
22
+ import shutil
23
+ import subprocess
24
+ import sys
25
+ import time
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+ _xvfb_process: subprocess.Popen | None = None
30
+ _display: str | None = None
31
+
32
+ XVFB_DISPLAY = ":99"
33
+ XVFB_SCREEN = "1920x1080x24"
34
+
35
+
36
+ def _is_display_available() -> bool:
37
+ """Return True if a usable DISPLAY is already set."""
38
+ display = os.environ.get("DISPLAY")
39
+ return bool(display and display.strip())
40
+
41
+
42
+ def _xvfb_available() -> bool:
43
+ """Return True if the Xvfb binary is on PATH."""
44
+ return shutil.which("Xvfb") is not None
45
+
46
+
47
+ def _cleanup() -> None:
48
+ """Kill the managed Xvfb process on interpreter exit."""
49
+ global _xvfb_process
50
+ if _xvfb_process is not None:
51
+ try:
52
+ _xvfb_process.terminate()
53
+ _xvfb_process.wait(timeout=3)
54
+ except Exception:
55
+ try:
56
+ _xvfb_process.kill()
57
+ except Exception:
58
+ pass
59
+ _xvfb_process = None
60
+
61
+
62
+ def ensure_virtual_display(
63
+ *,
64
+ display: str = XVFB_DISPLAY,
65
+ screen: str = XVFB_SCREEN,
66
+ ) -> str | None:
67
+ """Start Xvfb if needed and return the DISPLAY string.
68
+
69
+ Returns None on non-Linux platforms, when a display already exists,
70
+ or when Xvfb is not installed.
71
+ """
72
+ global _xvfb_process, _display
73
+
74
+ if not sys.platform.startswith("linux"):
75
+ return os.environ.get("DISPLAY")
76
+
77
+ if _is_display_available():
78
+ return os.environ["DISPLAY"]
79
+
80
+ if _display is not None and _xvfb_process is not None:
81
+ if _xvfb_process.poll() is None:
82
+ return _display
83
+
84
+ if not _xvfb_available():
85
+ logger.warning(
86
+ "Xvfb not found — headed browser sessions on a displayless server "
87
+ "will fail. Install with: apt-get install xvfb"
88
+ )
89
+ return None
90
+
91
+ try:
92
+ _xvfb_process = subprocess.Popen(
93
+ [
94
+ "Xvfb", display,
95
+ "-screen", "0", screen,
96
+ "-ac",
97
+ "+extension", "GLX",
98
+ "-noreset",
99
+ ],
100
+ stdout=subprocess.DEVNULL,
101
+ stderr=subprocess.DEVNULL,
102
+ )
103
+ time.sleep(1.0)
104
+
105
+ if _xvfb_process.poll() is not None:
106
+ logger.warning("Xvfb exited immediately (display %s may be in use)", display)
107
+ _xvfb_process = None
108
+ return None
109
+
110
+ os.environ["DISPLAY"] = display
111
+ _display = display
112
+ atexit.register(_cleanup)
113
+ logger.info("Xvfb started on %s (%s)", display, screen)
114
+ return display
115
+
116
+ except Exception as exc:
117
+ logger.warning("Failed to start Xvfb: %s", exc)
118
+ return None
119
+
120
+
121
+ def stop_virtual_display() -> None:
122
+ """Stop the managed Xvfb process."""
123
+ _cleanup()