Metadata-Version: 2.4
Name: stealthhttp
Version: 0.7.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Rust
Classifier: Topic :: Internet :: WWW/HTTP
Requires-Dist: wreq>=2.0 ; extra == 'all'
Requires-Dist: curl-cffi>=0.7 ; extra == 'all'
Requires-Dist: curl-cffi>=0.7 ; extra == 'curl'
Requires-Dist: pytest>=7.0 ; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
Requires-Dist: maturin>=1.5 ; extra == 'dev'
Requires-Dist: wreq>=2.0 ; extra == 'wreq'
Provides-Extra: all
Provides-Extra: curl
Provides-Extra: dev
Provides-Extra: wreq
License-File: LICENSE
Summary: HTTP with TLS fingerprint impersonation, rotation and proxy health tracking
Keywords: http,tls,ja3,ja4,fingerprint,impersonate,scraping,proxy
License: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# stealthhttp

A standalone HTTP client with TLS fingerprint impersonation — its own BoringSSL ClientHello and HTTP/1.1+2 stack, not a wrapper around `wreq`/`curl_cffi` — plus rotation, proxy health, behaviour emulation, and a captcha-solving orchestration layer.

```python
from stealthhttp import Session

s = Session(browser="chrome138", proxy="http://user:pass@ip:port")
r = s.get("https://example.com", timeout=15)
print(r.status_code, r.text)

print(s.ja3_hash, s.ja4)
```

## What this actually is

`src/transport/` builds the ClientHello from a `Profile` directly against BoringSSL (`boring` 5), runs HTTP/1.1 and HTTP/2 over `hyper`, and drives everything on a Tokio runtime with its own connection pool — the layer reqwest and curl occupy. wreq and curl_cffi are optional fallbacks for the handful of things the native engine doesn't implement yet (streaming bodies, WebSocket, SOCKS5, HTTP/3); the capability router picks them automatically and nothing degrades silently.

| Layer | Owner |
|---|---|
| ClientHello, HTTP/1.1+2, connection pool, batch execution | native engine (Rust: `boring` + `hyper` + Tokio) |
| Profile database, JA3/JA4, header consistency | stealthhttp core (Rust, pure-Python fallback) |
| Rotation, proxy health, pacing, retries, cookies, behaviour, captcha orchestration | stealthhttp (Python) |
| Streaming, WebSocket, SOCKS5, HTTP/3 | wreq / curl_cffi (fallback only) |

### Byte-exact against a live capture

```
observed  t13d1516h2_8daaf6152771_d8a2da3f94cd
expected  t13d1516h2_8daaf6152771_d8a2da3f94cd
missing [] extra []
groups: GREASE, X25519MLKEM768 (4588), X25519, P-256, P-384
```

15 ciphers, 16 extensions, GREASE, BoringSSL's per-connection extension permutation, ALPS at codepoint 17613 (the one Chrome has used since v124), ECH GREASE, brotli certificate compression — all from a ClientHello this library builds, not one borrowed from another client.

### Measured, not claimed

Batch of 60 requests, 12 Python threads for the thread-based comparisons:

| client | wall | req/s | |
|---|---|---|---|
| **native engine (raw batch)** | 0.11s | 539.7 | best |
| **`Session.fetch_many`** (full pipeline) | 0.14s | 437.9 | 1.23x |
| wreq + 12 threads | 0.44s | 134.9 | 4.0x slower |
| curl_cffi + 12 threads | 1.47s | 40.9 | 13.2x slower |
| curl_cffi sequential | 5.25s | 11.4 | 47.2x slower |
| wreq sequential | 5.46s | 11.0 | 49.1x slower |

All 60 requests in the batch rode a single pooled HTTP/2 connection. The win isn't a faster socket — one request is one round trip, and every client ties there. It's that a whole batch crosses into Rust once: one GIL release, with concurrency, connection reuse and stream multiplexing decided in Rust rather than N round trips with the interpreter scheduling each one. `curl_cffi` gains nothing from threads because libcurl handles aren't thread-safe, so correct usage means a session — and a TLS handshake — per thread.

