> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostate.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Widevine

> How the packages add the Widevine DRM module to the browser, how to install a copy you choose, and the known issue with Windows personas on Linux.

Widevine is Google's DRM module. Video sites use it through Encrypted Media Extensions, and real Chrome resolves `navigator.requestMediaKeySystemAccess("com.widevine.alpha", ...)`. Google's licence does not allow shipping the module with the browser, so the release archive has none, and a browser without it rejects that call with `NotSupportedError`.

## How the packages add it

Every launch through either package checks the browser for the module and adds it when it is missing. `apostate install` and `apostate run` in both CLIs do the same. To add it:

1. The package looks for a copy on the host, in Google Chrome's install and in the user data directories of Chromium-based browsers, and takes the newest version it finds.
2. With none found, it downloads the module from Google's component update service, as Chrome's own component updater does, and checks it against the SHA-256 the service returns.
3. It keeps the module in the cache directory, in `widevinecdm/`, so the download happens once per host. Both packages share the cache.
4. It copies the module into the browser's install, where it loads for every profile, the temporary one `launch()` uses included. On Linux, a persistent profile also gets a file that points at the module.

When the package finds no copy and cannot download one, the launch continues without DRM and prints one warning.

The packages add the module only to a browser inside an Apostate install, the directory a release archive unpacks to, with `build/MANIFEST.lock` or `resources/profiles/` beside the executable.

<Note>
  The packages have no option to skip Widevine. A module you delete comes back on the next package launch.
</Note>

## Install a copy you choose

To install a particular copy, point the package at a `WidevineCdm` directory. It replaces the module the browser has.

<CodeGroup>
  ```bash Python theme={null}
  apostate provision-drm --list
  apostate provision-drm --source "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Libraries/WidevineCdm"
  ```

  ```javascript Node theme={null}
  import { provisionWidevine } from "@heretic-tech/apostate";

  console.log(await provisionWidevine({
    source: "/Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Libraries/WidevineCdm",
  }));
  ```
</CodeGroup>

`provision-drm --list` prints the copies on the host, with version and size. On a Mac with Google Chrome installed, the first line is:

```text theme={null}
4.10.3112.0      20416896  /Applications/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Libraries/WidevineCdm
```

Without `--source` or `source`, the command installs a copy it finds or downloads, as a launch does. Unlike a launch, it fails when it can neither find nor download a copy. Both print or return the platform, whether Widevine is verified there, where the copy came from, its version, and the cache and install paths. The Node CLI has no `provision-drm` command. Use `provisionWidevine()`.

## Keep component updates on

`--disable-component-update` stops Chrome's preinstalled components from registering. On a macOS host Widevine is one of them, and a Playwright launch, which passes the switch by default, rejected the Widevine key system on a macOS arm64 host with the module installed. On Linux the browser loads the module from `WidevineCdm` beside the executable, so the switch does not keep it out there.

Patchright and Puppeteer do not pass the switch. The packages remove it from the driver's default switches and keep it only when you put it in `args` yourself. If you drive the browser with Playwright yourself, remove it too:

<CodeGroup>
  ```python Python theme={null}
  from playwright.sync_api import sync_playwright

  from apostate import ensure_binary

  with sync_playwright() as p:
      browser = p.chromium.launch(
          executable_path=str(ensure_binary()),
          args=["--fingerprint=42", "--fingerprint-platform=windows"],
          ignore_default_args=["--disable-component-update"],
      )
      print(browser.version)
      browser.close()
  ```

  ```javascript Node theme={null}
  import { chromium } from "playwright-core";
  import { ensureBinary } from "@heretic-tech/apostate";

  const browser = await chromium.launch({
    executablePath: await ensureBinary(),
    args: ["--fingerprint=42", "--fingerprint-platform=windows"],
    ignoreDefaultArgs: ["--disable-component-update"],
  });
  console.log(browser.version());
  await browser.close();
  ```
</CodeGroup>

`ensure_binary()` and `ensureBinary()` return the browser's path and add nothing else. A browser started this way gets no Widevine from the packages until a package launch or `apostate install` has added it to the install. [Raw binary](/guides/raw-binary) covers driving the browser yourself.

## Hosts

| Host        | Widevine                                                                                                        |
| ----------- | --------------------------------------------------------------------------------------------------------------- |
| macOS arm64 | Works                                                                                                           |
| Linux x64   | Works                                                                                                           |
| Linux arm64 | Works                                                                                                           |
| Windows x64 | Not verified. `provision-drm` warns that it is not verified there. If it fails, the browser starts without DRM. |

## Check it from a page

```python theme={null}
from apostate import launch

CHECK_WIDEVINE = """async () => {
    try {
        const access = await navigator.requestMediaKeySystemAccess("com.widevine.alpha", [{
            initDataTypes: ["cenc"],
            videoCapabilities: [{ contentType: 'video/mp4; codecs="avc1.42E01E"' }],
        }]);
        return `supported: ${access.keySystem}`;
    } catch (error) {
        return `${error.name}: ${error.message}`;
    }
}"""

with launch(fingerprint=42, fingerprint_platform="windows") as browser:
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.evaluate(CHECK_WIDEVINE))
```

With the module, this prints `supported: com.widevine.alpha`. Without it, it prints `NotSupportedError: Unsupported keySystem or supportedConfigurations.`

## Known issue with Windows personas on Linux

The module is the host's build. On a Linux host, a Windows persona carries the Linux module, and a page can tell:

* On 2026-09-26, with the v0.4.3 release binary on a bare-metal x86 Linux server under Xvfb, FingerprintJS Pro flagged Windows personas as `anti_detect_browser` in 6 of 6 runs with the Linux module present, and in 0 of 6 runs without it. The seeds and the exit were the same in both sets.
* The module's first license message names its platform, `Linux` and `x86-64`, in the clear, where real Windows Chrome sends a 2-byte certificate request.

DRM playback works with the module present. The packages and `apostate run` put the module back on every launch, and `--disable-component-update` does not keep it out on Linux. To run a Windows persona on Linux without it, delete the `WidevineCdm` directory beside the executable and start the binary directly, not through the packages or `apostate run`. [Known gaps](/known-gaps#the-linux-widevine-module-under-a-windows-persona) tracks the issue.
