feat(remote)!: let server_cdm outrank a service's cdm entry and set it per service

server_cdm: true on a remote_services entry now licenses remote runs with the server CDM even when the service has its own cdm entry, which stays the local-run device and the fallback. A services.<TAG>.server_cdm replaces the server value for one service, per-service entries match the tag in any case, and the configured fallback logs at info.

BREAKING CHANGE: with server_cdm: true, a per-service cdm entry no longer makes your own device license remote sessions; use dl --cdm or services.<TAG>.server_cdm: false.
This commit is contained in:
imSp4rky
2026-10-29 10:18:15 -06:00
parent d12a943d96
commit 7b927934e0
5 changed files with 147 additions and 37 deletions
+10 -8
View File
@@ -62,10 +62,11 @@ remote_services:
url: "https://my-box.example:8786"
api_key: "your-api-key"
# Optional: let the server's CDM do the licensing instead of a local CDM
server_cdm: false
# Optional: per-service config overrides applied locally (title_map, cdm, etc.)
server_cdm: true
# Optional: per-service config overrides applied locally (title_map, cdm, server_cdm, etc.)
services:
EXAMPLE:
server_cdm: false # this service licenses with your local CDM
title_map:
"0ABCDEF": "The Show (Renamed Locally)"
```
@@ -74,8 +75,8 @@ remote_services:
|---|---|---|
| `url` | yes | Base URL of the `unshackle serve` instance (no trailing slash needed) |
| `api_key` | no | Sent as the `X-Secret-Key` header on every request |
| `server_cdm` | no | When `true`, DRM keys are resolved by the server's CDM rather than a local device (default `false`) |
| `services` | no | Per-service local config overrides, keyed by service tag |
| `server_cdm` | no | `true` asks for the server's CDM, `false` uses your local device; unset follows the API key's server CDM grant |
| `services` | no | Per-service local config overrides, keyed by service tag in any case; a `server_cdm` here replaces the server entry's value for that service |
!!! warning "Port default"
The `unshackle serve` default port is **`8786`** (not `8080`). Use the port
@@ -267,16 +268,17 @@ hood the client walks a remote session through its lifecycle.
There are two ways DRM keys get resolved, chosen by the client's `server_cdm` flag.
=== "Proxied CDM (default)"
=== "Proxied CDM (server_cdm: false)"
Your **local CDM** builds the license challenge. The client sends that challenge
to `POST /api/session/{id}/license`, the server forwards it to the service, and
the raw license bytes come back for your local CDM to parse.
- Keeps your CDM local. The server only relays the license request.
- Used when `server_cdm` is `false` (the default).
- Also used for a run with `dl --cdm <name>`, and for a service whose own entry in
your `cdm` config named the loaded device. In each case the client sends
- Used when `server_cdm` is `false`, or when it is unset and the API key has no server
CDM for the service.
- Also used for a run with `dl --cdm <name>`, and, when `server_cdm` is unset, for a
service whose own entry in your `cdm` config named the loaded device. In each case the client sends
`server_cdm: false` when it creates the remote session, and the server builds the
service on your device even when the API key has the server CDM.
- A service option that the service lists in `SERVER_DEVICE_OPTIONS` keeps the server
+4 -2
View File
@@ -17,8 +17,10 @@ Over `--remote`, an override also makes your device license the remote session,
CDM. A per-service entry does the same when it names a device without a quality: a device name,
a `widevine` or `playready` sub-entry, or a profile or `default` sub-entry. A per-service entry
that selects only by quality, or only for a different profile, loads no device when the run
starts. unshackle then shows a warning, and the server decides who licenses the remote session.
With only the top-level `default` entry, the server CDM licenses when the API key has it. See
starts. When `server_cdm` is unset, unshackle then shows a warning, and the server decides who
licenses the remote session.
With only the top-level `default` entry, the server CDM licenses when the API key has it. Set
`server_cdm: true` on the server entry to keep a per-service entry for local runs only. See
[`remote_services`](services.md#remote_services).
```yaml
+26 -7
View File
@@ -89,16 +89,35 @@ Definitions of remote unshackle service servers, used by the `--remote` mode. Ea
named by you (pick it with `--server`, or do not write that flag when you configure only one) and gives
the server's `url` (required), an optional `api_key`, an optional `auth_headers` list, an
optional `server_cdm` boolean, an optional `timeout` in seconds, and an optional `services` sub-dict of per-service local
overrides such as `title_map`.
overrides such as `title_map`, keyed by service tag in any case. A `server_cdm` in a `services` entry replaces the `server_cdm` of
the server entry for that service.
Leave `server_cdm` unset and the client follows the server: it uses the server CDM for each
service the API key has it on, and the local CDM for the others. `true` asks for the server CDM
and falls back to the local CDM when the server refuses; `false` always uses the local CDM.
`dl --cdm <name>` on a remote run has the same effect as `false` for that run: the device you
name licenses the remote session. A [`cdm`](drm.md#cdm) entry for the service has the same
effect for that service when it names the device that unshackle loaded. Both take priority over
`server_cdm: true`. Remove the entry to use the server CDM for that service. The `default`
entry does not count.
name licenses the remote session, and it takes priority over `server_cdm: true`. When
`server_cdm` is unset, a [`cdm`](drm.md#cdm) entry for the service has the same effect for that
service when it names the device that unshackle loaded. The `default` entry does not count.
With `server_cdm: true`, the server CDM licenses the remote session, and the device of the entry
licenses only when the server refuses. Thus the entry applies to local runs and the server CDM
applies to remote runs:
```yaml
cdm:
EXAMPLE1: my_device # local runs of EXAMPLE1
remote_services:
my_server:
url: https://example.com
server_cdm: true # remote runs of EXAMPLE1 use the server CDM
services:
EXAMPLE2:
server_cdm: false # remote runs of EXAMPLE2 use your own device
```
When the server refuses the server CDM for a service whose `cdm` entry loaded the device,
unshackle logs the fallback to that device as info, not as a warning.
The server has the last word. When it keeps the server CDM for a remote session that your
device was to license, unshackle shows a warning. This occurs with a server that does not read
@@ -180,8 +199,8 @@ an API key without `server_cdm` for that service also cannot submit or retry `/a
jobs. Keys that have no `users` entry, such as `api_secret`, keep server CDM access.
A remote client that picks its own device licenses the remote session with that device. It picks
with `dl --cdm`, with a `cdm` entry for the service, or with `server_cdm: false` in its
`remote_services` entry. The server CDM makes no licence for that remote session, with or without
with `dl --cdm`, with `server_cdm: false` in its `remote_services` entry or in the `services`
entry for the service, or with a `cdm` entry for the service when `server_cdm` is unset. The server CDM makes no licence for that remote session, with or without
a limit. A service option that runs the remote session on the server's device, such as a
server-identity flag, keeps the server CDM.