NULLDRM — My Computer

NULLDRM

self-hosted capture · decrypt · restream

Free forever · open source soon

One suite under apps/, three ways to run it: tools alone on your desk, a VPS restreamer with a phone agent that heals down channels, or a swarm where everyone contributes the subscriptions they already own. New streaming apps are modules on disk — YAML, Starlark, or shell — no fork required. No paid tiers. No license keys. Source opens soon on Forgejo.

Welcome
Three ways — Wizard

Pick the level that fits. Same toolkit underneath — you decide how much automation and how much cooperation you want.

1

Tools alone

Capture · unlock · restream on your machine

Use the CLIs and helpers by themselves. Log Widevine licenses, pull keys, decrypt, and restream locally (or pack HLS for VLC). No VPS required. No always-on agent. You run a capture when you need fresh material.

  • Phone MITM or catalog/CDM helpers
  • One-shot capture when a key expires
  • Full control, zero swarm
2

VPS + phone agent

Host the restreamer · agent heals downtime

Run streamd on a VPS (or home server) so channels stay restreamable. Plug a phone into a machine running the agent. When a stream goes down or keys go missing, the agent notices, opens the app, collects new keys, and posts them back — then the restreamer restarts with fresh credentials.

  • streamd serves HLS + dashboard on the VPS
  • Agent watches health on a loop
  • Your phone(s) automate key refresh
3

Join a swarm

Many owners · one restream grid

A host runs streamd and configures which channels the swarm should keep alive. You point your agent at that host and contribute only the channels you own — maybe you have Sky, someone else has Virgin, someone else has a free-to-air app. Nobody needs every subscription. The swarm covers the lineup together.

  • Host defines the channel list
  • Contributors bring phones + the apps they can legitimately open
  • When a feed dies, any matching agent in the swarm can refresh it
That is the point of the swarm: distributed ownership. One person should not need every platform. Many agents, many subscriptions, one shared restream.
apps/ — Program Files

Everything you run lives under apps/. Same binaries for mode 1, 2, or 3 — you choose how much automation to turn on.

FolderWhat it does
apps/streamd Web dashboard + API on :8083. Owns stream list, health, claims, credentials, and NRE/ffmpeg HLS workers. This is the VPS / host brain.
apps/agent Always-on refresher. Polls streamd, queues jobs, picks a free phone, opens the app module, captures keys, POSTs them back, closes clean.
apps/capture One-shot CLIs. Bare capture = passive MITM (you play the app). With --app / --auto-play it drives a module. tg4info is catalog/keys without the phone UI.
apps/proxy On-device HTTPS MITM (appproxy) + host CLI proxyctl. Build/push the binary, install the CA, discover unknown apps into outputs/discover/.
apps/modules Phone app plugins on disk. Each folder is one streaming app. Loaded at startup — no rebuild of agent/capture.
apps/pkg Shared Go libraries (ADB, MITM helpers, module loader, sessions). Not a binary you run.
apps/wvkey Thin Python CDM helper. Turns a license response into KID:KEY. Used by capture/agent — keep it small.
Tip Build everything from the repo root with .\build.ps1. Tools resolve from bin/, env vars, or PATH — no machine-specific paths baked in.
Modules — how far you can go

A module is a folder. Drop it under apps/modules/<name>/ and capture / agent load it. You can automate almost the whole phone path: force-stop, launch, wait for UI, tap, swipe, run device shell, wait for playback, then let the MITM grab license + manifest. Branchy UI? Add Starlark. Need CDM math? Use the Python helper.

Layout

apps/modules/myapp/
  module.yaml           # required — identity, channels, steps
  hooks/open_live.star  # optional — custom UI logic
  helpers/parse.py      # optional — your own scripts (call via shell / host)

Example module.yaml

name: myapp
package: com.example.tvapp
proxy_bin: bin/proxy-android-arm64
ca_hash: "6c3578b4"
license_url: "https://example.com/widevine/license"   # optional default

capture:
  profile: myapp
  require: [license_url, mpd, pssh]   # what “done” means

channels:
  main:
    label: Main live
    vars:
      live_card_index: 0

