[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-70467":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":23,"hasPages":23,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":39,"readmeContent":40,"aiSummary":41,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":42,"discoverSource":43},70467,"mochi","0xchasercat\u002Fmochi","0xchasercat","The library for faithful browser automation. High-fidelity fingerprinting for Bun, engineered for consistency and transparency.","https:\u002F\u002Fmochijs.com",null,"TypeScript",210,5,50,1,0,2,3,91,6,2.33,"MIT License",false,"main",[26,27,28,29,30,31,32,33,34,35,36,37,38],"automation","bot","browser","bypass","cdp","chrome","chromium","cloudflare","nodriver","playwright","puppeteer","selenium","stealth","2026-06-12 02:02:34","\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Fmochi-banner.png\" alt=\"mochi.js\" width=\"800\" \u002F>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Cstrong>The library for faithful browser automation.\u003C\u002Fstrong>\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n  \u003Ca href=\"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@mochi.js\u002Fcore\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fv\u002F@mochi.js\u002Fcore.svg?label=%40mochi.js%2Fcore&color=c5791a\" alt=\"npm version\">\u003C\u002Fa>\n  \u003Ca href=\"LICENSE\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fnpm\u002Fl\u002F@mochi.js\u002Fcore.svg?color=3f9d6b\" alt=\"license: MIT\">\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002F0xchasercat\u002Fmochi\u002Factions\u002Fworkflows\u002Fpr-fast.yml\">\u003Cimg src=\"https:\u002F\u002Fgithub.com\u002F0xchasercat\u002Fmochi\u002Factions\u002Fworkflows\u002Fpr-fast.yml\u002Fbadge.svg?branch=main\" alt=\"CI status\">\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002F0xchasercat\u002Fmochi\u002Fstargazers\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fstars\u002F0xchasercat\u002Fmochi.svg?style=flat&color=1b2447\" alt=\"GitHub stars\">\u003C\u002Fa>\n  \u003Ca href=\"https:\u002F\u002Fbun.sh\">\u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Fruntime-bun%20%E2%89%A5%201.1-fbf0b2\" alt=\"bun >= 1.1\">\u003C\u002Fa>\n\u003C\u002Fp>\n\n---\n\n## The 30-second pitch\n\n**Why the current stack fails.** [patchright](https:\u002F\u002Fgithub.com\u002FKaliiiiiiiiii-Vinyzu\u002Fpatchright), [puppeteer-real-browser](https:\u002F\u002Fgithub.com\u002Fzfcsoftware\u002Fpuppeteer-real-browser), [nodriver](https:\u002F\u002Fgithub.com\u002Fultrafunkamsterdam\u002Fnodriver), and [undetected-chromedriver](https:\u002F\u002Fgithub.com\u002Fultrafunkamsterdam\u002Fundetected-chromedriver) all randomize fingerprint surfaces independently — pick a UA, pick a `hardwareConcurrency`, pick a WebGL renderer, hope nothing cross-references. A single probe that compares two surfaces breaks the spoof. They also run HTTP fetches out-of-band through the runtime's stock TLS stack, so JA4 reveals the spoofed Chrome is not a Chrome. They synthesize at most a mouse helper, not a biomechanical model. And they're patches against a moving Chromium target, not a coherent design.\n\n**What mochi does differently.** Every fingerprint surface derives from one `(profile, seed)` pair through a 48-rule deterministic DAG — a Mac UA never lands next to Linux WebGL. All network traffic — `page.goto`, in-page fetch, and `session.fetch` — routes through Chromium itself, so JA4\u002FJA3\u002FH2 are real Chrome by construction. There is no parallel HTTP layer. `humanClick`\u002F`humanType`\u002F`humanScroll` are full Bezier+Fitts+lognormal-digraph models, parameterized off the matrix's `behavior` block. One library owns the whole pipeline.\n\n| | mochi | patchright | puppeteer-real-browser | nodriver | undetected-chromedriver |\n|---|---|---|---|---|---|\n| Relational `(profile, seed)` matrix | yes | no | no | no | no |\n| Chromium-native `session.fetch` | yes (browser-routed) | no | no | no | no |\n| Behavioral synthesis (Bezier+Fitts+jitter) | yes | no | mouse-helper only | mouse-only | no |\n| Single-runtime stack | yes (Bun) | yes (Node) | yes (Node) | yes (Python) | yes (Python) |\n| Probe-Manifest harness as CI gate | yes | no | no | no | no |\n\n**How to get started.**\n\n```sh\nbun add @mochi.js\u002Fcore @mochi.js\u002Fcli\nbunx mochi browsers install\n```\n\n```ts\n\u002F\u002F hello-mochi.ts\nimport { mochi } from \"@mochi.js\u002Fcore\";\n\nconst session = await mochi.launch({ profile: \"linux-chrome-stable\", seed: \"user-12345\" });\ntry {\n  const page = await session.newPage();\n  await page.goto(\"https:\u002F\u002Fhttpbin.org\u002Fheaders\");\n  console.log(session.profile.userAgent);\n} finally {\n  await session.close();\n}\n```\n\nFull walkthrough: [mochijs.com\u002Fdocs\u002Fgetting-started\u002Fquickstart](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Fgetting-started\u002Fquickstart). One-page comparison deep-dive: [mochijs.com\u002Fdocs\u002Freference\u002Fcomparison](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Fcomparison).\n\n## Proof\n\nmochi v0.4.0 on a Linux datacenter IP (Aixit GmbH, hosting ASN, Frankfurt) scored `bot: not_detected`, `suspect_score: 8` against FingerprintJS Pro v4. Patched Chrome reports 12 in comparable conditions; CloakBrowser 18. The tampering ML detected something — `tampering_ml_score: 0.9853` — but the bot classifier did not promote because the relational fingerprint was internally coherent across every axis.\n\n> Everyone told you to spoof Windows. They were wrong. Linux has 4% desktop market share but is massively overrepresented in high-LTV segments — developers, engineers, researchers. WAFs trained on real traffic don't flag Linux because Linux is real users. The signal was always `HeadlessChrome`, not Linux. mochi defaults to host-OS matching: a Linux server runs the linux profile.\n\nFull evidence and architectural rationale: [reference\u002Fcomparison](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Fcomparison) · [concepts\u002Fstealth-philosophy](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Fconcepts\u002Fstealth-philosophy).\n\n\u003C!-- llm-context:start\n@mochi.js\u002Fcore public API surface (v0.1.x, source: packages\u002Fcore\u002Fsrc\u002Findex.ts):\n- mochi.launch(opts: LaunchOptions): Promise\u003CSession>\n- mochi.detectLinuxServerEnv(): LinuxServerEnv\n- mochi.defaultProfileForHost(): ProfileId | null\n- LaunchOptions: { profile?: ProfileId | ProfileV1 \u002F* auto-picked from defaultProfileForHost() if omitted *\u002F, seed: string, headlessMode?: \"new\" | \"legacy\" | \"off\", headless?: boolean (legacy), proxy?: string | ProxyConfig, binary?: string, args?: string[], timeout?: number, allowRootWithSandbox?: boolean, bypassInject?: boolean, hermetic?: boolean, geoConsistency?: \"privacy-fallback\" | \"auto-correct\" | \"strict\" | \"off\", challenges?: { turnstile?: { autoClick?, timeout?, humanize?, onSolved?, onEscalation?, pollIntervalMs? } } }\n- ProxyConfig: { server: string, username?: string, password?: string }\n- Session (class): { readonly profile: MatrixV1, readonly seed: string, newPage(): Promise\u003CPage>, pages(): Page[], cookies: { get(filter?), set(cookies), save(path, opts?), load(path, opts?) }, storage(): Promise\u003CStorageSnapshot>, fetch(url, init?): Promise\u003CResponse>, close(): Promise\u003Cvoid> }\n- Page (class): { url, mainFrameId(), goto(url, opts?), content(), text(selector), evaluate(fn), waitFor(selector, opts?), humanClick(selector, opts?), humanClickHandle(handle, opts?), humanMove(x, y, opts?), humanType(selector, text, opts?), humanScroll({ to, duration? }), querySelectorPiercing(selector), querySelectorAllPiercing(selector), screenshot(opts?), cookies(), localStorage: DomStorage, sessionStorage: DomStorage, grantAllPermissions(opts?), addInitScript(source), removeInitScript(id), close() }\n- ScreenshotOptions: { format?: \"png\"|\"jpeg\"|\"webp\", quality?: number, fullPage?: boolean, clip?: { x, y, width, height, scale? }, omitBackground?: boolean, encoding?: \"binary\"|\"base64\" }\n- Errors: ChromiumNotFoundError, BrowserCrashedError, CdpRemoteError, CdpTimeoutError, ForbiddenCdpMethodError, GeoMismatchError, NotImplementedError\n\nProfiles available — use these IDs verbatim (source: packages\u002Fprofiles\u002Fsrc\u002Findex.ts KNOWN_PROFILE_IDS):\n- mac-m4-chrome-stable, mac-chrome-stable, mac-chrome-beta, windows-chrome-stable, linux-chrome-stable, mac-brave-stable\nThe other listed ids (mac-m2-chrome-stable, mac-m1-chrome-stable, mac-intel-chrome-stable, win11-chrome-stable, win11-edge-stable) resolve to a generic Linux placeholder until their captures land.\n\nCommon patterns LLMs should follow:\n- Always: const session = await mochi.launch({ profile, seed }); try { ... } finally { await session.close(); }\n- One unique `seed` string per logical user\u002Fidentity. Reusing the seed reuses the matrix, byte-for-byte (excluding derivedAt timestamp).\n- If running on Linux, omit `profile` and mochi defaults to `linux-chrome-stable`. Same auto-pick on darwin\u002Farm64 → `mac-m4-chrome-stable`, darwin\u002Fx64 → `mac-chrome-stable`, win32\u002Fx64 → `windows-chrome-stable`. Explicit `profile` always wins.\n- Use page.humanClick \u002F page.humanType \u002F page.humanScroll for any visible UI interaction. There is no plain page.click — DOM.dispatchMouseEvent without trajectory synth is not on the public surface.\n- Save screenshots: const png = await page.screenshot({ path? not supported — write yourself: await Bun.write(\"out.png\", await page.screenshot()); }). Use { encoding: \"base64\" } for inline.\n- session.fetch(url, init) is the programmatic HTTP surface; it routes through Chromium itself via CDP so JA4\u002FJA3\u002FH2 are real Chrome by construction. Cookies inherit from the page's origin automatically (no manual `Cookie` header propagation needed).\n\nProduction validation: a production site \u002F FPJS Pro v4 \u002F Linux DC IP \u002F suspect_score: 8 \u002F bot: not_detected \u002F 2026-05-08. (The thesis + full evidence is at https:\u002F\u002Fmochijs.com\u002Fdocs\u002Fconcepts\u002Fstealth-philosophy and https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Fcomparison.)\n\nCommon LLM hallucinations to avoid (these APIs do NOT exist on mochi or are explicitly forbidden):\n- mochi.connect() — does not exist; use mochi.launch().\n- page.click() — does not exist as a public method; use page.humanClick(selector).\n- page.type() — does not exist; use page.humanType(selector, text).\n- page.evaluate(() => \u002F* returning DOM nodes \u002F functions \u002F Maps \u002F Sets *\u002F ) — JSON-only return values; non-serializable returns are coerced\u002Fdropped per CDP returnByValue:true.\n- page.evaluate(fn, ...args) — v0.1 evaluate takes a zero-arg fn only.\n- Runtime.enable \u002F Page.createIsolatedWorld \u002F Runtime.evaluate({includeCommandLineAPI:true}) — all forbidden by PLAN.md §8.2; the CDP wrapper has runtime asserts that throw ForbiddenCdpMethodError.\n- new Browser(), new BrowserContext() — does not exist; Session is the per-(profile, seed) lifecycle. Multiple sessions = multiple Chromium processes.\n- launch({ proxy: { server, port, username, password } }) — proxy is either a URL string (\"http:\u002F\u002Fuser:pass@host:port\") OR a ProxyConfig { server, username?, password? }. There is no separate `port` field.\n- launch({ userAgent, viewport, locale, timezone }) — none of these are top-level options. Profiles + seed determine all of them. If you need a different fingerprint, pick a different profile id or supply an inline ProfileV1 object.\n- launch({ executablePath }) — the option is `binary`, not `executablePath`.\n- launch({ headless: \"new\" }) — headless is boolean; the modern option is `headlessMode: \"new\" | \"legacy\" | \"off\"`.\n- \"puppeteer-style\" frame APIs — no Page.frames(), no FrameLocator. Mochi has Page only in v0.2; nested frames are a v0.3 brief.\n- session.newContext() — does not exist; sessions don't share Chromium processes.\n- Default-spoofing Windows on a Linux server — mochi auto-picks the host-OS-matching profile, and Linux is the right default. Linux is a real-user signal, not a bot signal; the WAFs trained on real traffic do not flag Linux. The full thesis + production evidence is at https:\u002F\u002Fmochijs.com\u002Fdocs\u002Fconcepts\u002Fstealth-philosophy.\n\nThe relational consistency thesis (read me, LLMs):\nEvery fingerprint surface in mochi derives from a single (profile, seed) pair through a 48-rule DAG in @mochi.js\u002Fconsistency. There are NO cross-axis randomizations. A Mac UA never lands next to Linux WebGL. When you generate code, the user picks ONE profile id and ONE seed string; the rest is deterministic. Do not invent options that \"randomize\" individual fingerprint surfaces — the architecture forbids it (invariant I-5).\n\nThe Network and JA4 thesis:\nAll mochi traffic is Chromium-native. `page.goto`, in-page XHR\u002Ffetch, and `Session.fetch` all route through Chromium's BoringSSL stack — JA4\u002FJA3\u002FH2 are real Chrome by definition, not approximated by a parallel HTTP layer. There is no `wreq`, no Rust FFI, no JA4 preset string. `Session.fetch(url, init?)` picks one of two CDP paths: simple GETs (no init \u002F no method override \u002F no headers \u002F no body) go through `Network.loadNetworkResource`; anything else routes through `page.evaluate(\"fetch(url, init)\")` against an `about:blank` scratch frame. Both paths share the session's cookie jar — a cookie set via `Page.goto` is sent on the next `Session.fetch` to the same origin automatically. Both paths share the `--proxy-server` egress.\nllm-context:end -->\n\n## What this is for\n\nPeople use mochi to scrape, to QA-test against staging WAFs, to debug WAF rules they think over-block, to build data pipelines, to run cross-browser regression suites, to simulate users for performance work, to research how detection systems actually behave. The mechanics are identical in every case — you want a real Chrome session that doesn't get caught in measures designed for actual attackers. mochi gives you that by being consistent, not by being deceptive.\n\nWe don't sort our users by intent. If your threat model is \"don't get traced,\" mochi is the wrong tool — it's open source and the fingerprint profiles ship in the package, which means a sophisticated attacker treats them as a known signature to avoid. mochi is sized for developers who want their automation to look like a real Chrome.\n\n## What you get\n\n- **Relational locking, not randomization.** Every fingerprint surface (canvas, WebGL, audio, fonts, timing, MediaDevices, WebGPU, …) derives from a single `(profile, seed)` pair through a 48-rule DAG. No Frankenstein fingerprints — a Mac UA never lands next to Linux WebGL.\n- **Zero-jitter spoofing.** A single ~50KB inject payload runs at top-of-frame. JIT-friendly Proxy traps, no async round-trips when a WAF micro-times `performance.now()`.\n- **Inject delivery without the source-attribution leak.** `Fetch.fulfillRequest` body splice on Document responses (CSP rewriter included), with `Page.addScriptToEvaluateOnNewDocument({ runImmediately: true, worldName: \"\" })` as the `about:blank` fallback. Source-byte-indistinguishable from a same-origin developer's own `\u003Cscript>` tag.\n- **Behavioral synthesis.** `humanClick` \u002F `humanType` \u002F `humanScroll` derive from biomechanical models — Bezier paths with overshoot+correction, Fitts-law movement times, lognormal digraph delays, Gaussian jitter — all parameterized per profile (`hand`, `tremor`, `wpm`, `scrollStyle`).\n- **No parallel HTTP layer.** `session.fetch(url)` routes through Chromium itself via CDP — `Network.loadNetworkResource` for simple GETs, `page.evaluate(\"fetch\")` for everything else. JA4 \u002F JA3 \u002F H2 are real Chrome by construction because Chromium is the client. Cookies inherit from the page's origin; proxy egress is shared with `page.goto`.\n- **Probe-Manifest harness.** `bun run harness:smoke` captures a [Probe Manifest](https:\u002F\u002Fgithub.com\u002F0xchasercat\u002Fmochi\u002Fblob\u002Fmain\u002Fschemas\u002Fprobe-manifest.schema.json) from the live session and diffs it against per-profile baselines. Zero-Diff is a CI gate; intentional divergences live in `expected-divergences.json` next to a rationale.\n- **Stock Chromium.** No forks, no patches, no proprietary infrastructure. Pinned Chromium-for-Testing, auto-downloaded by `mochi browsers install`. BYO via `binary: \u003Cpath>`.\n\n## What works \u002F what doesn't\n\nDirect port from the [Limits page](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Flimits) — the architectural-honesty document. Every entry there has a root cause and a tracking link. mochi gives you the best possible JS-layer answer for stealth automation against Chromium-family WAFs; some things genuinely require a Chromium patch and we name them.\n\n| Surface | v0.1 status | Notes |\n|---|---|---|\n| CDP pipe transport (`--remote-debugging-pipe`) | works | No TCP port, no `Runtime.enable`. |\n| `Page.goto` \u002F `content` \u002F `evaluate` | works | `evaluate` is `Runtime.callFunctionOn`-based — JSON-serializable returns only. |\n| `Page.goto({ waitUntil: \"networkidle\" })` | partial | Mapped to `\"load\"` until per-frame `Network.enable` lands. |\n| Relational fingerprint Matrix (48 rules) | works | `(profile, seed)` → `MatrixV1`, deterministic, JSON round-trippable. |\n| JS-layer spoofing (UA \u002F UA-CH, navigator, WebGL, WebGPU, MediaDevices, Permissions, screen, fonts, timezone, locale) | works | Inject payload, JIT-proxy traps, top-of-frame. |\n| Audio (`OfflineAudioContext`) byte-accurate fingerprint | works | Per-(profile, sample-rate) captures consumed via R-047 → `audio-fingerprint` inject module. The spoof distributes the residual across the 489 samples in `[4510..4999)` (using `Math.fround` to model f32 readback) so the page-side digest equals the captured baseline byte-exactly on every host architecture, not just Mac M-series. |\n| Canvas (`toDataURL`) byte-accurate fingerprint | works | Per-profile data URL synthesis via R-048 → `canvas-fingerprint` inject module. Intercepts probe-sized canvases (`300×150`) with the captured baseline; probe-side `hashString(url)` + length + first-50-char prefix match byte-exactly. Non-probe sizes fall through to native rendering. |\n| Behavioral synthesis (`humanClick` \u002F `humanType` \u002F `humanScroll`) | works | Bezier+Fitts+jitter; profile-parameterized (`hand`, `tremor`, `wpm`, `scrollStyle`). |\n| Profile catalog (`mac-m4-chrome-stable`, `mac-chrome-stable`, `mac-chrome-beta`, `windows-chrome-stable`, `linux-chrome-stable`, `mac-brave-stable`) | works | Six real-device baselines captured against real Chrome on real devices, each filtered by FingerprintJS Pro `suspectScore \u003C= 20` and validated by the harness round-trip. Other catalog ids (`mac-m2-…`, `mac-intel-…`, `win11-edge-…`) still resolve to the generic placeholder. |\n| Trace recording \u002F replay (`mochi record` → `humanClick(sel, { trace })`) | deferred | API surface forward-compatible; recorder lands in v1.x. |\n| Browser-routed `session.fetch` (JA4\u002FJA3\u002FH2-coherent) | works | Routes through CDP — `Network.loadNetworkResource` for GETs, `page.evaluate(\"fetch\")` for non-GET. Real Chrome JA4 by construction; cookies inherit from the page's origin; CORS applies for non-GET cross-origin calls. |\n| `session.fetch` on FreeBSD \u002F Alpine musl \u002F Windows arm64 | works | Routes through the running Chromium process, so any host that runs CfT runs `session.fetch`. |\n| `Page.screenshot` | works | PNG\u002FJPEG\u002FWebP via CDP `Page.captureScreenshot`; `fullPage`, `clip`, `omitBackground`, `quality`, `encoding` opts. Element-bounded capture (`{ element: handle }`) is a separate brief. |\n| Proxy auth (HTTP\u002FHTTPS\u002FSOCKS5) | works | Inline URL or `ProxyConfig` shape; CDP `Fetch.authRequired`, no extension. |\n| Cookie persistence (`Session.cookies.{save,load}`) | works | JSON file with version header + regex domain filter. Round-trips losslessly. |\n| `Page.localStorage.{get,set}` \u002F `Page.sessionStorage` | works | DOMStorage CDP, frame-scoped (defaults to current main-frame origin). |\n| `Page.grantAllPermissions()` | works | Wraps `Browser.grantPermissions` with the full descriptor list. Pairs with R-036. |\n| Proxy-PAC scripts | not yet | Use system network policy until the flag lands. |\n| Turnstile auto-click | works | `@mochi.js\u002Fchallenges` — opt-in via `challenges: { turnstile: { autoClick: true } }`. Visible-checkbox variants only; image \u002F audio \u002F managed escalations fire `onEscalation` instead of clicking blindly. |\n| Init-script delivery without `Page.createIsolatedWorld` | works | Dual-mechanism: `Fetch.fulfillRequest` body-splice on Document responses (CSP-rewritten) plus `Page.addScriptToEvaluateOnNewDocument({ runImmediately: true, worldName: \"\" })` fallback for `about:blank` and other non-HTTP nav targets. Idempotency guard via `__mochi_inject_marker`. |\n| `bot.incolumitas.com` anti-debugger trap | known limit | C++-only fix path. Every CDP-driven tool trips it identically. |\n| `deviceandbrowserinfo.com\u002Fare_you_a_bot` worker-injection trap | known limit | Same anti-debugger family as incolumitas. |\n| `fingerprint.com\u002Fweb-scraping` (datacenter IP, cold session) | known limit | Server-side IP-class scoring; route through residential. |\n| Cross-engine FPU \u002F JIT divergence (Safari-from-Chromium) | out of v1 scope | v1 is Chromium-family only. |\n| Mobile \u002F touch profiles | out of v1 scope | v2 roadmap. |\n\nThe [full limits document](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Flimits) has the per-vector root-cause analysis. Read it before opening an issue saying \"X site detects mochi\" — half the answers are already there.\n\n## Comparison\n\nmochi's peer group is the JS-layer stealth-automation tools that drive stock or near-stock Chromium. Each row below is a structural axis, not a marketing axis. The [Comparison page](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Fcomparison) has the deep version: each claim cites a specific upstream-source line range you can follow.\n\n| | mochi | [patchright](https:\u002F\u002Fgithub.com\u002FKaliiiiiiiiii-Vinyzu\u002Fpatchright) | [puppeteer-real-browser](https:\u002F\u002Fgithub.com\u002Fzfcsoftware\u002Fpuppeteer-real-browser) (archived) | [nodriver](https:\u002F\u002Fgithub.com\u002Fultrafunkamsterdam\u002Fnodriver) | [undetected-chromedriver](https:\u002F\u002Fgithub.com\u002Fultrafunkamsterdam\u002Fundetected-chromedriver) |\n|---|---|---|---|---|---|\n| Runtime | Bun ≥ 1.1 | Node | Node | Python | Python |\n| Browser | stock CfT | stock Chromium | stock Chrome + helpers | stock Chrome | stock Chrome (patched binary) |\n| `Runtime.enable` avoided | yes (asserted) | yes | no | partial | n\u002Fa (WebDriver) |\n| `Page.createIsolatedWorld` avoided | yes | yes | no | yes | n\u002Fa |\n| Relational `(profile, seed)` Matrix | yes | no | no | no | no |\n| JS-layer fingerprint coverage (48-rule DAG) | yes | partial (~12 patches) | partial (fingerprint-injector add-on) | partial | partial (flag-level) |\n| Probe-Manifest harness as CI gate | yes | no | no | no | no |\n| Behavioral synthesis (`humanClick`\u002F`humanType`) | yes (Bezier+Fitts+jitter) | no | mouse-helper only | mouse-only | no |\n| JA4\u002FJA3\u002FH2-coherent `session.fetch` | yes (browser-routed) | no | no | no | no |\n| Single-runtime stack (no `pip install` next to `npm install`) | yes | yes | yes | yes (Python only) | yes (Python only) |\n| Turnstile auto-click | yes (`@mochi.js\u002Fchallenges`) | yes | yes | partial | partial |\n| Stable-Chrome quirks accumulated over 4+ years | no | partial | partial | yes | yes |\n| Ecosystem maturity (issues \u002F PRs \u002F community) | new | mid | mid | mid | high |\n\n**Where mochi wins today:** relational consistency, no parallel HTTP layer, behavioral synthesis depth, harness-as-gate, single-runtime stack.\n\n**Where mochi loses today:** ecosystem age, Turnstile auto-click polish, accumulated quirks-fixes from years of production deployment.\n\nDeep version, with per-library audit reports: [mochijs.com\u002Fdocs\u002Freference\u002Fcomparison](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Fcomparison).\n\n## How it fits together\n\n```\n┌──────────────────────── User code (TypeScript) ────────────────────────┐\n│   import { mochi } from \"@mochi.js\u002Fcore\";                              │\n└────────────────────────────────┬───────────────────────────────────────┘\n                                 │\n            ┌────────────────────▼────────────────────┐\n            │  @mochi.js\u002Fcore   — launch, CDP pipe,   │\n            │                     Page, Session,      │\n            │                     Session.fetch (CDP) │\n            └────┬─────────────────┬──────────────────┘\n                 │                 │\n        ┌────────▼──┐  ┌───────────▼────────┐\n        │ inject    │  │ behavioral         │\n        │ (payload) │  │ Bezier+Fitts       │\n        └────┬──────┘  └────────────────────┘\n             │\n        ┌────▼──────────┐\n        │ consistency   │\n        │ (Matrix DAG)  │\n        └────┬──────────┘\n             │\n        ┌────▼──────────┐\n        │ profiles      │   ◄──── @mochi.js\u002Fharness\n        │ (data)        │         (Probe Manifest diff,\n        └───────────────┘          PR gate \u002F nightly)\n```\n\n\n## Documentation\n\n- **Site:** [mochijs.com](https:\u002F\u002Fmochijs.com) — landing, quickstart, reference, and the live `docs\u002F` content collection.\n- [Quickstart](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Fgetting-started\u002Fquickstart) — 5-minute walkthrough, copy-pasteable.\n- [Is mochi for me?](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Fgetting-started\u002Fis-mochi-for-me) — read this if you're choosing between mochi and a peer.\n- [The Consistency Engine](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Fconcepts\u002Fconsistency-engine), [Stealth philosophy](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Fconcepts\u002Fstealth-philosophy) — concept pages.\n- [Limits](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Flimits) — every known limit, with root cause and workaround.\n- [`PLAN.md`](PLAN.md) — design contract. The 8 architectural invariants live in §2.\n- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contributor workflow (clone → branch → PR, conventional commits, CI gate).\n- [`CHANGELOG.md`](CHANGELOG.md) — release notes.\n- [`packages\u002Fchallenges\u002FREADME.md`](packages\u002Fchallenges\u002FREADME.md) — Turnstile auto-click and the `challenges` convenience layer.\n\n## Convenience layers\n\nFor common bot-defense widgets, mochi ships opt-in convenience layers under `@mochi.js\u002Fchallenges`. These are thin wrappers around the existing inject + behavioral pipelines — no new fingerprint surface.\n\n```ts\nconst session = await mochi.launch({\n  profile: \"linux-chrome-stable\",\n  seed: \"user-12345\",\n  challenges: { turnstile: { autoClick: true } },\n});\n\u002F\u002F Every page from this session auto-clicks visible Turnstile checkboxes.\n```\n\nv0.2 covers: Cloudflare Turnstile (visible-checkbox variants only). Image\u002Faudio\u002Fmanaged escalations fire an `onEscalation` callback rather than clicking blindly. See [`packages\u002Fchallenges\u002FREADME.md`](packages\u002Fchallenges\u002FREADME.md) and the [Limits page](https:\u002F\u002Fmochijs.com\u002Fdocs\u002Freference\u002Flimits).\n\n## Why Bun-only?\n\n`mochi` is invariant I-3 in [`PLAN.md`](PLAN.md): Bun ≥ 1.1 only, no Node, no Deno. The reasons are concrete and load-bearing:\n\n- **Pipe-mode CDP** — `Bun.spawn` exposes file descriptors 3 + 4 directly to user code, which is what `--remote-debugging-pipe` needs. Node's `child_process` doesn't, so every Node-based stealth tool falls back to TCP — and a listening CDP port is a fingerprintable surface.\n- **Bun:SQL** — the offline-first profile lookup and `bun work` orchestrator both use `Bun.SQL` (libSQL-backed). No `better-sqlite3` native dep, no migration story, no platform-specific build issues.\n- **`Bun.spawn` + `Bun.serve` ergonomics** — the harness fixture server, `mochi capture` Probe Manifest collector, and the conformance-suite proxy chain all rely on Bun-native primitives that have no zero-cost equivalent in Node land.\n\nIf you need a Node-runtime stealth tool today, [patchright](https:\u002F\u002Fgithub.com\u002FKaliiiiiiiiii-Vinyzu\u002Fpatchright) and [puppeteer-real-browser](https:\u002F\u002Fgithub.com\u002Fzfcsoftware\u002Fpuppeteer-real-browser) are the live options.\n\n## Status\n\nFoundations in main; first npm release `2026-05-08`. `@mochi.js\u002Fcore` 0.8.0 drops the Rust `wreq` cdylib entirely — `Session.fetch` now routes through Chromium itself via CDP (real Chrome JA4 by construction; cookies inherit from the page's origin; proxy egress shared with `page.goto`). No parallel HTTP layer remains. 0.8 also retunes `ALL_BROWSER_PERMISSIONS` for Chromium 148. Earlier 0.5\u002F0.6\u002F0.7 lines shipped `Page.screenshot`, the cookies \u002F localStorage \u002F sessionStorage \u002F `grantAllPermissions` DX cluster, the `Fetch.fulfillRequest` dual-mechanism inject, byte-exact audio + canvas fingerprint blobs, and the host-OS-matching profile auto-pick (`mochi.defaultProfileForHost()`). Public API is stable; new surfaces are additive. The harness Zero-Diff gate runs on every PR. See [`CHANGELOG.md`](CHANGELOG.md) for what shipped where.\n\nIf you found this from somewhere and you're wondering whether to depend on it for production traffic: not yet. The \"what works \u002F what doesn't\" matrix above is the honest cut. v1.0 will say so plainly.\n\n## Contributing\n\n[`CONTRIBUTING.md`](CONTRIBUTING.md) covers the workflow: clone → branch → PR, conventional commits with package scopes, the gates CI runs (typecheck, lint, test, contract, harness Zero-Diff), and the documentation-discipline rules (every limit landed in `reference\u002Flimits.md` in the same PR as the gap it documents).\n\n## Acknowledgements\n\nStands on the shoulders of:\n\n- [nodriver](https:\u002F\u002Fgithub.com\u002Fultrafunkamsterdam\u002Fnodriver) — the no-`Runtime.enable` philosophy.\n- [rebrowser-patches](https:\u002F\u002Fgithub.com\u002Frebrowser\u002Frebrowser-patches) — leak vector documentation.\n- [patchright](https:\u002F\u002Fgithub.com\u002FKaliiiiiiiiii-Vinyzu\u002Fpatchright) — prior art on CDP-level stealth.\n- [CloakBrowser](https:\u002F\u002Fgithub.com\u002FCloakHQ\u002FCloakBrowser) — stealth conformance test bar.\n\n## License\n\n[MIT](LICENSE).\n","mochi 是一个用于浏览器自动化操作的库，特别强调了高保真度的指纹模拟。它通过使用一个基于 (profile, seed) 对的48规则确定性有向无环图来生成一致且透明的指纹信息，确保所有网络流量都通过Chromium本身路由，从而在JA4\u002FJA3等检测中表现得像真实的Chrome浏览器。此外，mochi提供了人类行为模拟功能如`humanClick`、`humanType`和`humanScroll`，以更自然地模仿用户交互。此项目适用于需要高度真实性和一致性浏览器自动化的场景，比如绕过网站反爬虫机制或进行复杂网页测试。采用TypeScript编写，并支持Bun运行时环境。","2026-06-11 03:32:30","CREATED_QUERY"]