@heretic-tech/apostate on npm) finds or downloads the browser, looks up the proxy’s exit, and starts the browser through Patchright, a Playwright build. It returns the driver’s own objects, so the rest of your script is ordinary Playwright or Puppeteer code. The package is an ES module, loaded with import, and needs Node 22 or later. Installation covers npm install @heretic-tech/apostate and the browser download.
Launch a browser
fingerprint is the seed. The same seed and persona give the same machine on every launch. fingerprintPlatform is the persona, the operating system the browser presents. Personas and Seeds and identity explain both.
Under Patchright or Playwright, launch() returns Playwright’s Browser. The browser runs on a temporary normal profile, which is deleted when the browser closes. newPage() opens pages in that profile. The first call returns the blank tab the browser started with.
newPage() takes no options, because every page shares the one profile.
Entry points
launchPersistentContext() with a directory stores a seed in DIR/apostate/identity, and every later launch with that directory presents the same machine. launch() refuses userDataDir.
Options
Proxies, Locale and timezone, Custom profiles and Linux servers cover the options in use. Node API lists every export and option, and Switches lists what
args accepts.
Driver options
Three options go through to the driver:envgoes over the browser’s environment, which the package givesLANGUAGE,LC_ALL,LC_MESSAGES,LANGandTZ(Locale and timezone). Your entries win.ignoreDefaultArgsremoves switches the driver adds by default. The package adds--disable-component-updateto the list, so the driver does not pass that switch (Widevine).defaultViewportgoes to Puppeteer. The package passesnullunless you set it.
page.setViewportSize(). From 0.4.4 it also turns off colour-scheme emulation, so a page reads the persona’s light or dark theme. With 0.4.3, call await page.emulateMedia({ colorScheme: null }) on each page (Known gaps). Screen and window has what a viewport of your own changes.
Diagnostics
The objectlaunch() or launchPersistentContext() returns carries three extra properties. A context from launchContext() carries its browser as context.apostateBrowser, which has them.
For the launch at the top of this page,
browser.apostateDiagnostics is:
geoip is resolved when the lookup answered, unresolved when it failed, disabled with geoip: false, and explicit when no lookup was needed because you passed locale and timezone and no proxy. proxy is the proxy URL without its credential. warnings lists the launch’s warnings:
- A launch with no seed warns that its machine changes on every launch.
- A profile you wrote warns that nothing checks its coherence.
- A failed GeoIP lookup warns that the locale or timezone was not set. GeoIP warnings are also printed with
console.warn, prefixed[Apostate].
--fingerprint-explain (Verify):
Drivers
Patchright is installed with the package and is the default. The package also drivesplaywright, playwright-core, puppeteer and puppeteer-core. It uses the first one installed, in that order, or the one you name with driver. None of them needs to download a browser. driverInfo() reports what is installed and which driver a launch would use:
Puppeteer
newPage() already opens pages in the browser’s normal profile. Two things Puppeteer does are visible to a page, measured with puppeteer-core 25.12.0:
- A stack trace from code you pass to
page.evaluate()names your script’s absolute path, percent-encoded afterpptr:evaluate;file. Under Patchright the same trace names no file. page.exposeFunction("hello", ...)also installs a global namedpuppeteer_hello.
launchProcess
launchProcess() starts the browser with no driver attached. It takes the same options as launch(), applies the persona, proxy and GeoIP the same way, starts Xvfb for a headed launch on a Linux host with no display, and returns an ApostateProcess. Connect any client to it, for example over the DevTools protocol:
userDataDir works here as an option. stdio and cwd go to Node’s spawn(), and stdio defaults to "ignore". From 0.4.4, launchProcess() passes --no-first-run and --no-default-browser-check. With 0.4.3, add both to args, or a headed first launch of a new user data directory can stop at a first-run dialog and never open its DevTools port.
Keep --remote-debugging-port on 127.0.0.1, its default. Any program that reaches the port can drive the browser. The browser closes DevTools requests that web pages send (Detection). Raw binary covers other ways to connect.
Host mode
fingerprint: "host" presents the host’s own values and composes nothing. "off", "false", "0", "disable" and "disabled" mean the same. Use it to tell whether a problem comes from the persona or from the host and network. Host mode refuses fingerprintPlatform, and the package sets no locale variables. Headless host mode reports an 800x600 screen (Screen and window).
Close the browser
await browser.close() closes the browser, deletes its temporary profile, and stops any Xvfb display the package started. Closing a context from launchContext() closes its browser. The returned objects also work with await using, tested on Node 26:
Errors
Package errors are subclasses ofApostateError and carry a code and a details object. A malformed option value, such as a proxy URL that does not parse, throws a TypeError instead. From 0.4.4, a timezone that is not an IANA zone throws a RangeError.