Skip to content

v0.2.2 — byte-WAF solving, deep V8 stacks, Performance Timeline

Choose a tag to compare

@yinnho yinnho released this 27 Aug 17:01
· 78 commits to main since this release

v0.2.2 — byte-WAF solving, deep V8 stacks, Performance Timeline

27 commits since v0.2.1. The theme: pages that used to come back empty now come back rendered — two whole classes of "site X returns a blank shell" are gone, and the JS runtime's performance surface no longer lies to analytics.

Auto-solve byte-WAF JS challenges (94c2ced + e274d9a)

juejin.cn (ByteDance properties generally) gates pages behind a <body onload="readygo()"> SHA-256 proof-of-work: no solver, no content — the page just never finishes "verifying". The engine now detects the stub (a readygo function plus a near-empty body), forwards the onload challenge, evaluates the PoW statements, and drains the challenge reload. Tier 1 defers byte-WAF stubs so the challenge script can register; Tier 2 drains the reload after the solve.

Result on juejin.cn article pages: challenge passes cleanly, ~16s cold, full rendered markdown out. Anonymous feeds still need login cookies (the recommend API returns data:null to logged-out visitors — same as raw curl), which fetch's cookies field handles.

V8 stack ceiling raised for minified bundles (e6e5d24)

A 1.09 MB Tailwind-inline docs page rendered to a RangeError: Maximum call stack size exceeded mid-bundle. Root cause is two-layered: V8 counts JS frames against an internal ~984KB limit that is independent of the hosting thread's native stack, and the default 2 MB thread dies a few thousand frames in where desktop Chrome runs the same bundle fine.

The fix sets both: every V8 runtime and session thread now spawns with an explicit deep stack (32 MB default, AGINXBROWSER_JS_STACK_MB to tune, 1–1024), and --stack-size is applied before the first isolate (with a 2 MB margin). A 20,000-frame recursion regression test pins it — past what any default-threaded embedder survives, comfortably inside Chrome's own ceiling.

Performance Timeline is real (02a779c)

mark()/measure() were no-ops and getEntriesByType() always returned [], so analytics bundles (Sentry, web-vitals wrappers) reading user-timing landed in fallback branches. Now:

  • mark/measure record real entries (500-cap buffers), Chrome-shaped TypeError/SyntaxError on bad args, clearMarks/clearMeasures honor names
  • getEntriesByType('navigation') derives a Level-2 entry from performance.timing — and __diting_init now stamps a plausible DCL/load offset chain instead of all-equal epochs (every derived duration was 0, which is its own automation tell)
  • paint entries (first-paint / first-contentful-paint) derive from the DCL offset once per navigation
  • entries carry prototype accessors + [native code] toString, same lie-detector posture as the FontFace shim
  • 'resource' stays honestly empty: no per-request network timings exist here, and a fabricated waterfall would be a lying telemetry surface — worse than absent

Live on browser.aginx.net — verified on posthog.com: dcl=374ms, load=1057ms, paint ×2, page renders unchanged.

Fingerprint and locale consistency (35ec115 + 4e68950)

obscura#734 lineage, two rounds: Intl's default locale now agrees with navigator.language and the Accept-Language header (three-way consistency a detector can cross-check), and the Intl wrapper functions themselves are fingerprint-proof — property descriptors and toString output match stock V8, closing the follow-up.

Smaller fixes worth naming

  • margin: auto actually centers (c5799df) — in-flow blocks and abspos elements pass real auto-margins into taffy instead of computed zeros (taffy#923 lineage)
  • Form controls behave (767fe52 + 88b8496) — fieldset-disabled controls never activate, checkbox click clears indeterminate, and indeterminate is a real IDL property instead of an expando (obscura#721 same-hole family)
  • Same-origin POSTs send Origin (f4904fa) and fetch()/XHR carry Fetch-Metadata + client-hint headers (5a87cca) — servers that gate on header presence stop 403-ing
  • Idle sessions keep pumping (67bccc8) — the event loop runs 200ms slices between tool calls (timers, promise chains, fetch callbacks keep firing while a session idles); now pinned by a regression test instead of only by the WorkOS Radar incident
  • Test modules split (72f7b30) — oversized test files broken into their own modules; no new abstraction

Packaging

  • Docker images: ghcr.io/yinnho/aginxbrowser built by CI on every release (e0e6e63), mirrored to Docker Hub yinnho/aginxbrowser when secrets are present (df9bcc0 + follow-ups)
  • crates.io metadata (700fa53) — readme/repository/keywords wired for the future publish
  • llms-install.md + logo asset for MCP directories (e731c76)

Full API reference: docs/API.md · Hosted instance: browser.aginx.net · claude mcp add aginxbrowser --transport http https://browser.aginx.net/mcp