Adding a DRM Key Vault
A vault is an external DRM key store that VibraVid queries before falling back to CDM extraction, and writes newly-extracted keys back to. Two backends ship today:
| Config key | Class | Protocol | Instance |
|---|---|---|---|
vault_1 |
Vault1Client (VibraVid/utils/vault/vault_1.py) |
REST | |
vault_2 |
Vault2Client (VibraVid/utils/vault/vault_2.py) |
JSON-RPC |
No code needed: another REST-compatible endpoint
If the backend speaks the same simple REST contract as vault_1 — Bearer-token-authenticated
POST {url}/get-keys and POST {url}/save-keys — just add a named entry under DRM.vault in
Conf/config.json:
"vault": {
"vault_1": { "url": "https://drm-db.dev", "token": "" },
"myvault": { "url": "https://drm.example.com", "token": "my-secret-token" }
}
DRMManager._build_vaults() (VibraVid/core/drm/manager.py) turns any name other than
vault_1/vault_2 into a Vault1Client instance via build_named_vault()
(VibraVid/utils/vault/__init__.py) — no Python code required. See
Configuration → Multiple / self-hosted DRM vaults
for the full wire contract both endpoints must implement.
Code needed: a different backend protocol
For a backend that isn't REST/JSON-compatible with vault_1 (a different transport, a
different auth scheme, a different response shape — like vault_2's JSON-RPC), subclass
BaseVault (VibraVid/utils/vault/base.py).
BaseVault
BaseVault gives every vault a shared __init__(name), close(), _clean_license_url() and
_prewarm(), plus a safe no-op default for every method a caller might invoke generically
across all vaults — override only what your backend actually supports:
class BaseVault:
def __init__(self, *, name: str): ...
@property
def is_connected(self) -> bool: ... # default: False
def close(self) -> None: ... # default: closes self.session if set
def track_download_async(self, title, media_type, service=None) -> None: ... # default: no-op
def set_keys(self, keys_list, license_url, pssh=None, kid_to_label=None) -> int: ... # default: 0
def get_keys_by_pssh(self, license_url, pssh) -> list[str]: ... # default: []
def get_keys_by_kids(self, license_url, kids, pssh=None) -> list[str]: ... # default: []
def get_keys_by_kid(self, license_url, kid) -> list[str]: ... # default: delegates to get_keys_by_kids
def report_wrong_key(self, kid, key, license_url, pssh=None) -> bool: ... # default: False
get_keys_by_kids() is the one method every vault should realistically implement — it's the
only lookup path DRMManager actually drives (_db_lookup() in manager.py). Everything else
is optional: vault_2.py (Vault2Client) only overrides is_connected and get_keys_by_kids
and inherits every other default as-is.
Minimal example
# VibraVid/utils/vault/vault_3.py
from VibraVid.utils.vault.base import BaseVault
from VibraVid.utils.config import config_manager
db_config = config_manager.config.get_dict("DRM", "vault")
VAULT_URL = db_config.get("vault_3", {}).get("url", "")
class Vault3Client(BaseVault):
def __init__(self, *, name: str):
super().__init__(name=name)
# set up self.session / auth here
@property
def is_connected(self) -> bool:
return bool(VAULT_URL)
def get_keys_by_kids(self, license_url, kids, pssh=None) -> list[str]:
# query your backend, return ["kid:key", ...]
...
my_vault = Vault3Client(name="myvault")
Registration
A new backend singleton is registered in exactly one place, _BUILTIN_VAULTS in
VibraVid/utils/vault/__init__.py:
# VibraVid/utils/vault/__init__.py
from .vault_3 import my_vault
_BUILTIN_VAULTS = [claudio_vault, lab_vault, my_vault]
This list backs all_vaults(), which generic callers use instead of importing and naming each
singleton by hand — e.g. MediaDownloader._report_wrong_key_to_vaults()
(VibraVid/core/velora/downloader.py) does for vault in all_vaults(): ... to notify every
built-in vault of a confirmed-wrong key, with no per-vault code to update when a new one is
added here.
DRMManager._build_vaults() (VibraVid/core/drm/manager.py) is a separate, ordered list —
it decides lookup priority (which vault is queried first) and additionally still needs an
explicit reserved-name branch for a config-key like vault_2/vault_3 that isn't REST/
build_named_vault()-compatible, the same way vault_2 is special-cased today:
# VibraVid/core/drm/manager.py
if name == "vault_2":
continue # reserved: always the JSON-RPC singleton, not a REST config entry
Add a matching if name == "vault_3": continue guard plus an if my_vault.is_connected: ...-style
unconditional append for your singleton at the top of _build_vaults() if it should also
participate in key lookups, not just wrong-key reporting.
Reporting a wrong key
report_wrong_key(kid, key, license_url, pssh) is called in the background (fire-and-forget,
from MediaDownloader._report_wrong_key_to_vaults() in VibraVid/core/velora/downloader.py)
whenever a post-decrypt key-sanity check proves a stored key wrong. Implement it only if your
backend has a way to record that (e.g. vault_1's save-keys with invalid: true,
incrementing a warning_count); otherwise leave BaseVault's no-op default, as vault_2 does
— its JSON-RPC backend has no equivalent concept.