Requirements
The browser runs on four hosts:
There is no build for Intel Macs or 32-bit Windows. The packages’ install path on Windows has only been tested against a test archive.
The Linux archives are
.tar.zst. The Python package reads them with Python 3.14 or later, the zstandard Python package, or the zstd command. The Node package needs the zstd command or a tar that accepts --zstd.
Install the package
pip install "apostate[playwright]", or npm install playwright) and pass the driver option. Detection explains why Patchright is the default. Do not run playwright install or patchright install. They download Playwright’s own Chromium, which Apostate never runs.
Download the browser
The first launch downloads the browser. To do it ahead of time:install picks the archive for this host, downloads it from the GitHub release the package names, checks its SHA-256 before opening it, extracts the whole tree into the cache, adds the Widevine module, and prints the browser’s path. A second run finds the install and downloads nothing.
Both packages install a command named
apostate. If both are installed, the one first on your PATH answers; python3 -m apostate always runs the Python one. Both use the same cache, so installing with one serves the other. CLI lists every command.
The cache
APOSTATE_CACHE_DIR, the cache_dir option (Node: cacheDir) and --cache-dir override it. Inside it:
install.json. If they differ, or the package pins a different archive, the next launch installs again.
Use a browser you already have
A launch looks for the browser in four places, in this order, and takes the first that answers:
A path you name, in either of the first two, can be the executable, a macOS
Chromium.app bundle, or the directory the archive unpacks to, such as apostate-152.0.7977.83-linux-x64/. The package takes the first two at your word. It refuses a path that names nothing with does not name a file, and a directory with no browser inside with names a directory with no browser inside it (expected Chromium.app, chrome or chrome.exe).
The well-known search never adopts a stock Chrome or Chromium. A candidate counts only if build/MANIFEST.lock with this build’s patch digests, or resources/profiles/catalogue.json, sits beside it, and its version is 152.0.7977.83. To have an archive you extracted by hand found, move the whole extracted directory into a well-known location, not only the browser out of it.
apostate info prints what the search found and what it passed over, under discovery, with a reason for each rejected candidate. In code, discovery_report() (Node: discoveryReport()) returns the same.
Mirrors
To fetch archives from your own server, setAPOSTATE_DOWNLOAD_BASE_URL to the directory that holds them, with the release’s file names:
Offline installs
On a host with no internet access, use one of these:- Copy the cache. Run
apostate installon a connected machine of the same platform, copy its whole cache directory to the offline host, and pointAPOSTATE_CACHE_DIRat it, or put it at the default path. The copy carries the Widevine module. - Unpack the archive. Download the archive and its
.manifest.jsonfrom the release page, compareshasum -a 256of the archive with the manifest’ssha256, and extract it on the offline host. Then setAPOSTATE_BINARYto the extracted directory, or move that directory into a well-known location.
Verify a download
Both packages hash the whole archive and compare it with a manifest’ssha256 before extracting anything. A mismatch stops the install, and the package opens nothing. Where the digest came from decides what the check proves, and apostate info reports it:
A package fetches a manifest only when its own does not list the archive, and it says so on stderr before downloading. A fetched digest comes from the same place as the archive, so it cannot catch a replaced one.
To check where an archive came from, verify its GitHub build-provenance attestation. Keep the archive when you install, then verify it with the GitHub CLI:
install/. APOSTATE_KEEP_ARCHIVE=1 keeps it on every install.
macOS signing
Releases since v0.2.0 are signed with a Developer ID, notarized and stapled, so a bundle downloaded with a web browser opens without a Gatekeeper prompt. To check a bundle:spctl prints accepted and source=Notarized Developer ID, stapler prints The validate action worked!, and codesign lists the Developer ID authority and Notarization Ticket=stapled.
Update
apostate install, installs the archive the new package pins. A new Chromium version installs beside the old one in the cache. A seed keeps its machine across Chrome updates. A release that changes the catalogue tables can move it. Seeds and identity explains how to check before you upgrade, and the changelog lists what each release changed.
Remove
clear deletes the whole cache directory, every installed version and the Widevine module included. Then uninstall the package with pip uninstall apostate or npm uninstall @heretic-tech/apostate. Fonts that apostate fonts install added stay in ~/.local/share/fonts/apostate-* on Linux and ~/Library/Fonts/apostate-* on macOS; delete those directories to remove them.