mirror of
https://github.com/germondai/trawl.git
synced 2026-08-17 12:11:23 +02:00
18 KiB
18 KiB
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[1.4.0] - 2026-08-10
Changed
- Cold-start performance milestone: TRAWL's complete first request, including browser launch, is now nearly 4x faster in like-for-like Docker benchmarks. Redis validation and browser warmup now run concurrently, Tier 0 becomes available immediately, and browser capacity is published progressively. Warm-request timings vary with browser state, session caching, and challenge behavior and are not included in this cold-start comparison.
- Bump all application and internal package versions to
1.4.0. - Update Biome to 2.5.7, Memoirist to 1.2.2, Nuxt to 4.5.2, and Nuxt SEO to 5.3.11. TypeScript remains on 5.9.3 for the Nuxt app and Playwright remains on 1.60.0 for Camoufox compatibility.
- Update GitHub Actions to their current stable major releases and make the CI release gate read-only and reproducible.
- Pin the runtime to Bun 1.3.14, Camoufox v152.0.4-beta.28, GeoLite2 City 2026.08.07, and Redis 8.8.1, with SHA-256 verification for downloaded browser/runtime data assets.
- The remaining audit findings are confined to Nuxt/VitePress development and build-time dependency trees; no compatible upstream update is currently available for those transitive packages.
Fixed
- Support explicit non-root Docker users by baking the pinned uBlock Origin addon into both API image variants and using a writable temporary home directory. Document CA volume ownership and read-only container requirements (#60).
- Reduce cold-start latency by warming Redis alongside the browser pool, publishing the first browser immediately, warming the remaining browsers concurrently, and accepting Tier 0 proxy traffic during warmup. Unavailable Redis now disables Tier 2 promptly instead of delaying the first request. Tier 0 also handles informational HTTP responses correctly and escalates authoritative
cf-mitigated: challengeheaders immediately. - Keep browser-tier status, headers, content type, and raw body aligned with the latest main-frame navigation response across redirects, and prevent persistent Cloudflare challenges from being returned as successful rendered pages (#53).
- Translate Prowlarr's serialized
headers.contentTypemetadata at the FlareSolverr/v1compatibility boundary and discardcontentLength, allowing form POST requests to enter the scraper pipeline (#50). - Bound Camoufox memory growth by counting every Tier 3/4 temporary context and rolling-replacing browsers at
BROWSER_RECYCLE_AFTER_CONTEXTS, while keeping existing capacity available during warm-up. Replacement launches are serialized, cleanup is timeout-bounded, and failed launches retain the usable browser (#52).
[1.3.1] - 2026-08-02
Fixed
- Keep pooled browser contexts under
BrowserPoolownership so repeat Tier 2 requests cannot reuse a context closed by a separate cache (#45).
Changed
- Bump all application and internal package versions to
1.3.1.
[1.3.0] - 2026-08-02
Added
- Detect and resolve Akamai Bot Manager behavioral interstitials across scraper tiers and the HTTP/HTTPS proxy (#33).
- Document supported upstream proxy formats, local Compose configuration, and residential proxy pools (#26).
Fixed
- Reject non-object request bodies and missing, non-string, or blank
urlvalues with HTTP 400 before scraper-tier execution (#34). - Recover stalled browser checkouts and bound browser close/launch operations so wedged Firefox processes cannot silently exhaust the pool (#36, #37, #48).
- Report HTTP 503 from
/healthwhenever the browser pool has no live capacity. - Pass authenticated proxy credentials to Firefox separately from the proxy server URL in Tier 3 and Tier 4 (#40).
- Return complete Tier 1 text responses instead of the 4 KiB challenge-detection preview (#46, #47).
- Record the correct architecture-specific Camoufox release metadata in API images.
Changed
- Bump all application and internal package versions to
1.3.0. - Update workspace dependencies to their latest compatible releases. TypeScript remains on 5.9 for the Nuxt app until
vue-tscsupports TypeScript 7.
[1.2.0] - 2026-07-26
Added
- Tier 0 direct forward in the MITM proxy (
apps/api/src/proxy/directForward.ts): the proxy at:8192now forwards requests directly to upstream via raw TCP/TLS instead of spinning up a browser for every request. Pool is reserved for the requests that actually need CF bypass; Netflix/YouTube/banks/etc. flow at near-direct speed. - Smart adaptive streaming (
apps/api/src/proxy/streaming.ts): small JSON/HTML/text responses are buffered so the challenge detector can inspect them; video/audio/binary files (.mp4,.mkv,.m3u8,.zip,.exe,.dmg, etc.) are streamed straight through to the client. Default threshold: 8 MiB. - Per-hostname challenge cache (
apps/api/src/proxy/challengeCache.ts): the proxy remembers which hostnames recently returned Cloudflare challenges and sends repeat visits directly to the tiered solver for 5 minutes. - Persistent browser context cache: solved sessions retain their browser fingerprint, cookies, cache, and storage across proxy requests, with bounded per-proxy reuse and cleanup.
- General HTTP/HTTPS proxy support: CONNECT tunneling, plain HTTP absolute-form requests, WebSocket upgrades, request bodies, redirects, authentication headers, cookies, and HTTP Range/206 responses are forwarded with their required semantics.
proxySanitizeHeaders()(packages/tiers/src/utils/sanitize.ts): permissive header sanitizer for the transparent MITM proxy — passesAuthorization,Cookie,Range,User-Agent,Referer, and custom API tokens through, strips only RFC 7230 hop-by-hop headers.- Raw bytes in
@trawl/tiers:ScrapeResultand tier results now exposebody?: Uint8Array,responseHeaders?: Record<string,string>, andcontentType?: stringalongside the existinghtmlfield. The proxy uses these fields to preserve binary content without HTML normalization. - Tier 1 method handling fix:
runTier1()now forwards the request body forPUT,PATCH,DELETE,QUERY(was onlyPOST). - Graceful proxy shutdown (
shutdownMitmProxy()): the API captures the proxy handle on startup andlifecycle.tscallsshutdownMitmProxy()onSIGTERM/SIGINTbefore the browser pool shutdown, so in-flight connections drain. - MITM proxy port + CA volume in docker-compose:
8192:8192port mapping,MITM_PROXY_*env vars, and a persistenttrawl_proxy_cavolume so the CA survives container restarts. - Proxy tests: header sanitization, response policy, adaptive streaming, challenge caching, direct HTTP forwarding, Range/206 handling, chunked responses, compressed challenge detection, and explicit media streaming.
- Proxy documentation: architecture, configuration, client setup, and CA installation guides for operating systems, browsers, Java/JDownloader, containers, and application-specific trust stores.
Changed
packages/types/src/index.ts—ScrapeResultextended additively with optionalbody,responseHeaders, andcontentType; native/scrapeconsumers should tolerate these additional fields.apps/api/src/proxy/server.ts—fetchRaw/reissuereplaced byproxyRequest()(Tier 0 withscrape()fallback) for both CONNECT-based HTTPS and plain HTTP traffic.lifecycle.ts—registerLifecycleHandlers()accepts an optional{ onShutdown }callback.apps/api/src/index.ts— captures the proxy handle on startup and wires it intoregisterLifecycleHandlers.- All application and internal package versions bumped to
1.2.0.
[1.1.0] - 2026-07-22
Added
- Browser-backed MITM forward-proxy mode (
MITM_PROXY_ENABLED, off by default): HTTP(S) forward proxy that re-issues requests through the browser pool so clients like Prowlarr, Jackett, JDownloader, and changedetection.io can hit fingerprint-bound Cloudflare sites that the/v1cookie handoff cannot. The generated root CA is persisted, per-host certificates are minted in memory, and the root is downloadable atGET /proxy-ca.crt. New env:MITM_PROXY_{ENABLED,PORT,HOST,CA_DIR,MAX_TIER,DEBUG}.
Changed
fetchRawrotatesproxyPoolon Cloudflare challenge (samemarkBad → next()pattern as Tier 3) instead of retrying on the same IP.- Main MITM proxy listener supports a configurable bind address through
MITM_PROXY_HOST. ci.ymlruns on PRs targetingdevin addition tomain.publish.ymlinspects the actually-pushed tag fromdocker/metadata-actioninstead of re-deriving fromgithub.sha(which previously mismatched the 7-char short SHA).node-forge ^1.3.1runtime dep for CA + per-host leaf cert generation.
[1.0.1] - 2026-07-18
Changed
packages/browser/src/pool.ts— renamed Firefox prefs key fromprefs(silently ignored by camoufox-js@0.11.1) tofirefox_user_prefs(which camoufox-js maps to Playwright'sfirefoxUserPrefs). The prefs are now actually applied.packages/browser/src/pool.ts— added the safe-only subset of Firefox prefs: telemetry off (datareporting.*,toolkit.telemetry.*,app.crashreporter,breakpad.*), dead UI features off (extensions.screenshots.*,browser.sessionstore.max_tabs_undo), dead network services off (browser.safebrowsing.*,extensions.update.*,browser.fixup.alternate.*,app.normandy.*,app.shield.*,network.connectivity-service.*,network.captive-portal-service.*,network.prefetch-next,beacon.enabled),security.OCSP.enabled: 0, and tightened network timeouts (tls-handshake-timeout: 30,connection-timeout: 60,response.timeout: 120). None of these touch the JS/CSS fingerprint surface.apps/api/Dockerfile— stage-3 prune of apt cache +/usr/share/{locale,doc,man}(image-size win, runtime-neutral).apps/api/Dockerfile— added 9 Bun runtime ENV flags (BUN_DISABLE_CJS=1,BUN_DEBUG=0,BUN_DISABLE_SOURCEMAPS=1,BUN_HTTP_KEEPALIVE=0,BUN_AGENT_DISABLE=1,BUN_INSPECT=0,BUN_LOCKFILE_MIGRATION=false,MIMALLOC_PURGE_DELAY=0,NODE_NO_WARNINGS=1). All verified runtime-neutral in smoke tests.packages/browser/package.json— movedpatchright+playwright-corefromdependenciestodevDependencies(build hygiene; camoufox-js bundles both transitively at runtime).- All packages bumped to
1.0.1.
Added
scripts/bench-targets.sh,scripts/bench-success-rate.sh,scripts/bench-compare.sh— observability harnesses for measuring CF challenge latency + bypass success rate.
[1.0.0] - 2026-07-10
Changed
- Shared types (
BrowserHandle,BrowserFingerprint,SupportedMethod) centralized instead of being duplicated per-package packages/tierssplit intotiers/andutils/, deduplicating cookie and network-failure helpers;apps/api's entrypoint split intoconfig,deps, androutes, adding a proper root status route- Evaluated switching the cache backend to Dragonfly and reverted:
benchmarking showed Dragonfly only wins throughput when load is spread across multiple
connections, but
packages/browser'sSessionCacheholds a single sharedRedisClientconnection for the process lifetime — so at TRAWL's actual access pattern, plain Redis is faster at every scale tested, regardless ofBROWSER_POOL_SIZE. Docker Compose configs (docker-compose.yml,.full.yml,.prod.yml) now pinredis:8.8-alpine; theredisservice name andREDIS_URLenv var are unchanged from before the Dragonfly experiment.
Added
- Landing page shows a live GitHub star count
[0.7.0] - 2026-07-08
Added
- Audio STT fallback for the hCaptcha solver
Fixed
- JS-only challenge pages that only look like plain HTML now correctly escalate from Tier 1 to the browser tiers (#22, #23)
[0.6.0] - 2026-07-08
Added
BROWSER_RECYCLE_AFTER_CONTEXTSenv var (default8, set0to disable) bounds long-running browser process growth by recycling the pooled Camoufox/Firefox instance after a configurable number of Tier 3/Tier 4 temporary context creationsBROWSER_CONTENT_PROCESSESenv var (default2) caps Firefox content processes per pooled browser via thedom.ipc.processCountFirefox pref. Firefox's default of 8 lets thread count climb when Tier 3/Tier 4 churn disposable contexts (see #13). The cap bounds the leak at the source without needing to restart the browser.- Browser fingerprints now randomize OS/screen/window per instance and match the HTTP
User-Agentto the emulated platform
Changed
BROWSER_RECYCLE_AFTER_CONTEXTSno longer recycles preemptively after every N temporary contexts. The pool now recycles only when Tier 3 or Tier 4 returns ablocked/needs-jsoutcome, preserving cookies,cf_clearance, and warm fingerprint state across successful solves. This eliminates the HTTP-429 storm observed in single-browser setups where the previous "recycle every N uses" logic left the only browserrestarting=truefor ~13s during every recycle window (#17, thanks @CoolDotty)- Tier detection now recognizes more block/error page variants; Tier 4 gains full captcha parity, with proxy/timing info surfaced in responses (#19, thanks @edasque)
Fixed
- Missing
curlin the API runtime image broke healthchecks (#20, #21) - Missing GeoLite2 mmdb caused a GeoIP startup crash on boot; now baked into the image (#20, #21)
[0.5.0] - 2026-07-06
Added
- Native
method+bodysupport across all four scraper tiers — theFlareSolverrRequest.cmd=request.postbody is now actually delivered upstream instead of being silently dropped (thanks @whoshoe for the original POST support) ScrapeRequest.methodaccepts the full standard verb set:GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS,TRACE,QUERY(RFC 9341).CONNECTis intentionally excluded (tunneling verb, inappropriate for a proxy)- POST /
*request bodies are forwarded uncapped — operators who want a byte ceiling should impose it at their ingress / fronting proxy - Body-bearing requests require a
Content-Typeheader; the tier functions no longer auto-injectapplication/x-www-form-urlencoded, which previously mislabelled JSON / XML bodies ScrapeRequestfield renamed frompostData→bodyfor REST-idiomatic naming. (FlareSolverrRequest.postDatais unchanged because it's the upstream wire contract.)- Native Imperva/Incapsula WAF challenge detection and solving in Tier 3 and Tier 4
- Proxy rotator reworked into a sticky, failure-aware pool with per-request override support
PORT_APIenv var renamed toPORTand made properly configurable (#9, #10)
Security
- Reserved-name header denylist prevents callers from spoofing
cf_clearancecookies, overriding the per-tierUser-Agent, or rewriting routing signals (X-Forwarded-For,Host) during a POST bypass flow
Fixed
/v1now accepts Prowlarr's CardigannFlareSolverrProxyobject shape ({url, username, password}) for the per-requestproxyfield, instead of crashing withproxy.server: expected string, got objectwhen Prowlarr sends it through (#12, #15). The boundary normalises both the object form and a plain URL string into a single URL string before the orchestrator forwards it to Playwright/Camoufox. Credentials are URL-encoded so embedded@/:characters survive the round-trip.
Limitations
- The Playwright
page.route(url, …)interceptor only handles the first top-frame GET to that exact URL. Server redirects to a different URL, XHR sub-resources, and chainedPOST→POSTform flows do not have thepostDataoverride applied - No idempotency-key support; transient network failures and pool churn can re-fire a POST (separate ticket)
Tests
packages/tiers/tests/sanitize.test.ts— header sanitiser, method allowlist, postData size cap, Content-Type enforcementpackages/tiers/tests/runTier1Post.test.ts— tier1 GET/POST round-trip and User-Agent non-override- Run via
bun --cwd packages/tiers test
[0.4.0] - 2026-07-01
Added
:baselineDocker image variant for pre-AVX2 CPUs and older kernels, published to its own GHCR tag — confirmed working on a Synology DS920+ (DSM 7.3.2, kernel 4.4.302), see #1
Fixed
- Docker healthchecks failing, root-caused to
wgetvs. the runtime image; switched to acurl-based healthcheck with a proper timeout and start period (#3, #4) - Startup crash loop (
EISDIR, missingmemoirist/camoufox-js) fixed by switchingbun installto--linker=hoisted(#1, #6) /healthnow correctly returns 503 while the browser pool is still initializing
[0.3.0] - 2026-06-30
Added
- Configurable browser pool concurrency limiter to guard against OOM under burst load
BROWSER_ACQUIRE_TIMEOUT_MSenv var, default 15s
Changed
- A saturated browser pool now returns HTTP 429 instead of a raw 500
- Default
BROWSER_POOL_SIZEraised to 3
[0.2.0] - 2026-06-26
Added
- Custom
headersfield onScrapeRequest, forwarded through Tier 1-4 via URL-scoped route interception and exposed on/v1and/scrapewith CORS support cmdis now optional on/v1, defaulting torequest.get
Changed
- Multi-arch Docker publish now runs on native arm64 runners with a two-phase per-digest build and manifest merge
[0.1.0] - 2026-06-26
Added
- Initial release with 4-tier execution engine
- Native captcha solving for Cloudflare Turnstile, reCAPTCHA v2 (audio STT), hCaptcha (audio bypass), and GeeTest v3 slider
- Persistent browser pool with real Camoufox Firefox
- Session caching via Redis
- FlareSolverr v2-compatible
/v1endpoint - WebSocket live scrape streaming at
/scrape/live - Self-healing browser pool with automatic restart on crash
- Sticky domain routing to maximize session cache hits
- Nuxt 4 landing page with live stats
- VitePress documentation site
- Docker Compose deployment with amd64/arm64 platform targeting