v0.2.2 — byte-WAF solving, deep V8 stacks, Performance Timeline
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/measurerecord real entries (500-cap buffers), Chrome-shapedTypeError/SyntaxErroron bad args,clearMarks/clearMeasureshonor namesgetEntriesByType('navigation')derives a Level-2 entry fromperformance.timing— and__diting_initnow 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: autoactually 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, andindeterminateis a real IDL property instead of an expando (obscura#721 same-hole family) - Same-origin POSTs send
Origin(f4904fa) andfetch()/XHR carryFetch-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/aginxbrowserbuilt by CI on every release (e0e6e63), mirrored to Docker Hubyinnho/aginxbrowserwhen 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