Nothing is cracked
Keys come from a Widevine device file you supply. NULLDRM automates the request flow a normal client already performs — it does not break the DRM.
NULLDRM logs the Widevine handshake from an Android app, recovers the content keys, and keeps a live restream healthy. One binary, self-hosted, no license keys.
An HTTPS proxy runs on the phone. NULLDRM drives the real app to a channel, reads the licence exchange as it happens, and resolves the keys with your own CDM.
PS C:\DRM-Decryption> ./bin/drm capture --app rte --channel rteone --auto-play [*] Stopping any old appproxy... [*] Pushing appproxy... 20:30:45 CA cert: /data/local/tmp/appproxy-ca.crt 20:30:45 capture → /data/local/tmp/appproxy_cap.json 20:30:45 DNS → [1.1.1.1:53 1.0.0.1:53 8.8.8.8:53] 20:30:45 listening on [::]:8080 [+] appproxy pid=12004; capture -> /data/local/tmp/appproxy_cap.json [*] Setting HTTP proxy -> 127.0.0.1:8080 (was "null") [*] Re-injecting system CA (Magisk)... [+] System CA present in conscrypt [*] Launching rte (package from module.yaml)… [*] Auto-play RTE One… [*] tap_ui "Live"/"Live tab, 2 out of 5" @ 324,2253 [*] tap Play @ 540,720 [+] player is PLAYING [*] Waiting for license + PSSH + manifest… watch: .cache\appproxy-default.log [+] Capture: pid: •••••••••••• mpd: https://dai.google.com/linear/dash/pa/event/••••/stream/•••• license: https://widevine.entitlement.••••/wv/web/ModularDrm?schema=1.0&form=json pssh: AAAASnBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAACoiIG••••… [+] key: ••••••••••••••••••••••••••••••••:•••••••••••••••••••••••••••••••• [+] session: outputs\rte\20261005-203112\session.json [*] Closing player… [*] HTTP proxy cleared
Redacted above: the package id, licence account, programme id, PSSH and keys. Those belong to your device and your subscription — NULLDRM keeps them in a local file that is never committed.
Keys come from a Widevine device file you supply. NULLDRM automates the request flow a normal client already performs — it does not break the DRM.
Some apps publish a playback catalogue, so a channel resolves with no phone at all. Everything else goes through the on-device proxy.
Live keys rotate. The agent notices a dead channel, re-runs the capture on a phone and posts fresh credentials back to the restreamer.
Same binary in all three. You decide how much automation and how much cooperation.
Run a capture when you need fresh material, decrypt locally, play it back. No server, nothing always-on.
Host the restreamer so channels stay up. Leave a phone plugged into the agent and it refreshes keys on its own when something dies.
A host publishes the channel list. Contributors point agents at it and cover only the channels they actually subscribe to.
That is the point of a swarm: distributed ownership. One person should not need every subscription. Many agents, many logins, one shared restream.
Go 1.25+ and Python 3.10+. A phone is only needed for the capture step.
# 1. build the one binary git clone https://git.nulldrm.com/nulldrm/Null-DRM-Official go -C apps/cli build -o ../../bin/drm . # 2. the CDM helper python -m venv .venv && .venv/bin/pip install -r apps/wvkey/requirements.txt # 3. see which app modules are compiled in ./bin/drm modules # 4. capture a channel ./bin/drm capture --app rte --channel rteone --auto-play --wvd data/device.wvd
A fresh clone ships no account ids, keys or hostnames — module code is public, its values are not. The quickstart walks through filling in your own, and the capture guide shows how to discover them for an app nobody has written a module for yet.
Everything ships as drm. Each subcommand is one of these.
| Part | What it does |
|---|---|
drm capture |
Drives the phone through the on-device proxy and writes a session: manifest, PSSH, keys. |
drm catalog |
Resolves a channel from a public playback catalogue, no phone involved. |
drm serve |
Control plane: REST API, SQLite, dashboard, and the restream supervisor. |
drm agent |
Polls the control plane, claims a free phone and refreshes dead channels. |
drm proxy |
Builds and installs the on-device HTTPS proxy, and records unknown apps. |
drm modules |
Lists the app modules this build contains and where each one reads its values. |
An app module is a small Go package plus a local values file. Adding one means a new package and a single import line — no fork, no plugin runtime. See writing a module.
Open source, free forever: no paid tiers, no licence keys, no feature gates.
The official source is Forgejo at nulldrm/Null-DRM-Official.
Modules, swarm tips and open-source updates. Shown once — it is always in the menu.