Files
unshackle/guide/vaults/index.html
T
2026-08-28 00:11:04 +00:00

2856 lines
81 KiB
HTML

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Movie, TV, and Music archival software. User guide and REST API reference">
<link rel="canonical" href="https://docs.unshackle.dev/guide/vaults/">
<link rel="prev" href="../proxies-and-vpn/">
<link rel="next" href="../output-and-naming/">
<link rel="icon" href="../../assets/favicon.svg">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.7">
<title>Key Vaults - unshackle</title>
<link rel="stylesheet" href="../../assets/stylesheets/main.ec1eaa64.min.css">
<link rel="stylesheet" href="../../assets/stylesheets/palette.ab4e12ef.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style>
<link rel="stylesheet" href="../../assets/_mkdocstrings.css">
<script>__md_scope=new URL("../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
</head>
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="black" data-md-color-accent="indigo">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#key-vaults" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../.." title="unshackle" class="md-header__button md-logo" aria-label="unshackle" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m800-274-80-80v-206H514l-80-80h166v-80q0-50-34.5-85T481-840q-50 0-84 34.5T363-720v9l-73-73q22-61 75-98.5T481-920q83 0 141 58.5T680-720v80h40q33 0 56.5 23.5T800-560v286Zm20 246-62-62q-11 5-20 7.5T720-80H240q-33 0-56.5-23.5T160-160v-400q0-25 14.5-46t37.5-30L28-820l56-56L876-84l-56 56ZM686-160 539-309q-11 11-25.5 17t-31.5 6q-33 0-56.5-23.5T402-366q0-17 6-31.5t17-25.5L286-560h-46v400h446ZM486-360Zm131-97Z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
unshackle
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Key Vaults
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="(prefers-color-scheme)" data-md-color-scheme="default" data-md-color-primary="black" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1M8 13h8v-2H8zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4a5 5 0 0 0 5-5 5 5 0 0 0-5-5"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="black" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5m0 8a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="black" data-md-color-accent="indigo" aria-label="Switch to system preference" type="radio" name="__palette" id="__palette_2">
<label class="md-header__button md-icon" title="Switch to system preference" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h10a5 5 0 0 0 5-5 5 5 0 0 0-5-5M7 15a3 3 0 0 1-3-3 3 3 0 0 1 3-3 3 3 0 0 1 3 3 3 3 0 0 1-3 3"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://github.com/unshackle-dl/unshackle" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
unshackle-dl/unshackle
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../.." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../../getting-started/installation/" class="md-tabs__link">
Getting Started
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../downloading/" class="md-tabs__link">
User Guide
</a>
</li>
<li class="md-tabs__item">
<a href="../../reference/configuration/" class="md-tabs__link">
Configuration
</a>
</li>
<li class="md-tabs__item">
<a href="../../dev/creating-a-service/" class="md-tabs__link">
Developer
</a>
</li>
<li class="md-tabs__item">
<a href="../../reference/api/service/" class="md-tabs__link">
API Reference
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../.." title="unshackle" class="md-nav__button md-logo" aria-label="unshackle" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="m800-274-80-80v-206H514l-80-80h166v-80q0-50-34.5-85T481-840q-50 0-84 34.5T363-720v9l-73-73q22-61 75-98.5T481-920q83 0 141 58.5T680-720v80h40q33 0 56.5 23.5T800-560v286Zm20 246-62-62q-11 5-20 7.5T720-80H240q-33 0-56.5-23.5T160-160v-400q0-25 14.5-46t37.5-30L28-820l56-56L876-84l-56 56ZM686-160 539-309q-11 11-25.5 17t-31.5 6q-33 0-56.5-23.5T402-366q0-17 6-31.5t17-25.5L286-560h-46v400h446ZM486-360Zm131-97Z"/></svg>
</a>
unshackle
</label>
<div class="md-nav__source">
<a href="https://github.com/unshackle-dl/unshackle" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
unshackle-dl/unshackle
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" >
<label class="md-nav__link" for="__nav_2" id="__nav_2_label" tabindex="0">
<span class="md-ellipsis">
Getting Started
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
Getting Started
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../getting-started/installation/" class="md-nav__link">
<span class="md-ellipsis">
Installation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../getting-started/quickstart/" class="md-nav__link">
<span class="md-ellipsis">
Quick Start
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../getting-started/configuration-file/" class="md-nav__link">
<span class="md-ellipsis">
The Configuration File
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_3" checked>
<label class="md-nav__link" for="__nav_3" id="__nav_3_label" tabindex="">
<span class="md-ellipsis">
User Guide
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_3_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_3">
<span class="md-nav__icon md-icon"></span>
User Guide
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../downloading/" class="md-nav__link">
<span class="md-ellipsis">
Downloading
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../cli-reference/" class="md-nav__link">
<span class="md-ellipsis">
CLI Reference
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../drm-and-cdm/" class="md-nav__link">
<span class="md-ellipsis">
DRM & CDM Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../proxies-and-vpn/" class="md-nav__link">
<span class="md-ellipsis">
Proxies & VPN
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Key Vaults
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Key Vaults
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#what-a-vault-stores" class="md-nav__link">
<span class="md-ellipsis">
What a vault stores
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#how-lookups-and-caching-work" class="md-nav__link">
<span class="md-ellipsis">
How lookups and caching work
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#configuring-vaults" class="md-nav__link">
<span class="md-ellipsis">
Configuring vaults
</span>
</a>
<nav class="md-nav" aria-label="Configuring vaults">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#vault_timeout" class="md-nav__link">
<span class="md-ellipsis">
vault_timeout
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#sqlite-backend" class="md-nav__link">
<span class="md-ellipsis">
SQLite backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#mysql-backend" class="md-nav__link">
<span class="md-ellipsis">
MySQL backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#http-backend" class="md-nav__link">
<span class="md-ellipsis">
HTTP backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#api-backend" class="md-nav__link">
<span class="md-ellipsis">
API backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#managing-vaults-with-unshackle-kv" class="md-nav__link">
<span class="md-ellipsis">
Managing vaults with unshackle kv
</span>
</a>
<nav class="md-nav" aria-label="Managing vaults with unshackle kv">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#kv-add-import-keys-from-a-file" class="md-nav__link">
<span class="md-ellipsis">
kv add: import keys from a file
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#kv-search-find-a-content-key-by-kid" class="md-nav__link">
<span class="md-ellipsis">
kv search: find a content key by KID
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#kv-copy-copy-keys-between-vaults" class="md-nav__link">
<span class="md-ellipsis">
kv copy: copy keys between vaults
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#kv-sync-mirror-vaults-both-ways" class="md-nav__link">
<span class="md-ellipsis">
kv sync: mirror vaults both ways
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#kv-prepare-pre-create-service-tables" class="md-nav__link">
<span class="md-ellipsis">
kv prepare: pre-create service tables
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#see-also" class="md-nav__link">
<span class="md-ellipsis">
See also
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="../output-and-naming/" class="md-nav__link">
<span class="md-ellipsis">
Output & Naming
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../subtitles/" class="md-nav__link">
<span class="md-ellipsis">
Subtitles
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../troubleshooting/" class="md-nav__link">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
<div class="md-nav__link md-nav__container">
<a href="../../reference/configuration/" class="md-nav__link ">
<span class="md-ellipsis">
Configuration
</span>
</a>
<label class="md-nav__link " for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
Configuration
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../reference/configuration/directories/" class="md-nav__link">
<span class="md-ellipsis">
Directories
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/services/" class="md-nav__link">
<span class="md-ellipsis">
Services & Authentication
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/download/" class="md-nav__link">
<span class="md-ellipsis">
Download & Processing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/output/" class="md-nav__link">
<span class="md-ellipsis">
Output & Naming
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/drm/" class="md-nav__link">
<span class="md-ellipsis">
DRM & CDM
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/network/" class="md-nav__link">
<span class="md-ellipsis">
Network & Proxy
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/vaults/" class="md-nav__link">
<span class="md-ellipsis">
Key Vaults
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/post-scripts/" class="md-nav__link">
<span class="md-ellipsis">
Post-Scripts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/misc/" class="md-nav__link">
<span class="md-ellipsis">
Other Settings
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/configuration/example/" class="md-nav__link">
<span class="md-ellipsis">
Annotated Example
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" >
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
<span class="md-ellipsis">
Developer
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Developer
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../dev/creating-a-service/" class="md-nav__link">
<span class="md-ellipsis">
Creating a Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../dev/service-architecture/" class="md-nav__link">
<span class="md-ellipsis">
Service Architecture
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5_3" >
<div class="md-nav__link md-nav__container">
<a href="../../dev/rest-api/" class="md-nav__link ">
<span class="md-ellipsis">
REST API
</span>
</a>
<label class="md-nav__link " for="__nav_5_3" id="__nav_5_3_label" tabindex="0">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_5_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_5_3">
<span class="md-nav__icon md-icon"></span>
REST API
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../dev/rest-api/quickstart/" class="md-nav__link">
<span class="md-ellipsis">
Quick Start
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../dev/rest-api/authentication/" class="md-nav__link">
<span class="md-ellipsis">
Authentication
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../dev/rest-api/endpoints/" class="md-nav__link">
<span class="md-ellipsis">
Endpoints
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../dev/rest-api/job-lifecycle/" class="md-nav__link">
<span class="md-ellipsis">
Job Lifecycle
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../dev/rest-api/remote-sessions/" class="md-nav__link">
<span class="md-ellipsis">
Remote Sessions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../dev/rest-api/errors/" class="md-nav__link">
<span class="md-ellipsis">
Error Responses
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_6" >
<label class="md-nav__link" for="__nav_6" id="__nav_6_label" tabindex="0">
<span class="md-ellipsis">
API Reference
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_6">
<span class="md-nav__icon md-icon"></span>
API Reference
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../reference/api/service/" class="md-nav__link">
<span class="md-ellipsis">
Service
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/api/config/" class="md-nav__link">
<span class="md-ellipsis">
Config
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/api/tracks/" class="md-nav__link">
<span class="md-ellipsis">
Tracks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/api/titles/" class="md-nav__link">
<span class="md-ellipsis">
Titles
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../reference/api/drm/" class="md-nav__link">
<span class="md-ellipsis">
DRM
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
Table of contents
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#what-a-vault-stores" class="md-nav__link">
<span class="md-ellipsis">
What a vault stores
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#how-lookups-and-caching-work" class="md-nav__link">
<span class="md-ellipsis">
How lookups and caching work
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#configuring-vaults" class="md-nav__link">
<span class="md-ellipsis">
Configuring vaults
</span>
</a>
<nav class="md-nav" aria-label="Configuring vaults">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#vault_timeout" class="md-nav__link">
<span class="md-ellipsis">
vault_timeout
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#sqlite-backend" class="md-nav__link">
<span class="md-ellipsis">
SQLite backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#mysql-backend" class="md-nav__link">
<span class="md-ellipsis">
MySQL backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#http-backend" class="md-nav__link">
<span class="md-ellipsis">
HTTP backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#api-backend" class="md-nav__link">
<span class="md-ellipsis">
API backend
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#managing-vaults-with-unshackle-kv" class="md-nav__link">
<span class="md-ellipsis">
Managing vaults with unshackle kv
</span>
</a>
<nav class="md-nav" aria-label="Managing vaults with unshackle kv">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#kv-add-import-keys-from-a-file" class="md-nav__link">
<span class="md-ellipsis">
kv add: import keys from a file
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#kv-search-find-a-content-key-by-kid" class="md-nav__link">
<span class="md-ellipsis">
kv search: find a content key by KID
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#kv-copy-copy-keys-between-vaults" class="md-nav__link">
<span class="md-ellipsis">
kv copy: copy keys between vaults
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#kv-sync-mirror-vaults-both-ways" class="md-nav__link">
<span class="md-ellipsis">
kv sync: mirror vaults both ways
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#kv-prepare-pre-create-service-tables" class="md-nav__link">
<span class="md-ellipsis">
kv prepare: pre-create service tables
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#see-also" class="md-nav__link">
<span class="md-ellipsis">
See also
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="key-vaults">Key Vaults<a class="headerlink" href="#key-vaults" title="Permanent link">&para;</a></h1>
<p>A <strong>key vault</strong> is a store of DRM <strong>content keys</strong> that unshackle remembers between downloads. Once unshackle has recovered a title's keys, it saves them to your configured vaults. The next time you download something that reuses those same keys, unshackle finds them in a vault instead of performing another license exchange. The download starts faster and puts less load on the service.</p>
<p>Vaults are entirely optional, but if you download regularly they are one of the biggest quality-of-life improvements you can configure.</p>
<div class="admonition tip">
<p class="admonition-title">Vaults save license calls even on the very first download</p>
<p>The benefit is not limited to repeat downloads. Within a single run, a service often issues the <em>same</em> Key ID and content key for both the video and audio tracks. The same pair frequently covers several resolutions or bitrates of the same title. Once unshackle has recovered a key it writes it to your vaults immediately. The moment the next track presents a KID it has already seen, the key comes from the vault instead of another license exchange. A single fetched key can therefore satisfy many tracks in one run.</p>
</div>
<h2 id="what-a-vault-stores">What a vault stores<a class="headerlink" href="#what-a-vault-stores" title="Permanent link">&para;</a></h2>
<p>A vault stores every content key as a <strong>KID → KEY</strong> pair, grouped by <strong>service</strong>:</p>
<ul>
<li><strong>KID</strong> (Key ID): a 16-byte identifier for a specific encrypted track, stored as 32 lowercase hex characters with no dashes.</li>
<li><strong>KEY</strong>: the 16-byte content key that decrypts that track, also 32 hex characters.</li>
<li><strong>Service</strong>: the canonical service tag (for example <code>EXAMPLE</code>, not <code>ExampleService</code>) that namespaces the key. Each service gets its own logical table.</li>
</ul>
<div class="admonition note">
<p class="admonition-title">Why keys are matched by KID</p>
<p>Vaults match keys by KID, never by PSSH/CENC header. A KID does not change unless the underlying video file itself changes, whereas the PSSH box can vary between requests for the very same content. That makes the KID a far more reliable match key.</p>
</div>
<div class="admonition info">
<p class="admonition-title">Null keys are ignored everywhere</p>
<p>A content key made entirely of zeroes (<code>00000000000000000000000000000000</code>) is treated as "no key". Vault lookups skip it, and every backend raises an error if you try to add one. This prevents a placeholder key from masking a real one.</p>
</div>
<h2 id="how-lookups-and-caching-work">How lookups and caching work<a class="headerlink" href="#how-lookups-and-caching-work" title="Permanent link">&para;</a></h2>
<p>When unshackle needs a content key during a download it asks your vaults <strong>in the order they appear in your config</strong>. It returns the first non-null key it finds and stops searching. If a vault raises a permission error or is unreachable, unshackle skips it and continues the search with the next one.</p>
<p>When unshackle recovers new keys from a license, it pushes them to <strong>all</strong> configured vaults, so every vault gradually converges on the same set of keys. Two rules apply to pushing:</p>
<ul>
<li>unshackle skips keys that already exist in a vault (it never overwrites or deletes existing data).</li>
<li>A vault marked <code>no_push: true</code> receives lookups, but unshackle never writes to it. This is useful for read-only or shared upstream vaults you do not own.</li>
</ul>
<div class="admonition tip">
<p class="admonition-title">A good two-vault setup</p>
<p>A common arrangement is a fast local <strong>SQLite</strong> vault plus a shared remote vault (<strong>MySQL</strong>, <strong>HTTP</strong>, or <strong>API</strong>). The local vault answers instantly and works offline; the remote vault lets you share keys across machines or with a group.</p>
</div>
<h2 id="configuring-vaults">Configuring vaults<a class="headerlink" href="#configuring-vaults" title="Permanent link">&para;</a></h2>
<p>Declare vaults under the <code>key_vaults</code> list in your unshackle config file. Each entry is a mapping with two required fields, <code>type</code> (which backend to use) and <code>name</code> (a label you choose), plus whatever settings that backend needs.</p>
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-0-1"><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="nt">key_vaults</span><span class="p">:</span>
</span><span id="__span-0-2"><a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">SQLite</span>
</span><span id="__span-0-3"><a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">local</span>
</span><span id="__span-0-4"><a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">~/.local/share/unshackle/vaults/local.db</span>
</span><span id="__span-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">MySQL</span>
</span><span id="__span-0-6"><a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">shared</span>
</span><span id="__span-0-7"><a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a><span class="w"> </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">vault.example.com</span>
</span><span id="__span-0-8"><a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a><span class="w"> </span><span class="nt">database</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">unshackle</span>
</span><span id="__span-0-9"><a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a><span class="w"> </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">reader</span>
</span><span id="__span-0-10"><a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a><span class="w"> </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="s">"s3cret"</span>
</span></code></pre></div>
<p>The <code>type</code> value is matched to a vault backend by name. The four backends that ship with unshackle are <code>SQLite</code>, <code>MySQL</code>, <code>HTTP</code>, and <code>API</code>, described below.</p>
<div class="admonition warning">
<p class="admonition-title">SQLite vaults are loaded critically</p>
<p>During a download, a <strong>SQLite</strong> vault that fails to load will <strong>abort the run</strong>. The assumption is that a local database you configured should always be available. <code>MySQL</code>, <code>HTTP</code>, and <code>API</code> vaults are loaded leniently: if one fails to load it is logged and skipped, and the download continues with the remaining vaults.</p>
</div>
<h3 id="vault_timeout"><code>vault_timeout</code><a class="headerlink" href="#vault_timeout" title="Permanent link">&para;</a></h3>
<p>Network-backed vaults (<code>HTTP</code> and <code>API</code>) accept an optional per-vault <code>timeout</code> in seconds. If you omit it, unshackle injects the global default:</p>
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a><span class="nt">vault_timeout</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">10.0</span><span class="w"> </span><span class="c1"># default, in seconds</span>
</span></code></pre></div>
<p>A <code>timeout</code> set inline on an individual vault always wins over this global value. The <code>SQLite</code> and <code>MySQL</code> backends do not take a <code>timeout</code> field.</p>
<hr />
<h2 id="sqlite-backend">SQLite backend<a class="headerlink" href="#sqlite-backend" title="Permanent link">&para;</a></h2>
<p>A locally-accessed SQLite database file. This is the simplest and fastest option and needs no server.</p>
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="nt">key_vaults</span><span class="p">:</span>
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">SQLite</span>
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">local</span>
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="w"> </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">~/.local/share/unshackle/vaults/local.db</span>
</span></code></pre></div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>type</code></td>
<td>yes</td>
<td>Must be <code>SQLite</code>.</td>
</tr>
<tr>
<td><code>name</code></td>
<td>yes</td>
<td>A label for this vault.</td>
</tr>
<tr>
<td><code>path</code></td>
<td>yes</td>
<td>Path to the database file. <code>~</code> is expanded. The file (and its tables) are created automatically on first use.</td>
</tr>
<tr>
<td><code>no_push</code></td>
<td>no</td>
<td>If <code>true</code>, keys are read but never written. Defaults to <code>false</code>.</td>
</tr>
</tbody>
</table>
<p>Each service gets its own table (named after the service tag), created on demand with <code>kid</code> and <code>key_</code> columns. Connections use WAL journaling with a 30-second busy timeout, so multiple threads can safely use the same database file.</p>
<div class="admonition warning">
<p class="admonition-title">Moving the .db file silently forks your vault</p>
<p>Because the file is created on demand, if you relocate an existing <code>.db</code> without updating <code>path</code> to match, unshackle will create a fresh empty database at the old location. That leaves you with two divergent vaults and no error to warn you. Always update the config path whenever you move the file.</p>
</div>
<div class="admonition tip">
<p class="admonition-title">Keep a local SQLite vault even when you share a MySQL one</p>
<p>The SQLite vault is best thought of as an offline-only backup should anything ever happen to a shared <code>MySQL</code> vault. Each member of a group should keep their own local SQLite vault alongside the shared database, so no one loses their keys if the shared vault goes down or away.</p>
</div>
<hr />
<h2 id="mysql-backend">MySQL backend<a class="headerlink" href="#mysql-backend" title="Permanent link">&para;</a></h2>
<p>A remotely-accessed MySQL/MariaDB database, ideal for sharing keys across several machines. It connects via <code>pymysql</code>. Prefer MariaDB over MySQL where you have the choice.</p>
<div class="admonition warning">
<p class="admonition-title">The vault connects directly to the database server</p>
<p>A <code>MySQL</code> vault (like <code>SQLite</code>) speaks the database wire protocol directly to the host or IP you configure. It cannot sit behind a PHP API or any other application layer. A common real-world snag: many hosting providers firewall their MySQL server to their own internal network. A database on shared hosting is therefore frequently unreachable from the outside world even when your credentials are perfectly correct. If connections time out, confirm the server accepts remote connections from your address before suspecting the config.</p>
</div>
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-3-1"><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="nt">key_vaults</span><span class="p">:</span>
</span><span id="__span-3-2"><a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">MySQL</span>
</span><span id="__span-3-3"><a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">shared</span>
</span><span id="__span-3-4"><a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a><span class="w"> </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">vault.example.com</span>
</span><span id="__span-3-5"><a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a><span class="w"> </span><span class="nt">database</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">unshackle</span>
</span><span id="__span-3-6"><a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="w"> </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">unshackle</span>
</span><span id="__span-3-7"><a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="w"> </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="s">"your-password"</span>
</span><span id="__span-3-8"><a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="w"> </span><span class="nt">port</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">3306</span>
</span></code></pre></div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>type</code></td>
<td>yes</td>
<td>Must be <code>MySQL</code>.</td>
</tr>
<tr>
<td><code>name</code></td>
<td>yes</td>
<td>A label for this vault.</td>
</tr>
<tr>
<td><code>host</code></td>
<td>yes</td>
<td>Database server hostname or IP.</td>
</tr>
<tr>
<td><code>database</code></td>
<td>yes</td>
<td>Database (schema) name.</td>
</tr>
<tr>
<td><code>username</code></td>
<td>yes</td>
<td>Login user.</td>
</tr>
<tr>
<td><code>no_push</code></td>
<td>no</td>
<td>If <code>true</code>, keys are read but never written.</td>
</tr>
</tbody>
</table>
<p>Any additional fields you provide (such as <code>password</code>, <code>port</code>, or <code>ssl</code>) are passed straight through to <code>pymysql.connect</code>, so you can use the full range of its connection options.</p>
<div class="admonition warning">
<p class="admonition-title">Account security is on you, not on unshackle</p>
<p>unshackle enforces none of this. It is operational policy for a database that may be shared with a group. Never connect with the <code>root</code> account, not even for yourself. Give every account a password, and never let two users share a username/password. Restrict each account's access to only the <code>unshackle</code> database, and grant normal users only <code>SELECT</code> and <code>INSERT</code>. Reserve the <code>CREATE</code> grant for a single trusted user who bootstraps the service tables (see <a href="#kv-prepare-pre-create-service-tables"><code>kv prepare</code></a>); everyone else can insert new keys but should not be creating tables.</p>
</div>
<div class="admonition note">
<p class="admonition-title">Grants determine what the vault can do</p>
<p>On connection the backend reads its own <code>SHOW GRANTS</code>. It requires at least <code>SELECT</code> to load at all (a missing <code>SELECT</code> grant makes the vault fail to load). Writing keys additionally needs <code>INSERT</code>, and creating a new service table needs <code>CREATE</code>. A read-only account still works fine as a lookup source. It cannot push new keys. Because <code>MySQL</code> is loaded leniently during downloads, a permission problem will not abort the run.</p>
</div>
<hr />
<h2 id="http-backend">HTTP backend<a class="headerlink" href="#http-backend" title="Permanent link">&para;</a></h2>
<p>A flexible HTTP vault that speaks one of three wire protocols, selected with <code>api_mode</code>. Use this to integrate with a community or self-hosted vault service.</p>
<table>
<thead>
<tr>
<th><code>api_mode</code></th>
<th>Transport</th>
<th>Authentication</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>query</code> (default)</td>
<td><code>GET</code> with query parameters</td>
<td><code>username</code> + <code>password</code></td>
<td>Full read and write.</td>
</tr>
<tr>
<td><code>json</code></td>
<td><code>POST</code> with a JSON payload</td>
<td><code>password</code> used as a token</td>
<td>Read and write per content key; cannot bulk-enumerate keys.</td>
</tr>
<tr>
<td><code>decrypt_labs</code></td>
<td><code>POST</code> in DecryptLabs format</td>
<td><code>api_key</code> sent as a header</td>
<td><strong>Read-only</strong>. <code>no_push</code> is forced on.</td>
</tr>
</tbody>
</table>
<div class="tabbed-set tabbed-alternate" data-tabs="1:3"><input checked="checked" id="__tabbed_1_1" name="__tabbed_1" type="radio" /><input id="__tabbed_1_2" name="__tabbed_1" type="radio" /><input id="__tabbed_1_3" name="__tabbed_1" type="radio" /><div class="tabbed-labels"><label for="__tabbed_1_1">query mode</label><label for="__tabbed_1_2">json mode</label><label for="__tabbed_1_3">decrypt_labs mode</label></div>
<div class="tabbed-content">
<div class="tabbed-block">
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-4-1"><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="nt">key_vaults</span><span class="p">:</span>
</span><span id="__span-4-2"><a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">HTTP</span>
</span><span id="__span-4-3"><a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">community</span>
</span><span id="__span-4-4"><a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="w"> </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">https://vault.example.com/api</span>
</span><span id="__span-4-5"><a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="w"> </span><span class="nt">api_mode</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">query</span>
</span><span id="__span-4-6"><a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a><span class="w"> </span><span class="nt">username</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">myuser</span>
</span><span id="__span-4-7"><a id="__codelineno-4-7" name="__codelineno-4-7" href="#__codelineno-4-7"></a><span class="w"> </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="s">"your-password"</span>
</span></code></pre></div>
<p>Query mode requires <code>username</code>. Both the username and password are sent as query parameters on each request.</p>
</div>
<div class="tabbed-block">
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-5-1"><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a><span class="nt">key_vaults</span><span class="p">:</span>
</span><span id="__span-5-2"><a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">HTTP</span>
</span><span id="__span-5-3"><a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">jsonvault</span>
</span><span id="__span-5-4"><a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a><span class="w"> </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">https://vault.example.com/rpc</span>
</span><span id="__span-5-5"><a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a><span class="w"> </span><span class="nt">api_mode</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">json</span>
</span><span id="__span-5-6"><a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a><span class="w"> </span><span class="nt">password</span><span class="p">:</span><span class="w"> </span><span class="s">"your-api-token"</span>
</span></code></pre></div>
<p>Requests are <code>POST</code>ed as <code>{"method": ..., "params": ..., "token": ...}</code> using methods like <code>GetKey</code>, <code>InsertKey</code>, and <code>GetServices</code>. Bulk writes (<code>kv copy</code>, <code>kv add</code>) first try <code>InsertKeys</code> with <code>params: {"keys": {kid: key, ...}, "service": ..., "title": ...}</code> (up to 500 content keys per call) and expect <code>{"inserted": &lt;count&gt;}</code> back. A server that answers HTTP 404 to <code>InsertKeys</code> gets one <code>InsertKey</code> call per content key for the rest of the run instead. This mode cannot list all keys in bulk, so a <code>kv copy</code> from a json vault relies on the remote server's own duplicate handling.</p>
</div>
<div class="tabbed-block">
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-6-1"><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="nt">key_vaults</span><span class="p">:</span>
</span><span id="__span-6-2"><a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">HTTP</span>
</span><span id="__span-6-3"><a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">decrypt_labs</span>
</span><span id="__span-6-4"><a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a><span class="w"> </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">https://api.decryptlabs.example/</span>
</span><span id="__span-6-5"><a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a><span class="w"> </span><span class="nt">api_mode</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">decrypt_labs</span>
</span><span id="__span-6-6"><a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a><span class="w"> </span><span class="nt">api_key</span><span class="p">:</span><span class="w"> </span><span class="s">"your-decrypt-labs-key"</span>
</span></code></pre></div>
<p>A read-only lookup source. The API key is sent in a <code>decrypt-labs-api-key</code> header and the vault refuses all writes.</p>
</div>
</div>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>type</code></td>
<td>yes</td>
<td>Must be <code>HTTP</code>.</td>
</tr>
<tr>
<td><code>name</code></td>
<td>yes</td>
<td>A label for this vault.</td>
</tr>
<tr>
<td><code>host</code></td>
<td>yes</td>
<td>Base URL of the vault API.</td>
</tr>
<tr>
<td><code>api_mode</code></td>
<td>no</td>
<td><code>query</code> (default), <code>json</code>, or <code>decrypt_labs</code>.</td>
</tr>
<tr>
<td><code>username</code></td>
<td>for <code>query</code></td>
<td>Required in query mode; ignored otherwise.</td>
</tr>
<tr>
<td><code>password</code></td>
<td>one of these</td>
<td>Password (query mode) or token (json mode).</td>
</tr>
<tr>
<td><code>api_key</code></td>
<td>one of these</td>
<td>Alternative to <code>password</code>; used for <code>decrypt_labs</code>. If you give both, <code>api_key</code> wins.</td>
</tr>
<tr>
<td><code>no_push</code></td>
<td>no</td>
<td>Read-only when <code>true</code> (always <code>true</code> for <code>decrypt_labs</code>).</td>
</tr>
<tr>
<td><code>timeout</code></td>
<td>no</td>
<td>Request timeout in seconds; defaults to <code>vault_timeout</code>.</td>
</tr>
</tbody>
</table>
<p>You must supply either <code>password</code> or <code>api_key</code>, or the vault will fail to load.</p>
<hr />
<h2 id="api-backend">API backend<a class="headerlink" href="#api-backend" title="Permanent link">&para;</a></h2>
<p>A simple RESTful HTTP vault with a fixed, well-defined protocol. Authentication is a bearer token.</p>
<div class="language-yaml highlight"><span class="filename">unshackle.yaml</span><pre><span></span><code><span id="__span-7-1"><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a><span class="nt">key_vaults</span><span class="p">:</span>
</span><span id="__span-7-2"><a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">API</span>
</span><span id="__span-7-3"><a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">myapi</span>
</span><span id="__span-7-4"><a id="__codelineno-7-4" name="__codelineno-7-4" href="#__codelineno-7-4"></a><span class="w"> </span><span class="nt">uri</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">https://vault.example.com/api</span>
</span><span id="__span-7-5"><a id="__codelineno-7-5" name="__codelineno-7-5" href="#__codelineno-7-5"></a><span class="w"> </span><span class="nt">token</span><span class="p">:</span><span class="w"> </span><span class="s">"your-bearer-token"</span>
</span><span id="__span-7-6"><a id="__codelineno-7-6" name="__codelineno-7-6" href="#__codelineno-7-6"></a><span class="w"> </span><span class="nt">headers</span><span class="p">:</span>
</span><span id="__span-7-7"><a id="__codelineno-7-7" name="__codelineno-7-7" href="#__codelineno-7-7"></a><span class="w"> </span><span class="nt">CF-Access-Client-Id</span><span class="p">:</span><span class="w"> </span><span class="s">"CLIENT_ID_HERE"</span>
</span><span id="__span-7-8"><a id="__codelineno-7-8" name="__codelineno-7-8" href="#__codelineno-7-8"></a><span class="w"> </span><span class="nt">CF-Access-Client-Secret</span><span class="p">:</span><span class="w"> </span><span class="s">"CLIENT_SECRET_HERE"</span>
</span></code></pre></div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Required</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>type</code></td>
<td>yes</td>
<td>Must be <code>API</code>.</td>
</tr>
<tr>
<td><code>name</code></td>
<td>yes</td>
<td>A label for this vault.</td>
</tr>
<tr>
<td><code>uri</code></td>
<td>yes</td>
<td>Base URI of the vault API (a trailing slash is trimmed).</td>
</tr>
<tr>
<td><code>token</code></td>
<td>yes</td>
<td>Bearer token, sent as <code>Authorization: Bearer &lt;token&gt;</code>.</td>
</tr>
<tr>
<td><code>no_push</code></td>
<td>no</td>
<td>If <code>true</code>, keys are read but never written.</td>
</tr>
<tr>
<td><code>timeout</code></td>
<td>no</td>
<td>Request timeout in seconds; defaults to <code>vault_timeout</code>.</td>
</tr>
<tr>
<td><code>headers</code></td>
<td>no</td>
<td>Extra HTTP <code>headers</code> sent with every request, for example the Cloudflare Access service-token headers <code>CF-Access-Client-Id</code> and <code>CF-Access-Client-Secret</code>.</td>
</tr>
</tbody>
</table>
<p>The backend talks to endpoints under <code>uri</code> (for example <code>GET {uri}/{service}/{kid}</code> to look up a single key). It interprets a numeric <code>code</code> field in each JSON response to detect errors such as an invalid token, rate limiting, or an invalid service tag.</p>
<div class="admonition warning">
<p class="admonition-title">This is unshackle's own protocol, not a generic vault client</p>
<p>The <code>API</code> backend expects unshackle's specific HTTP request and response shape. An API or HTTP key-vault endpoint built for a different project or service will generally <em>not</em> be compatible. You cannot point this at an arbitrary third-party vault API and expect it to work. To bridge another service, put an adapter in front of it that speaks this format, or use the <code>HTTP</code> backend if one of its wire protocols matches. When pushing many keys at once it batches requests and automatically shrinks the batch size if the server rejects an over-large request.</p>
</div>
<div class="admonition tip">
<p class="admonition-title">DecryptLabs shortcut</p>
<p>An <code>API</code> vault whose <code>name</code> contains <code>decrypt_labs</code> will have its <code>token</code> filled in automatically from a global <code>decrypt_labs_api_key</code> in your config if you leave <code>token</code> unset. This lets you keep the API key in one place.</p>
</div>
<hr />
<h2 id="managing-vaults-with-unshackle-kv">Managing vaults with <code>unshackle kv</code><a class="headerlink" href="#managing-vaults-with-unshackle-kv" title="Permanent link">&para;</a></h2>
<p>The <code>kv</code> command group manages the contents of your configured vaults. Every subcommand refers to vaults <strong>by the <code>name</code> you gave them</strong> in <code>key_vaults</code>; an unknown name is an error.</p>
<div class="language-console highlight"><pre><span></span><code><span id="__span-8-1"><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="gp">$ </span>unshackle<span class="w"> </span>kv<span class="w"> </span>--help
</span></code></pre></div>
<h3 id="kv-add-import-keys-from-a-file"><code>kv add</code>: import keys from a file<a class="headerlink" href="#kv-add-import-keys-from-a-file" title="Permanent link">&para;</a></h3>
<p>Add <code>KID:KEY</code> pairs to one or more vaults for a given service. The file must contain one pair per line as <code>HEX:HEX</code> (32 hex characters each); lines that do not match are ignored.</p>
<div class="language-console highlight"><pre><span></span><code><span id="__span-9-1"><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="gp">$ </span>unshackle<span class="w"> </span>kv<span class="w"> </span>add<span class="w"> </span>keys.txt<span class="w"> </span>EXAMPLE<span class="w"> </span><span class="nb">local</span><span class="w"> </span>shared
</span></code></pre></div>
<div class="language-text highlight"><span class="filename">keys.txt</span><pre><span></span><code><span id="__span-10-1"><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a>9a04f07998404286ab92e65be0885f95:b1c2d3e4f5a6978012345678deadbeef
</span><span id="__span-10-2"><a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>0e7cc1a4e2f9411db0f24a3fbb8f8b21:00112233445566778899aabbccddeeff
</span></code></pre></div>
<p>The service argument (<code>EXAMPLE</code> above) is normalized to its canonical tag automatically. Keys that already exist in a vault are reported as skipped.</p>
<h3 id="kv-search-find-a-content-key-by-kid"><code>kv search</code>: find a content key by KID<a class="headerlink" href="#kv-search-find-a-content-key-by-kid" title="Permanent link">&para;</a></h3>
<p>Search your vaults for a KID and print any matching content key.</p>
<div class="language-console highlight"><pre><span></span><code><span id="__span-11-1"><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="gp">$ </span>unshackle<span class="w"> </span>kv<span class="w"> </span>search<span class="w"> </span>9a04f07998404286ab92e65be0885f95
</span></code></pre></div>
<p>The KID must be 32 hex characters (dashes are stripped for you). By default the search scans every configured vault and every service table; narrow it with options:</p>
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-s</code>, <code>--service</code></td>
<td>Limit the search to a single service tag.</td>
</tr>
<tr>
<td><code>-v</code>, <code>--vault</code></td>
<td>Limit the search to one configured vault by name.</td>
</tr>
</tbody>
</table>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Remote vaults that cannot enumerate their service tables (for example an <code>HTTP</code> vault in <code>json</code> or <code>decrypt_labs</code> mode) can only be searched when you also pass <code>--service</code>. Without it, they are skipped.</p>
</div>
<h3 id="kv-copy-copy-keys-between-vaults"><code>kv copy</code>: copy keys between vaults<a class="headerlink" href="#kv-copy-copy-keys-between-vaults" title="Permanent link">&para;</a></h3>
<p>Copy every content key from one or more source vaults into a single destination vault. <code>kv copy</code> never alters existing rows; it adds only genuinely new KIDs and skips null keys.</p>
<div class="language-console highlight"><pre><span></span><code><span id="__span-12-1"><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="gp">$ </span>unshackle<span class="w"> </span>kv<span class="w"> </span>copy<span class="w"> </span>shared<span class="w"> </span><span class="nb">local</span>
</span></code></pre></div>
<p>The first argument is the destination; the rest are sources. You can supply several sources at once:</p>
<div class="language-console highlight"><pre><span></span><code><span id="__span-13-1"><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a><span class="gp">$ </span>unshackle<span class="w"> </span>kv<span class="w"> </span>copy<span class="w"> </span><span class="nb">local</span><span class="w"> </span>shared<span class="w"> </span>community
</span></code></pre></div>
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-s</code>, <code>--service</code></td>
<td>Only copy the given service.</td>
</tr>
<tr>
<td><code>-l</code>, <code>--local-only</code></td>
<td>Only copy services you have installed locally, skipping tables for unknown services.</td>
</tr>
</tbody>
</table>
<p><code>--service</code> and <code>--local-only</code> are mutually exclusive.</p>
<h3 id="kv-sync-mirror-vaults-both-ways"><code>kv sync</code>: mirror vaults both ways<a class="headerlink" href="#kv-sync-mirror-vaults-both-ways" title="Permanent link">&para;</a></h3>
<p>Make each of two or more vaults hold every content key the others have. This is effectively a two-way <code>copy</code> between each pair, so afterwards all listed vaults hold the same set of keys.</p>
<div class="language-console highlight"><pre><span></span><code><span id="__span-14-1"><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="gp">$ </span>unshackle<span class="w"> </span>kv<span class="w"> </span>sync<span class="w"> </span><span class="nb">local</span><span class="w"> </span>shared
</span></code></pre></div>
<p>It accepts the same <code>--service</code> and <code>--local-only</code> options as <code>copy</code>, and requires at least two vaults.</p>
<h3 id="kv-prepare-pre-create-service-tables"><code>kv prepare</code>: pre-create service tables<a class="headerlink" href="#kv-prepare-pre-create-service-tables" title="Permanent link">&para;</a></h3>
<p>Create the per-service tables on table-based vaults (<code>SQLite</code>, <code>MySQL</code>) ahead of time, for every service you have installed. Vaults that do not use tables (the network-backed <code>HTTP</code> and <code>API</code> backends) are skipped.</p>
<div class="language-console highlight"><pre><span></span><code><span id="__span-15-1"><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="gp">$ </span>unshackle<span class="w"> </span>kv<span class="w"> </span>prepare<span class="w"> </span>shared
</span></code></pre></div>
<p>This is mainly useful with a <code>MySQL</code> vault where you want the tables created once by an account that has the <code>CREATE</code> grant.</p>
<hr />
<h2 id="see-also">See also<a class="headerlink" href="#see-also" title="Permanent link">&para;</a></h2>
<ul>
<li><a href="../downloading/">Downloading</a>: how keys are recovered and used during a download.</li>
</ul>
</article>
</div>
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../proxies-and-vpn/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Proxies &amp; VPN">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
Proxies & VPN
</div>
</div>
</a>
<a href="../output-and-naming/" class="md-footer__link md-footer__link--next" aria-label="Next: Output &amp; Naming">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Output & Naming
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
Made with
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
Material for MkDocs
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../..", "features": ["content.code.annotate", "content.code.copy", "content.tabs.link", "content.tooltips", "navigation.footer", "navigation.indexes", "navigation.sections", "navigation.tabs", "navigation.top", "navigation.tracking", "search.highlight", "search.share", "search.suggest", "toc.follow"], "search": "../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../assets/javascripts/bundle.d7400e89.min.js"></script>
</body>
</html>