Run it yourself: `python benchmarks/bench_native.py --n 80 --workers 16`.

### Live test results (v0.6.2)

Tested against real sites, no proxy, native engine only:

| Target | WAF | Status | Result |
|---|---|---|---|
| cloudflare.com | Cloudflare | 200 | pass |
| discord.com | Cloudflare | 200 | pass |
| nike.com | Akamai Bot Manager | 200 | pass |
| hermes.com | DataDome | 200 | pass |
| incapsula.com | Imperva | 200 | pass |
| wikipedia.org | — | 200 | pass |
| example.com | — | 200 | pass |
| httpbin.org | — | 200 | pass |

Fingerprint verification (tls.peet.ws, browserleaks.com, howsmyssl.com): JA4 exact match, TLS 1.3, 16 ciphers, 18 extensions, GREASE rotation, correct HTTP/2 Akamai fingerprint — 5/5 checks passed.

Sites requiring a JS engine (Kasada, PerimeterX captcha, Shape) are expected to block at the HTTP layer — use the CDP solver or `BrowserBridge` for those.

### Known gaps, stated rather than hidden

- Firefox lacks `record_size_limit` (28) and `delegated_credentials` (34) — `boring` exposes no API for either yet.
- HTTPS through a proxy needs a CONNECT tunnel; done by hand (TLS must start *after* the tunnel, or the proxy's cert and the destination's SNI both leak) — see `src/transport/engine.rs`.
- Streaming responses, WebSocket, SOCKS5 and HTTP/3 aren't implemented natively; the router falls back to wreq/curl_cffi for those, loudly (`CapabilityUnsupported`) if neither is installed.

## Install

```bash
pip install maturin
maturin develop --release          # builds the native transport
python -c "import stealthhttp._core as c; print(c.HAS_TRANSPORT)"   # True
```

Building needs a Rust toolchain and `cmake` (`pip install cmake` is enough — no Go, no system BoringSSL). Without a compiled core the package still runs on a pure-Python fingerprint mirror plus wreq/curl_cffi if either is installed — `stealthhttp.NATIVE` and `available_backends()` report what's active.

## Features

### Fingerprint consistency engine

