# Configuration Reference This page is the exhaustive, authoritative reference for **every** key you can set in `unshackle.yaml`. For each key you will find its name, expected type, default value, and what it does. Keys are grouped by area so you can jump straight to the part of unshackle you are configuring. If you are new to the config file (where it lives, how unshackle discovers it, and how to edit it), start with [The Configuration File](../getting-started/configuration-file.md). This page assumes you already know the basics and want precise details. !!! info "How defaults work" Every key is optional. If you omit a key, unshackle uses the built-in default listed here. There is **no schema validation**: unknown keys are silently ignored (they never become settings), and the file is parsed with `yaml.safe_load`. An empty or missing file means *all* defaults apply. !!! warning "Two keys behave specially on load" - `curl_impersonate` is a **deprecated** alias of [`network`](#network-proxy) and emits a `DeprecationWarning`. - `scene_naming` has been **removed**. If it is present at all, unshackle exits immediately with an error telling you to use [`output_template`](#output-naming) instead. See [Deprecated & removed keys](#deprecated-removed-keys) at the end of this page. --- ## Quick index | Area | Keys | |------|------| | [Directories](#directories) | `directories`, `filenames` | | [Services & authentication](#services-auth) | `services`, `credentials`, `firefox_cookies`, `remote_services`, `serve` | | [Download & processing](#download-processing) | `dl`, `subtitle`, `audio`, `muxing`, `language_tags` | | [Output & naming](#output-naming) | `output_template`, `tag`, `tag_group_name`, `tag_imdb_tmdb`, `chapter_fallback_name`, `unicode_filenames` | | [DRM & CDM](#drm-cdm) | `cdm`, `remote_cdm`, `decryption` | | [Network & proxy](#network-proxy) | `network`, `headers`, `proxy_providers` | | [Key vaults](#key-vaults) | `key_vaults`, `vault_timeout` | | [External API keys](#external-api-keys) | `tmdb_api_key`, `simkl_client_id`, `decrypt_labs_api_key`, `ipinfo_api_key` | | [Caching & updates](#caching-updates) | `title_cache_enabled`, `title_cache_time`, `title_cache_max_retention`, `update_checks`, `update_check_interval` | | [Logging, privacy & debug](#logging-privacy-debug) | `redact_paths`, `debug`, `debug_keys`, `debug_requests`, `set_terminal_bg` | | [Deprecated & removed](#deprecated-removed-keys) | `curl_impersonate`, `downloader`, `scene_naming` | --- ## Directories { #directories } The `directories` key is a **dict** mapping directory names to filesystem paths. Only the names listed below are honoured; a handful are **protected** and cannot be moved (attempts to override them are silently skipped). All defaults are computed relative to the installed package, and every user-settable path is passed through `Path(...).expanduser()`, so `~` works. ```yaml title="unshackle.yaml" directories: downloads: ~/Media/unshackle temp: /mnt/fast-scratch/unshackle-temp wvds: ~/.unshackle/WVDs prds: ~/.unshackle/PRDs ``` | Key | Type | Default | Overridable | Purpose | |-----|------|---------|:-----------:|---------| | `downloads` | path | `/downloads` | ✅ | Default output directory for finished files. | | `temp` | path | `/temp` | ✅ | Temporary working files during download/decrypt/mux. | | `cache` | path | `/cache` | ✅ | Generic cache, title cache, and the update-check store. | | `cookies` | path | `/cookies` | ✅ | Per-service cookie files (and VPN cookie/token files). | | `logs` | path | `/logs` | ✅ | Log files. | | `exports` | path | `/exports` | ✅ | Export JSON files. | | `wvds` | path | `/WVDs` | ✅ | Widevine device files (`.wvd`). | | `prds` | path | `/PRDs` | ✅ | PlayReady device files (`.prd`). | | `dcsl` | path | `/DCSL` | ✅ | DCSL data. | | `commands` | path | `unshackle/commands` | ✅ | CLI command modules. | | `services` | list \| path | `[unshackle/services]` | ✅ | Service search paths and/or remote repo specs (see below). | | `vaults` | path | `unshackle/vaults` | ✅ | Vault backend modules. | | `fonts` | path | `unshackle/fonts` | ✅ | Bundled fonts. | | `user_configs` | path | `unshackle/` | ❌ protected | Where `unshackle.yaml` lives. | | `data` | path | `unshackle/` | ❌ protected | Base for the data subdirectories above. | | `core_dir` | path | `unshackle/core` | ❌ protected | Package core. | | `namespace_dir` | path | `unshackle/` | ❌ protected | Package root. | | `app_dirs` | — | `AppDirs("unshackle", False)` | ❌ protected | Internal AppDirs instance. | !!! note "The `services` directory is special" `services` may be a **list**, and each entry can be either a local directory or a **repository spec**: a git URL (`https://…`, `ssh://…`, `git@…`, or anything ending in `.git`) or `owner/repo` shorthand. Repo specs are cloned and updated automatically; plain paths are used as-is. **List order is priority**: the first source to define a service tag wins, so put your own local service directories **last** to make them fallbacks. ```yaml directories: services: - unshackle-dl/community-services # cloned from GitHub - https://example.com/private.git@stable - ~/my-services # local, lowest priority ``` !!! info "How git-backed service repos are handled" unshackle's use of git is **read-only on the remote**: it only ever performs a shallow `clone`, `fetch`, `pull`, and a local `reset`; **nothing is ever pushed**. Private repos are authenticated through your existing git credential helper, and unshackle stores no tokens of its own. Clones live under `/_repos//` (or the bundled `unshackle/services`), and **nothing is written to the cache directory**. After the first clone unshackle re-pulls **at most once every 24 h**, so it does not touch the network on every run. !!! warning "The two refresh paths diverge on purpose" The **automatic** 24 h-TTL refresh that happens during a normal `dl`/`search` run will deliberately **refuse to refresh and exit** (naming the offending clone) if that clone has uncommitted changes to tracked files or unpushed local commits, so a background pull can never clobber work in progress. Untracked files (new service folders, `__pycache__`) never block it. The **manual** `unshackle util refresh-services` command does the opposite: it **hard-resets** the clone to upstream, discarding any local edits. Reach for the manual command only when you actually want to throw local changes away. !!! warning "Read-only installs and reinstalls" If the services directory lives inside the installed package, a reinstall can delete the `_repos` clones. They are simply re-cloned on next use. On **read-only installs** you must point `services` at a writable path, or cloning will fail. ### Filenames { #filenames } The `filenames` key is a **dict** of templated file/name patterns. Each value is used verbatim (no path processing). Braced fields like `{time}` and `{service}` are filled in at runtime. | Key | Type | Default | Notes | |-----|------|---------|-------| | `log` | str | `"unshackle_{name}_{time}.log"` | Written under `directories.logs`. | | `debug_log` | str | `"unshackle_debug_{service}_{time}.jsonl"` | Structured debug log, under `directories.logs`. | | `config` | str | `"config.yaml"` | Per-service config file, under that service's directory. | | `root_config` | str | `"unshackle.yaml"` | The main config filename itself. | | `chapters` | str | `"Chapters_{title}_{random}.txt"` | Under `directories.temp`. | | `subtitle` | str | `"Subtitle_{id}_{language}.srt"` | Under `directories.temp`. | --- ## Services & authentication { #services-auth } ### `services` - **Type:** `dict` keyed by service tag  ·  **Default:** `{}` Per-service configuration, keyed by the canonical service tag (for example `AMZN`, `NF`, `DSNP`). unshackle reads several well-known sub-keys out of each service's block: - **`proxy_map`**: a dict mapping `"provider:query"` or `"query"` to a proxy value, used to auto-select a proxy for that service. - **`title_map`**: an exact-match rename map applied to fetched titles (source name → desired name), so a service that names a title differently from your library still matches. Individual services may read any additional keys they define. The whole block is handed to the service as `self.config`. !!! tip "Why put secrets here and not in the service's own `config.yaml`?" A service ships a per-service `config.yaml` inside its own folder, and that file is typically shared/committed and **not** meant to be edited by you. So anything user- or device-specific (API keys, account IDs, device attributes) belongs in this `services` block of `unshackle.yaml` instead, where it is **merged over** the service's `config.yaml` at load time. That is why the two config locations exist: one for shared service defaults, one for your private overrides. ```yaml services: AMZN: proxy_map: default: us title_map: "The Grand Tour: The Movie": "The Grand Tour Presents The Movie" ``` ### `credentials` - **Type:** `dict` keyed by service tag  ·  **Default:** `{}` Per-service login credentials. Each value is a `username:password[:extra]` string (a list or dict form is also accepted where a service supports it). These are parsed into `Credential` objects; the credential's SHA-1 is also used as an account hash for cache keys. ```yaml credentials: NF: user@example.com:hunter2 AMZN: - primary@example.com:pw1 - secondary@example.com:pw2 ``` !!! tip "Cookies vs credentials" Cookies are stored as files under [`directories.cookies`](#directories), not in this key. A service's `authenticate()` accepts cookies, credentials, or both. ### `firefox_cookies` - **Type:** `dict`  ·  **Default:** `{}` Settings for extracting cookies directly from a local Firefox profile. A service block is expected to provide `hosts` (a list of cookie hostnames; entries shorter than 3 characters are ignored to prevent dumping the whole cookie store) and an optional `local_storage` boolean to also pull matching local-storage entries. Extraction is read-only. !!! note "Firefox does not need to be closed" The extractor copies **both** `cookies.sqlite` **and** its WAL file into a `0700` temp directory; the WAL copy exists to capture writes Firefox has not yet flushed to the main DB, so the copy is current even against a running browser. The only real constraint is that Firefox must not hold an exclusive write lock at the exact instant of the copy. The live profile is otherwise left untouched. !!! tip "When you actually need `local_storage: true`" Enable it only for services that keep their auth tokens in the browser's **localStorage** (`webappsstore.sqlite`) rather than in HTTP cookies. If a service authenticates with plain cookies, leaving it off is correct. !!! warning "Extraction fails silently to file cookies" If extraction yields no cookies or fails for **any** reason, unshackle silently falls back to the normal file-based cookie path (`cookies/.txt` or `cookies//.txt`). This is a safety net, but it also means that if you expected Firefox cookies and file cookies are being used instead, extraction quietly failed. Check that first when troubleshooting. ### `remote_services` - **Type:** `dict`  ·  **Default:** `{}` Definitions of remote unshackle service servers, used by the `--remote` mode. Each entry carries a `services` sub-dict of remote service tags, which unshackle turns into synthetic CLI commands that run against the remote server. ### `serve` - **Type:** `dict`  ·  **Default:** `{}` Configuration for the `serve` command (the built-in REST API server). !!! tip "Declaring `dl` defaults under `serve`" You can nest `dl` flags (`downloads`, `workers`, `best_available`, and so on) inside `serve`. The point is to raise concurrency and behaviour defaults **server-wide** (every request the server handles picks them up) without having to change each client call individually. --- ## Download & processing { #download-processing } ### `dl` - **Type:** `dict`  ·  **Default:** `{}` Default options forwarded to the `dl` command. Anything you would otherwise pass as a `dl` flag can be given a persistent default here. See [Downloading](../guide/downloading.md) for the flags themselves. ### `subtitle` - **Type:** `dict`  ·  **Default:** `{}` Subtitle handling options. See [Subtitles](../guide/subtitles.md). ### `audio` - **Type:** `dict`  ·  **Default:** `{}` Audio handling options. The most notable sub-key is **`codec_priority`**, an ordered list of audio codecs used to break ties in track selection. ```yaml audio: codec_priority: - EAC3 - AC3 ``` !!! note "`codec_priority` is a soft priority: nothing is dropped" `codec_priority` only breaks ties **among tracks that share the same bitrate and language**. Codecs not in the list keep their normal bitrate-based ordering and are simply placed after all listed codecs, and none are ever removed. Two higher-level rules still override it: **Atmos tracks always take precedence over `codec_priority`**, and **audio-description tracks are always forced to the end** regardless of it. When `codec_priority` is unset, tracks sort by bitrate alone, with those same Atmos/descriptive rules still applied. ### `muxing` - **Type:** `dict`  ·  **Default:** `{}` Matroska (MKV) muxing options. ### `language_tags` - **Type:** `dict`  ·  **Default:** `{}` Language-tag remapping and the rule engine behind the `{lang_tag}` filename variable. When a `rules` list is present, each rule is evaluated in order and the first match's `tag` is used. A rule may test: | Condition | Meaning | |-----------|---------| | `audio` | Matches against the audio track languages. | | `subs_contain` | At least one subtitle language matches. | | `subs_contain_all` | Every listed language must be present (scalar or list). | ```yaml language_tags: rules: - audio: [en] subs_contain_all: [en] tag: "" # native English release, no tag - audio: [ja] subs_contain: [en] tag: "SUBBED" ``` --- ## Output & naming { #output-naming } For the full picture of how templates render, see [Output & naming](../guide/output-and-naming.md). The keys are summarised here. ### `output_template` - **Type:** `dict`  ·  **Default:** `{}` Filename templates keyed by media kind, using `{variable}` placeholders. Recognised kinds are `movies`, `series`, and `songs` (and `albums` for folder templates). A trailing `?` on a variable (e.g. `{quality?}`) marks it optional: if empty, it and one adjacent separator are dropped. ```yaml title="unshackle.yaml" output_template: movies: "{title}.{year}.{quality?}.{source}-{tag}" series: "{title}.{season_episode}.{episode_name?}.{quality?}.{source}-{tag}" songs: "{track_number}. {title}" folder: # special nested key, see below movies: "{title} ({year})" series: "{title} ({year})" ``` **The nested `folder` sub-key** controls output subfolders and is popped out of `output_template`: - A **dict** sets per-kind folder templates (kinds: `movies`, `series`, `songs`, `albums`). - A **str** sets a single folder template for everything. unshackle validates templates on load and emits warnings for unknown variables, non-string values, filesystem-unsafe characters (`< > : " / \ | ? *`), empty templates, and unknown folder-kind keys. ??? example "All valid template variables" `title`, `year`, `season`, `episode`, `season_episode`, `episode_name`, `date`, `quality`, `resolution`, `source`, `tag`, `track_number`, `artist`, `album_artist`, `album`, `disc`, `track_total`, `disc_total`, `release_type`, `genre`, `explicit`, `isrc`, `upc`, `label`, `audio`, `audio_channels`, `audio_full`, `atmos`, `dual`, `multi`, `video`, `hdr`, `hfr`, `edition`, `repack`, `lang_tag`. ### Tagging & naming keys | Key | Type | Default | Description | |-----|------|---------|-------------| | `tag` | str | `""` | Release-group tag appended to filenames and written into MKV tags. | | `tag_group_name` | bool | `true` | Include the group name (`tag`) in the MKV `Group` tag. | | `tag_imdb_tmdb` | bool | `true` | Write IMDb/TMDB/TVDB external-ID tags into the MKV (needs metadata providers). | | `chapter_fallback_name` | str | `""` | Fallback chapter-name template, e.g. `"Chapter {i:02}"`. | | `unicode_filenames` | bool | `false` | Allow Unicode in filenames; when `false`, names are ASCII-sanitised. | --- ## DRM & CDM { #drm-cdm } The full conceptual guide lives at [DRM & CDM](../guide/drm-and-cdm.md). These are the config keys. Device files themselves live in [`directories.wvds`](#directories) (`.wvd`) and `directories.prds` (`.prd`). ### `cdm` - **Type:** `dict`  ·  **Default:** `{}` Maps a service tag to the CDM (Widevine/PlayReady device) to use, with a `default` fallback. Resolution is case-insensitive: an explicit CLI override wins, then the per-service entry, then `default`. ```yaml cdm: default: chromecdm_l3 NF: android_l1 ``` A value may also be a **nested dict** for advanced selection by quality, DRM system, or profile: ```yaml cdm: AMZN: ">=1080": android_l1 # by track height "<1080": chromecdm_l3 widevine: android_l1 # by DRM system playready: sl3000 default: chromecdm_l3 ``` Quality keys support `1080`, `>=1080`, `>720`, `<=576`, `<480` style comparisons; DRM keys are `widevine` / `playready`. ### `remote_cdm` - **Type:** `list[dict]`  ·  **Default:** `[]` A list of remote CDM server definitions. Each entry is matched by its `name` (referenced from [`cdm`](#cdm)), and its `type` selects the backend: | `type` | Backend | Key fields | |--------|---------|-----------| | `decrypt_labs` | Decrypt Labs KeyXtractor | `host` (default `https://keyxtractor.decryptlabs.com`), `device_name`, `secret` (falls back to `decrypt_labs_api_key`) | | `custom_api` | Fully YAML-configurable remote API | `device`, `auth`, `endpoints`, `request_mapping`, `response_mapping`, `caching`, `timeout` | | *(none)*, `Device Type: PLAYREADY` | pyplayready `RemoteCdm` | `host`, `secret`, `device_name`, `security_level` (default 3000) | | *(none)*, otherwise | pywidevine `RemoteCdm` | `host`, `secret`, `device_name`, `device_type`, `system_id`, `security_level` (default 3000) | ```yaml remote_cdm: - name: keyxtractor type: decrypt_labs device_name: L1 - name: my_wv_server host: https://cdm.example.com secret: s3cr3t device_name: android_l1 device_type: ANDROID system_id: 26830 security_level: 1 ``` ### `decryption` - **Type:** `str` or `dict`  ·  **Default:** `"shaka"` Selects the tool used to physically decrypt CENC content. The value is compared case-insensitively; `"mp4decrypt"` selects Bento4's mp4decrypt, and **anything else** (including `"shaka"`) selects shaka-packager. === "Global" ```yaml decryption: shaka ``` === "Per-service" ```yaml decryption: default: shaka SOMESERVICE: mp4decrypt ``` --- ## Network & proxy { #network-proxy } ### `network` - **Type:** `dict`  ·  **Default:** `{}` TLS-fingerprinting and HTTP client settings for the rnet-based session. | Sub-key | Type | Default | Description | |---------|------|---------|-------------| | `browser` | str | `"Chrome131"` | Impersonation preset. Must be an exact preset name (e.g. `Chrome131`, `Firefox135`, `Edge101`, `Safari18`, `OkHttp4_12`, `OkHttp5`, `Opera118`); unknown names raise an error. | | `http1_only` | bool | *(unset)* | Force HTTP/1.1. | | `http2_only` | bool | *(unset)* | Force HTTP/2. | | `pool_max_idle_per_host` | int | *(unset)* | Connection-pool tuning. | | `pool_max_size` | int | *(unset)* | Connection-pool tuning. | | `tcp_nodelay` | bool | *(unset)* | Disable Nagle's algorithm. | ```yaml network: browser: Firefox135 http2_only: true ``` !!! note "When to force `http1_only`" HTTP/2 is the right default on large CDNs, so leave it on. Force HTTP/1.1 only for a host that throttles per-connection, or a slow origin stuck behind HTTP/2 flow control. In benchmarks this won 30 to 50% on such hosts but **cost up to 27% on fast CDNs**, so enable it only when your own measurements justify it. !!! warning "Renamed from `curl_impersonate`" The old `curl_impersonate` section is a deprecated alias. If you still use it, unshackle honours it (only when `network` is absent) but emits a `DeprecationWarning`. Rename it to `network`. ### `headers` - **Type:** `dict`  ·  **Default:** `{}` Default HTTP headers merged into every session unshackle creates. ```yaml headers: Accept-Language: en-US,en;q=0.9 ``` !!! warning "Don't set `Accept-Encoding` (and similar) here" Keep this block to sane cross-service defaults such as `Accept-Language` and `User-Agent`. Compatibility headers like `Accept-Encoding` are set for you by the rnet HTTP backend as part of its browser-impersonation profile, and overriding them can break the impersonation fingerprint. Anything useful to only some services belongs in that service's config, not here. ### `proxy_providers` - **Type:** `dict`  ·  **Default:** `{}` Proxy/VPN provider configuration. Each sub-key names a provider, and its block is passed straight to that provider's constructor. See [Proxies & VPN](../guide/proxies-and-vpn.md) for the full provider guide. Recognised providers and their exit ports: | Provider | Config key | Credentials | Proxy scheme/port | |----------|-----------|-------------|-------------------| | Basic (static) | `basic` | country → URI(s) | as specified | | NordVPN | `nordvpn` | 48-char **service** credentials | `https://…:89` | | Surfshark | `surfsharkvpn` | 48-char **service** credentials | `https://…:443` | | Windscribe | `windscribevpn` | service credentials | `https://…:443` | | ExpressVPN | `expressvpn` | browser cookies / token cache | `https://cat:…@…:443` | | ProtonVPN | `protonvpn` | TV login or exported cookies | `https://…:4443` (or `:443` Secure Core) | | Gluetun | `gluetun` | per-VPN keys/creds | `http://localhost:{port}` (local Docker) | | Hola | *(none, auto)* | none | `http://…:{peer}` | ```yaml proxy_providers: basic: us: http://user:pass@1.2.3.4:8080 de: - http://a.example:8080 - socks5://b.example:1080 nordvpn: username: <48-char service username> password: <48-char service password> ``` !!! note "Provider loading differs between CLI and REST server" The `dl` CLI loads all providers, including `windscribevpn` and `gluetun`. The REST API / remote-client path uses a separate resolver that does **not** load `windscribevpn` or `gluetun`. ExpressVPN and ProtonVPN also auto-load when their cookie file exists, and Hola auto-loads whenever the `hola-proxy` binary is present. --- ## Key vaults { #key-vaults } The full guide is at [Vaults](../guide/vaults.md). Two keys configure them. ### `key_vaults` - **Type:** `list[dict]`  ·  **Default:** `[]` An ordered list of key-vault backends. unshackle queries them in order and reuses content keys instead of re-licensing. Each entry needs a `type` (the backend module name) and a `name`, plus backend-specific keys. | `type` | Purpose | Required keys | Notes | |--------|---------|--------------|-------| | `SQLite` | Local SQLite database | `name`, `path` | Loaded **critically**; a failure aborts the run. | | `MySQL` | Remote MySQL database | `name`, `host`, `database`, `username` | Extra keys (e.g. `password`, `port`) forwarded to pymysql. | | `API` | RESTful JSON API | `name`, `uri`, `token` | Honours `vault_timeout`. | | `HTTP` | HTTP API with modes | `name`, `host`, one of `password`/`api_key` | `api_mode`: `query` (default), `json`, `decrypt_labs`. | ```yaml key_vaults: - type: SQLite name: Local path: ~/.unshackle/keys.db - type: MySQL name: Team host: db.example.com database: keys username: unshackle password: hunter2 no_push: false ``` !!! tip "Common per-entry options" - `no_push: true` makes a vault read-only (keys are fetched but never written to it). - A vault of `type: api` whose `name` contains `decrypt_labs` auto-fills its `token` from [`decrypt_labs_api_key`](#external-api-keys) when not set inline. - An all-zero content key (32 zeros) is treated as "no key" everywhere and is never stored. ### `vault_timeout` - **Type:** `float`  ·  **Default:** `10.0` Timeout in seconds for vault operations. Injected automatically into any backend whose constructor accepts a `timeout` parameter (a per-vault `timeout` still wins). --- ## External API keys { #external-api-keys } All default to an empty string and enable optional metadata/geolocation features. | Key | Type | Default | Description | |-----|------|---------|-------------| | `tmdb_api_key` | str | `""` | TMDB API key for metadata enrichment and external-ID tags. | | `simkl_client_id` | str | `""` | SIMKL client ID for metadata lookups; an alternative/fallback source to TMDB. | | `decrypt_labs_api_key` | str | `""` | Global Decrypt Labs API key (used by remote CDM / vault). | | `ipinfo_api_key` | str | `""` | ipinfo.io API key for IP/region lookups. | !!! tip "SIMKL is a fallback for TMDB" SIMKL is positioned as an alternative metadata source, and it is most valuable precisely **when no `tmdb_api_key` is configured**. It improves title-matching and tagging reliability in that case rather than replacing TMDB where TMDB is available. !!! note "`ipinfo_api_key` never touches your service sessions" The token is only ever sent to `api.ipinfo.io` as a per-request `Authorization` header; it is **never** attached to your service session, so it cannot leak to a streaming provider. Lookups degrade gracefully through a fallback chain: the authenticated Lite endpoint (higher rate limits and richer fields) → anonymous ipinfo → `ip-api.in` as a last resort. --- ## Caching & updates { #caching-updates } ### Title cache unshackle caches fetched title metadata (region- and account-aware) to avoid repeat API calls. | Key | Type | Default | Description | |-----|------|---------|-------------| | `title_cache_enabled` | bool | `true` | Master switch for the title cache. | | `title_cache_time` | int (seconds) | `1800` (30 min) | Lifetime of fresh cached titles. | | `title_cache_max_retention` | int (seconds) | `86400` (24 h) | How long stale cached data may still be served as a fallback when a live fetch fails. | ### Update checks | Key | Type | Default | Description | |-----|------|---------|-------------| | `update_checks` | bool | `true` | Whether to check for new unshackle releases. | | `update_check_interval` | int (hours) | `24` | Minimum hours between update checks. | !!! note Update checks query the GitHub releases API with a fixed 5-second timeout and cache the result in `directories.cache/update_check.json`. --- ## Logging, privacy & debug { #logging-privacy-debug } | Key | Type | Default | Description | |-----|------|---------|-------------| | `redact_paths` | bool | `true` | Mask install-root / venv / home prefixes in logged paths (``, ``, `~`). Set `false` to show full paths. | | `debug` | bool | `false` | Global debug mode. | | `debug_keys` | bool | `false` | Log decryption keys. | | `debug_requests` | bool | `false` | Log HTTP requests. | | `set_terminal_bg` | bool | `false` | Append the theme's background colour to output styling. | !!! tip "When to enable `set_terminal_bg`" When on, the theme's background colour is appended to the foreground styles so the full colour palette renders correctly on terminals whose *default* background differs from unshackle's theme. Turn it on if the ASCII-art banner or coloured output **looks off** on your terminal. Otherwise leave it off. !!! danger "Key exposure" Decryption content keys are written to the structured debug log at INFO level during the DRM handshake. Treat `debug`, `debug_keys`, and the `unshackle_debug_*.jsonl` files as sensitive, and keep `redact_paths` enabled when sharing logs. `debug_keys` affects **only** content-encryption keys (the `content_key`/`key` fields). Passwords, tokens, cookies, and session tokens are **always** redacted regardless of this setting, and KIDs, key counts, and other metadata are always logged either way. --- ## Deprecated & removed keys { #deprecated-removed-keys } | Key | Status | Behaviour | |-----|--------|-----------| | `curl_impersonate` | **Deprecated** → use [`network`](#network-proxy) | Emits a `DeprecationWarning`; still honoured only if `network` is absent. | | `downloader` | **Deprecated** | Any value other than `"requests"` emits a `DeprecationWarning`; the value is otherwise ignored (the unified requests downloader is always used). | | `scene_naming` | **Removed** | If present at all, unshackle exits with an error directing you to configure [`output_template`](#output-naming) instead. | --- ## A complete annotated example The following config sets a handful of common keys. Every key not shown keeps its default. ```yaml title="unshackle.yaml" directories: downloads: ~/Media/unshackle temp: /mnt/scratch/unshackle network: browser: Chrome131 headers: Accept-Language: en-US,en;q=0.9 cdm: default: chromecdm_l3 NF: android_l1 decryption: shaka credentials: NF: user@example.com:hunter2 output_template: movies: "{title}.{year}.{quality?}.{source}-{tag}" series: "{title}.{season_episode}.{quality?}.{source}-{tag}" folder: movies: "{title} ({year})" series: "{title} ({year})" tag: MYGRP key_vaults: - type: SQLite name: Local path: ~/.unshackle/keys.db proxy_providers: basic: us: http://user:pass@1.2.3.4:8080 tmdb_api_key: "your-tmdb-key" title_cache_time: 3600 redact_paths: true ``` For how these interact at runtime, continue to [Downloading](../guide/downloading.md), [DRM & CDM](../guide/drm-and-cdm.md), and [Output & naming](../guide/output-and-naming.md).