launch:
  - force_stop: {}
  - sleep: 400ms
  - monkey_launch: {}
  - sleep: 4s
  - dismiss_shade: {}
  - wait_ui:
      any_desc: ["live", "home"]
      timeout: 30s

autoplay:
  # Pure YAML path — or set hook: hooks/open_live.star
  - tap_ui:
      desc_re: ["(?i)Live"]
      timeout: 20s
  - sleep: 2s
  - tap_indexed_cards:
      index: "{{ vars.live_card_index }}"
      timeout: 25s
  - wait_playback:
      timeout: 60s
      soft_fail: true

Step actions (YAML)

force_stop, monkey_launch, sleep, dismiss_shade, wait_ui, tap_ui, tap_xy, swipe, shell, flow, tap_chip_then_play, tap_indexed_cards, wait_playback, starlark

Device shell (ADB)

Run any on-device command mid-flow — clear caches, am start extras, dumpsys, even a tiny on-phone script:

- shell:
    args: ["am", "start", "-n", "com.example/.LiveActivity"]
- shell:
    args: ["sh", "-c", "rm -f /data/local/tmp/debug.log"]

Starlark when YAML is not enough

# hooks/open_live.star
def open_live():
    dismiss_shade()
    nodes = dump_ui()
    for n in nodes:
        if "live" in n["desc"].lower():
            tap(n["cx"], n["cy"])
            break
    sleep(2)
    tap_indexed_card(index=var("live_card_index"), timeout=25)
    if not playback_active():
        fail("playback did not start")

open_live()

Builtins: log, fail, sleep, tap, swipe, dump_ui, dismiss_shade, playback_active, tap_indexed_card, call_flow, var, plus package / channel / label.

Python on the host (CDM)

Phone automation stays in YAML/Starlark. License crypto stays in a thin Python helper:

# apps/wvkey/wvkey.py — called by Go after MITM capture
python apps/wvkey/wvkey.py \
  --mode raw \
  --wvd data/device.wvd \
  --pssh <base64> \
  --license-url https://… \
  --quiet
# → prints KID:KEY lines

Modes: modulardrm (JSON license bodies) and raw (octet-stream / Brightcove). Keep custom Python next to your module if you want — wrap it from your own host scripts, or call tools after capture finishes.

How much can modules do? Enough to cover a new app without touching Go: identity, channels, full launch/autoplay, MITM capture hints, Starlark for messy UI, device shell for edge cases, and host Python for CDM. Point streamd at app=myapp; any agent with that APK installed can refresh it in a swarm.
How it works — Help and Support

Two capture styles

  • Phone MITM — on-device HTTPS proxy + module automation when the app must play.
  • Catalog + CDM — some apps expose enough material for helpers like tg4info --keys without opening the UI.
Tip Posting credentials to a running stream hard-restarts the media worker so NRE/ffmpeg pick up the new MPD and keys. Operator detail: Docs.
Swarm mode — Network Neighborhood

Mode 3 in detail. The host is just someone running streamd and publishing which channels they want kept alive. Contributors run agents pointed at that host.

  1. Host configures the channel list (Sky sports, Virgin entertainment, free apps, …)
  2. You join with an agent + phone that has your apps / logins
  3. Agent only picks up work for channels it can actually open
  4. Fresh keys / manifests POST back to the host → restream continues
  5. If your box sleeps, another contributor with the same app can cover

Example lineup: one person covers Sky, another covers Virgin Media, another covers a public broadcaster app. The host restreams the combined grid. Nobody bought the whole universe.

  • Every node is self-hosted — the host picks who may write credentials
  • No mandatory central SaaS; swarms are voluntary
  • Start alone (mode 1 or 2), grow into a crew when you are ready
Source — git.nulldrm.com

Primary repo: git.nulldrm.com/nulldrm/DRM-Decryption. The code will be open source soon — same toolkit, free forever, no license keys.

Until the public release lands, treat the Forgejo project as the source of truth for docs and layout. Follow releases there when binaries drop.
—Stars
—Forks
—Open issues
—Updated

Releases

Loading releases…
100% free — no licenses, no subscriptions, no feature gates. Devices, accounts, and what you restream stay on you.
NULLDRM Docs Git Telegram