Everything visible at the HTTP layer is *derived from the same profile* that drives TLS, so the two can never disagree. This is the most common way a scraper gives itself away: a Chrome 136 ClientHello paired with a Chrome 120 `sec-ch-ua`, or a Firefox JA3 sending client hints at all (Firefox doesn't implement them).

```python
>>> from stealthhttp import resolve_profile
>>> resolve_profile("chrome138").sec_ch_ua
'"Not:A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"'
>>> resolve_profile("firefox144").sec_ch_ua      # correctly sends none
None
```

The Chromium `sec-ch-ua` GREASE algorithm — brand permutation, GREASE punctuation, GREASE version — is derived from the major version, verified byte-for-byte against live Chrome 131–138, so it produces correct hints for versions no library ships yet.

### Request kinds: fetch metadata that matches what's being fetched

A browser sends a different `sec-fetch-*`/`accept`/`priority` group for a navigation than for an XHR. Sending the navigation set on an API call is a common, easily-filtered mistake.

```python
s.get(api_url, kind="xhr")       # sec-fetch-mode: cors, accept: */*, no upgrade-insecure-requests
s.get(img_url, kind="image")     # sec-fetch-dest: image, image accept list, priority: u=2, i
s.browse(page_url)               # fetches the document, then its stylesheets/scripts/images/favicon
```

`Session.browse()` exists because a "browser" that fetches HTML and never a stylesheet, script, font, image or favicon is the loudest behavioural signal there is.

### Offline JA3/JA4 self-check

```python
>>> from stealthhttp import predict, verify
>>> predict("chrome138").ja4
't13d1516h2_8daaf6152771_d8a2da3f94cd'   # no network
>>> verify("chrome138").ok                # compares against a live capture
True
```

> Chrome has shuffled its ClientHello extension order since v110, so its JA3 hash legitimately changes per connection — a *fixed* JA3 is the anomaly. JA4 sorts its inputs and stays stable; prefer it.

### Rotation, proxy health, pacing, retries

```python
s = Session(
    browser=["chrome138", "firefox144", "edge138"], rotate="sticky_per_domain",
    proxy_pool=["http://p1", "http://p2", "http://p3"],
    delay=(0.4, 1.8), jitter_distribution="lognormal",
    retries=3, circuit_breaker=True,
)
```

Sticky rotation keeps one browser and one proxy per host, since a clearance cookie is bound to the IP and User-Agent that earned it — `ChallengeTracker` pins that pair once a `cf_clearance`/`datadome`/etc. cookie shows up and refuses to rotate it away. Consecutive proxy failures bench it for a doubling cooldown; a detected ban takes it out until `revive_all()`. Retries respect `Retry-After`, skip POST by default, and a circuit breaker stops hammering a host that's down.

### Batch execution

```python
responses = s.fetch_many([url1, url2, ..., url100])   # one GIL crossing, one pooled connection
```

Each request still goes through rotation, coherent headers, cookies and proxy selection — the whole batch just crosses into Rust once instead of once per request.

### DNS pinning

```python
s = Session(resolve={"example.com": "1.2.3.4"})   # connects there; SNI/Host still say example.com
```

Owning the TCP connect makes this possible. `wreq` accepts `resolve=` and silently ignores it — this was measured, not assumed, and is why `CapabilityUnsupported` exists rather than a request that quietly goes to the wrong place.

### Identity: persistent, and importable from a real browser

```python
from stealthhttp import Identity

ident = Identity.from_storage_state("cookies_export.json", browser="chrome138", proxy=proxy)
s = Session(identity=ident)
...
s.save_identity("account-42.json")   # cookies + clearance pins, ready to resume
```

`from_storage_state` and `from_cookies_txt` are browser-agnostic — they read a cookie export, not caring whether it came from Playwright, plain Chrome, or an antidetect browser. That's the hand-off this is built for: solve an interactive challenge once, for real, then keep going at HTTP speed with the identity that earned clearance.

### Humanization: session-level timing and travel plausibility

`Jitter` paces gaps *within* a session. Antifraud engines (Sift, Forter, Stripe Radar) also watch what only shows up *across* sessions — what hours an account is normally active, and whether it appears somewhere travel couldn't explain in the time available. Neither piece here is wired into the request path automatically; both are tools you consult explicitly when scheduling work, not a hook that could silently make `Session.get()` block for hours.

```python
from stealthhttp import ActivityClock, FlowStep, check_travel

clock = ActivityClock.for_country("DE")
if not clock.is_plausible_now():
    time.sleep(clock.wait_seconds())          # 3am in Berlin is a quiet hour, not a good time to run a batch

check = ident.check_travel("RU")               # before reusing an identity with a different proxy
if not check.plausible:
    ident = store.get("a-different-account")   # this one was just in the US an hour ago

s.visit_flow([                                  # realistic dwell time per page, not just inter-request jitter
    FlowStep("https://shop.example/", dwell=(5, 15)),
    FlowStep("https://shop.example/product/42", dwell=(10, 40)),
    FlowStep("https://shop.example/cart", dwell=(3, 10)),
])
```

`check_travel` is centroid-distance-based and, measured directly, that alone falsely flags the single most common real proxy-pool pairing: US↔Canada within an hour computed as "1898 km/h needed" because a country's geometric centroid can be thousands of km from its own border. Fixed with an explicit `ADJACENT_COUNTRIES` table (US-CA, US-MX, and two dozen other confirmed-adjacent pairs) that bypasses the distance check for bordering countries, while genuinely impossible jumps (US↔Russia, GB↔Australia within an hour) are still caught at 15,000+ km/h.

### JS environment emulation and sensor data

An HTTP client can't execute JavaScript, but it can generate the same *data* that JS collectors send. `stealthhttp` emulates the observable effects of a browser's JS environment:

```python
from stealthhttp import JsEnvironment, InteractionSequence
from stealthhttp.collectors import AkamaiSensor, PerimeterXCollector

# Coherent navigator/screen/WebGL/audio/canvas properties, derived from the profile
env = JsEnvironment.derive("chrome138", platform="macos", seed=42)
print(env.navigator_dict())   # matches what our HTTP headers claim
print(env.webgl.renderer)     # "ANGLE (Apple, ANGLE Metal Renderer: Apple M2, ...)"

# Natural mouse movement, keyboard timing, scroll behavior
events = InteractionSequence.generate(seed=42, viewport=(env.window.inner_width, env.window.inner_height))

# Antibot sensor payloads for Akamai, PerimeterX, DataDome, Imperva
sensor = AkamaiSensor(abck_cookie=resp.cookies.get("_abck", ""))
payload = sensor.generate(env, events, page_url)
s.post(sensor.post_url(page_url), data=sensor.form_body(payload))
```

What this covers:
- **`JsEnvironment`**: complete navigator/screen/window/WebGL/canvas/audio/font property sets, deterministic per seed, coherent with HTTP headers. Database of real hardware configs (Apple Silicon, NVIDIA/AMD/Intel GPUs, screen resolutions per platform).
- **`InteractionSequence`**: mouse trajectories (Bezier curves, Fitts's law timing, overshoot-and-correct), keystroke timing (right-skewed, bigram-aware), touch gestures (tap/swipe/scroll with realistic pressure), scroll behavior (wheel steps or trackpad momentum).
- **Collectors**: sensor payload generators for **Akamai Bot Manager**, **PerimeterX/HUMAN**, **DataDome**, and **Imperva/Incapsula** — each targeting a documented format version.
- **`HintStore`**: Client Hints negotiation — Chrome sends low-entropy hints always, high-entropy only after Accept-CH, Critical-CH triggers automatic retry. Firefox/Safari correctly send none.
- **`HstsStore`**: HSTS preload enforcement (~80 top domains) + learned policies — no plain HTTP to google.com.
- **Cookie `SameSite`**: enforcement of Strict/Lax/None, `__Secure-`/`__Host-` prefix validation.
- **`solve_sha256()`**: SHA-256/HMAC/double-SHA proof-of-work solver for computational challenges.

### Automatic antibot bypass

`auto_bypass=True` makes the session automatically detect and bypass antibot challenges without a real browser:

```python
s = Session(browser="chrome138", proxy=proxy, auto_bypass=True)
r = s.get("https://akamai-protected-site.com")   # _abck sensor submitted automatically
r = s.get("https://datadome-site.com")            # device fingerprint sent to api-js.datadome.co
r = s.get("https://cloudflare-site.com")          # PoW solved and submitted
print(s.bypass.report())                          # per-vendor success stats
```

What the engine handles transparently:
- **Cloudflare managed PoW**: extracts challenge params, solves SHA-256/double-SHA, submits solution, gets `cf_clearance`
- **Akamai Bot Manager**: generates sensor_data from JsEnvironment + InteractionSequence, POSTs to `/_sec/cp_challenge/verify`, validates `_abck`
- **DataDome**: detects `x-datadome-cid`, submits device fingerprint to `api-js.datadome.co/js/`, gets `datadome` cookie
- **PerimeterX/HUMAN**: generates collector payload, POSTs to `/_px/xhr/api/v2/collector`, gets `_px3` cookie
- **Imperva/Incapsula**: generates reese84 token from device fingerprint + timing, submits, gets `reese84` cookie

What still needs external help (detected but not solved inline):
- Cloudflare Turnstile / hCaptcha interstitial → use `solving/` providers
- DataDome captcha interstitial (`captcha-delivery.com`) → solver token
- PerimeterX captcha widget (`px-captcha`) → solver token
- Kasada → needs a real JS engine

The engine reports what it encountered:
```python
s.bypass.stats()    # [{"vendor": "akamai", "attempted": 3, "succeeded": 2, ...}]
s.bypass.report()   # human-readable summary
```

### Captcha identification and solving orchestration

`stealthhttp` doesn't solve captchas by itself where no HTTP client can — reCAPTCHA/hCaptcha/Turnstile/Arkose need a trained model or a real browser producing a token; Cloudflare's managed challenge, Kasada, Akamai Bot Manager, PerimeterX and DataDome need a real engine, full stop, because there's no token to buy. What it does provide:

```python
from stealthhttp import identify
from stealthhttp.solving import SolverPool, CapsolverProvider, SlideSolver, AudioSolver

challenge = identify(resp.status_code, resp.headers, resp.content, resp.url)
print(challenge.explain())   # vendor, sitekey, and whether a token or a browser is what's needed

pool = SolverPool([SlideSolver(), AudioSolver(my_asr_engine), CapsolverProvider()])
result = pool.solve_challenge(challenge, proxy=resp.proxy, user_agent=s.user_agent)
```

- **Vendor identification + parameter extraction** for reCAPTCHA v2/v3/Enterprise, hCaptcha, Turnstile, Arkose, GeeTest, Cloudflare, DataDome, PerimeterX, Imperva, Akamai, Kasada, AWS WAF, Queue-it — with a stated solvability for each (token vs. browser vs. not solvable).
- **`SolverPool`**: routes by capability, prefers free local solvers over paid ones, demotes a provider once it's measurably failing a given vendor, fails over between providers, tracks spend.
- **Slide puzzles** (`SlideSolver`, no model): locates the notch by image differencing (100% precision on accepted answers when the cut-out contrasts with the background, 76% with 17/50 deferred when it doesn't — measured) and generates a human-plausible drag trajectory (eased velocity, overshoot-and-correct, non-uniform timing — these puzzles score the motion, not just the endpoint).
- **Text-arithmetic captchas** (`MathCaptchaSolver`, no model): fully deterministic — parses and evaluates "what is 7 + 3?"-style questions, numeric or spelled out.
- **Audio challenges** (`AudioSolver`): the accessibility alternative reCAPTCHA/hCaptcha both ship — a legitimate bypass path, not a loophole, and needs no browser. Pluggable ASR engine, like `OcrSolver`; a ready-made offline `vosk_engine()` (measured: 60-67% exact match, 12.3% char error rate on a 40MB model against synthesized speech) and an online `whisper_api_engine()` (needs `OPENAI_API_KEY`; request mechanics tested, real accuracy not measured here for lack of a key). The well-known free/unofficial Google speech endpoint was tried and is measured dead (403) as of this writing.
- **Image grids** (`ImageGridSolver`): zero-shot classification via CLIP, a *general-purpose* pretrained vision-language model — not something trained on captcha imagery. Three prompting strategies were measured before picking one: a textbook contrastive "photo of X / photo without X" prompt scored 46% (CLIP is known to handle negation poorly), a neutral negative scored 75%, plain cosine similarity to a single prompt scored **88%** and is what ships. That number is from a synthetic calibration set, not real reCAPTCHA imagery — see `stealthhttp/solving/vision.py` for why real accuracy isn't claimed against Google's specifically hardened tile set.
- **Provider adapters** for Capsolver / 2captcha / anti-captcha (no keys embedded; bring your own).
- **`BrowserBridge`** (optional, needs `pip install playwright`) for the challenges that need a real engine: opens one page, waits for clearance, hands back an `Identity` carrying the same proxy and User-Agent — because a cookie earned from a different IP or UA isn't honoured. Verified end to end: a real Chromium cookie, harvested via `storage_state`, correctly reached the native transport in a live test. Not required for token-type captchas or for hand-off from an already-solved browser session (any cookie export works — `Identity.from_cookies_txt` doesn't care whether it came from Playwright, plain Chrome, or an antidetect browser).

### Stealth audit and profile comparison (v0.2)

Score your configuration against known detection vectors, compare fingerprint profiles side-by-side:

```python
from stealthhttp import StealthScore, FingerprintDiff, format_diff

scorer = StealthScore()
result = scorer.score("chrome138")
print(result.total, result.grade)       # 66.25 C — TLS/HTTP/TCP/JS/timing/behavioral breakdown

diff = FingerprintDiff()
summary = diff.diff("chrome138", "firefox140")
print(format_diff(summary))             # field-by-field comparison with importance weighting
```

### Automatic WAF-aware profile selection (v0.2)

The engine picks the best profile for a target domain — factoring in WAF vendor, country, and failure history:

```python
from stealthhttp import AutoProfileSelector

selector = AutoProfileSelector()
rec = selector.select("cloudflare.com", waf_vendor="cloudflare")
print(rec.profile_name, rec.score)      # chrome138 0.40
```

### Header canonicalization (v0.2)

Exact browser header order and casing per Chrome/Firefox/Safari — the second most common tell after TLS is sending headers in the wrong order:

```python
from stealthhttp import HeaderCanonicalizer

headers = HeaderCanonicalizer.canonicalize(
    {"accept": "text/html", "user-agent": "...", "accept-encoding": "gzip, br"},
    browser_family="chrome",
)
```

### TLS intelligence and MITM detection (v0.2)

Analyze server certificates for CDN fingerprinting and detect MITM proxies from certificate anomalies:

```python
from stealthhttp import CertAnalyzer, MitmDetector, ServerFingerprint

analysis = CertAnalyzer.analyze_from_headers(response.headers)
fp = ServerFingerprint.from_response_headers(response.headers)
mitm = MitmDetector.check("example.com", cert_cn, response.headers)
```

### Response body intelligence (v0.2)

Detect fingerprinting scripts, classify cookies by antibot vendor, and identify tracking patterns in response bodies:

```python
from stealthhttp import BodyIntelligence, CookieClassifier

analysis = BodyIntelligence.analyze(body, headers)
print(analysis.anti_bot_signals)         # fingerprinting JS, tracking pixels, data collectors
cookie_type = CookieClassifier.classify("_abck", value)
print(cookie_type.vendor)               # "akamai"
```

### Network simulation and traffic shaping (v0.2)

Simulate realistic network conditions — bandwidth, latency, packet loss — to match expected residential/mobile/datacenter timing:

```python
from stealthhttp import NetworkSimulator, NETWORK_PROFILES

sim = NetworkSimulator("4g")             # 20Mbps, 50ms latency, mobile packet loss
delay = sim.simulate_latency()           # jittered realistic delay
```

### DNS leak prevention and CDN detection (v0.2)

Check for DNS leak risks when using proxies, detect CDN providers from response headers and CNAME chains:

```python
from stealthhttp import DnsLeakChecker, CdnDetector

risk = DnsLeakChecker().check_leak_risk(proxy_url="socks5://...")
cdn = CdnDetector().detect_from_headers(response.headers)
```

### Advanced proxy scoring (v0.2)

Composite proxy scoring with latency percentiles, geo-awareness, and benchmarking:

```python
from stealthhttp import ProxyScorer

scorer = ProxyScorer()
scorer.record_request("http://proxy:8080", success=True, latency_ms=200)
score = scorer.score("http://proxy:8080")    # 0-100 composite score
ranked = scorer.rank()                       # all proxies sorted by score
```

### Request dependency graph (v0.2)

Build browser-like resource loading graphs from HTML, simulate load times under different network profiles:

```python
from stealthhttp import build_graph_from_html, estimate_load_time

graph = build_graph_from_html(html_content, base_url)
print(graph.critical_path())             # render-blocking chain
t = estimate_load_time(graph, "cable")   # simulated load time in ms
```

### Response decoder (v0.2)

Full content decoding pipeline — gzip, deflate, brotli, zstd — with automatic charset detection from BOM, meta tags, and Content-Type:

```python
from stealthhttp import decode_response

result = decode_response(body, headers)
print(result.text, result.encoding_used)
```

### Redirect intelligence (v0.2)

Track redirect chains, detect challenge redirects (Cloudflare, DataDome, Imperva patterns), and predict redirect behaviour per domain.

### Error analysis (v0.2)

Classify errors into categories (TLS, proxy, WAF, rate-limit), track patterns per domain, and get actionable fix suggestions.

### Multi-session coordination (v0.2)

Shared rate limiters across sessions, identity rotation policies, coordinated session pools for distributed scraping:

```python
from stealthhttp import SharedRateLimiter, SessionPool, CoordinationHub

limiter = SharedRateLimiter(default_rps=5.0)
pool = SessionPool(max_sessions=10)
hub = CoordinationHub()
```

### Health monitoring and alerting (v0.2)

Real-time health monitoring with latency tracking, success rate, and configurable alert thresholds:

```python
from stealthhttp import HealthMonitor, AlertManager

monitor = HealthMonitor()
monitor.record_request(latency_ms=150, success=True)
print(monitor.overall_status())          # healthy / degraded / unhealthy
```

### Concurrency and throttling middleware (v0.2)

Per-domain concurrent request limiting with backoff, and minimum interval between requests:

```python
from stealthhttp import ConcurrencyLimiter, DomainThrottle

pipeline.add(ConcurrencyLimiter(max_concurrent=10))
pipeline.add(DomainThrottle(default_interval=1.0))
```

### Detection vector catalogue and self-audit

```python
from stealthhttp import audit
from stealthhttp.detection import report

print(report())          # every vector this library is aware of, and its status
a = audit(session=s)     # checks the measurable ones against a live echo service
print(a.report())
```

Listing the vectors this can't address (JS fingerprinting, the TCP/IP stack fingerprint, proof-of-work) next to the ones it does is deliberate — a partial defence advertised as complete just tells you to stop looking.
## Building the Rust core

```bash
pip install maturin cmake
maturin develop --release
python -c "import stealthhttp; print(stealthhttp.NATIVE)"                    # fingerprint core
python -c "import stealthhttp._core as c; print(c.HAS_TRANSPORT)"            # native transport
```

The test suite asserts the native core and the Python fallback produce identical output, so they cannot silently diverge.

## Tests

```bash
pytest -m "not network"   # offline
pytest -m network         # live fingerprint/transport verification
cargo test --lib          # Rust unit tests
```

## Layout

```
src/                             Rust core: PyO3 bindings, fingerprint DB, JA3/JA4, native transport
python/stealthhttp/              Python package
  backends/                      native / wreq / curl_cffi, capability routing
  solving/                       captcha ID, orchestration, local + provider solvers
  bypasses/                      auto antibot bypass (CF, Akamai, DD, PX, Imperva)
  collectors/                    sensor payload generators (Akamai, PX, DataDome, Imperva)
  jsenv.py, jsdb.py              JS environment emulation + hardware databases
  sensors.py                     mouse/keyboard/touch/scroll event generation
  clienthints.py, hsts.py        Accept-CH negotiation, HSTS enforcement
  pow.py                         proof-of-work solver (SHA-256, HMAC, double-SHA)
  _fallback.py                   pure-Python mirror of the Rust fingerprint core
benchmarks/                      performance benchmarks
tests/                           664 unit + integration tests
```

## Legal

For authorised testing, research, and accessing services you have the right to access. Respect `robots.txt`, terms of service, and rate limits. MIT.

