coysh-digital / manager-restore
Offline tool for generating Manager recovery keys and decrypting Manager backup artifacts. Never contacts a server.
Requires
- php: >=8.2
- ext-json: *
- ext-sodium: *
- coysh-digital/manager-protocol: ^1.2
Requires (Dev)
- pestphp/pest: ^3.0
- phpstan/phpstan: ^2.0
This package is auto-updated.
Last update: 2026-08-01 12:00:49 UTC
README
The tool you run when everything else has gone wrong.
Manager encrypts your database backups to a key that only you hold. That is the whole point, and it has a consequence: Manager cannot open them, and neither can we. This is how you open them.
It works entirely offline. It opens no sockets, reads no configuration, and needs no Manager installation. If Manager has been gone for a year and all you have left is a file and a key, this still works — and there is a test that asserts the offline part rather than merely intending it.
Requirements
PHP 8.2 or newer with the sodium extension. That is the same thing your Craft site already runs on, so
if you can run Craft you can run this.
Install
composer global require coysh-digital/manager-restore
Or download the phar from the releases page and verify it before running it:
gpg --verify manager-restore.phar.asc manager-restore.phar
Verifying matters more here than usual. This is the one program that will ever hold your recovery private key, so obtaining it from somewhere other than the party you are protecting yourself against is the point.
The five minutes that matter
# 1. Create a recovery keypair. This happens on your machine. Manager never sees the secret half. manager-restore keygen --label="Ops laptop" --out=~/keys/acme-recovery # 2. Register the .pub contents in Manager, under Settings → Backups. # Manager gives you a challenge. Prove you hold the other half: manager-restore prove --key=~/keys/acme-recovery.secret --challenge=<paste> # 3. Pin the fingerprint on every Craft site, in config/manager-connector.php: # 'recoveryKeyFingerprints' => ['MGRK-…'], # # Do not skip this one. See "The step people skip" below. # 4. Later, when you need a backup back: manager-restore decrypt --key=~/keys/acme-recovery.secret \ --out=./restore.sql ./01JZX….artifact
Commands
| Command | What it does |
|---|---|
keygen --label=NAME [--out=PATH] |
Creates a recovery keypair. Writes PATH.pub and PATH.secret, both mode 0600, and refuses to overwrite either. |
fingerprint KEYFILE |
Recomputes a key file's fingerprint from the key material. --quiet prints just the fingerprint. |
prove --key=SECRET --challenge=B64 |
Answers an enrolment challenge. The answer is a hash of the challenge and is safe to send. |
inspect ARTIFACT [--json] |
Describes an artifact. No key needed — everything printed is metadata the file carries in the clear. |
verify --key=SECRET ARTIFACT |
Proves your key opens it, by reading the first chunk. Cheap enough to run on everything, monthly. |
decrypt --key=SECRET --out=PATH ARTIFACT |
Writes the database dump. --out=- pipes to stdout. |
audit --key=SECRET DIRECTORY |
Checks a folder of artifacts and exits non-zero if any is sealed to a key you did not authorise. |
Exit codes: 0 fine, 1 refused or failed, 2 used wrongly. A cron job running audit needs to tell
"you typed it wrong" from "a backup is sealed to somebody you do not know", so those are separate.
The step people skip
Pinning fingerprints in config/manager-connector.php is the step that carries the most weight, and it
is the one that looks most skippable.
Your Craft site is told which keys to encrypt to, and it is told by Manager. A Manager installation that had been compromised — or that was simply asked, under a legal instrument, to hand over your data — could send a key of its own, and your site would encrypt to it without anything looking wrong.
Pinning is what stops that. The fingerprints live in a file on your server, in your version control, and the connector refuses the whole backup if Manager offers a key that is not among them. It refuses rather than filtering, and it refuses before taking the dump, so a refused backup never writes a plaintext database to disk.
Manager cannot change that file. That is the entire idea.
Belt and braces, for sites that were never pinned or were pinned late:
# Once a month, over whatever you have downloaded. manager-restore audit --key=~/keys/acme-recovery.secret \ --also=MGRK-OTHER-KEY-YOU-AUTHORISED \ ~/backups
--also is passed explicitly rather than fetched from Manager, because Manager is the party being
audited and asking it what to expect would be circular.
What this tool can and cannot tell you
It can tell you that a file decrypts with your key, that its contents match the checksum recorded when the backup was taken, that it was signed by the site it claims, and exactly which keys can read it.
It cannot tell you that a backup restores. A file that decrypts is a file that decrypts. Whether the SQL inside it loads into a working database is a different question and the only way to answer it is to load it into one. Do that on a scratch host, on a schedule, and write down when you last did it.
verify reads the first chunk. That proves your key works and the artifact was not sealed to somebody
else, which is the failure worth catching cheaply. It does not prove the rest of the file is intact —
only decrypt reads all of it.
If you have lost the secret key
Then the backups encrypted to it are gone. Not "recoverable with difficulty" — gone.
Manager holds ciphertext, a wrapped key it cannot open, and metadata. There is no escrow copy, no support process, and no key we could be compelled to produce, because we never had one. That is the trade zero-knowledge makes, and it is a real cost rather than a technicality.
Two things reduce the chance of being in that position:
- Keep two recovery keys, stored differently. Manager seals every backup to every active key, so losing one costs nothing. The armoured secret is 44 characters of printable text — a copy on paper in a safe fails in a different way from a laptop, which is exactly what you want from a second copy.
- Re-prove them. A key nobody has touched in a year is a key nobody can find. Manager will prompt.
Design notes
No console framework, and one runtime dependency. This is the program that stands between you and a permanently unreadable backup, and it should be something you can read in an afternoon before deciding to trust it. Argument parsing for seven commands is a hundred lines. A hundred lines you can audit is worth more here than a thousand you will not.
No key generation in a browser. The obvious design is a button in Manager that generates the key
locally with WebCrypto and posts only the public half. It fails on two counts. WebCrypto cannot open a
libsodium sealed box — crypto_box_seal needs HSalsa20, XSalsa20-Poly1305 and BLAKE2b, none of which it
has — so without shipping a WASM bundle the browser would manufacture a key it could never use. And more
fundamentally: Manager serves that JavaScript. Against a compromised or compelled platform, which is the
adversary client-side encryption exists for, a page that promises to keep your private key is worth
nothing.
No restore into a site. Manager will not load a backup into a Craft install and this tool has no code
that could. Restoring a production database is destructive and irreversible; doing it safely needs a
confirmation flow, a defined behaviour when it fails half way, and a tested path back from that state.
None of that follows from being able to decrypt a file. You get a .sql file and you load it, on a host
you chose, with a command you typed.
Nothing is printed that should not be. Stack traces are suppressed — a trace from this tool would have a secret key or a plaintext chunk in a frame somewhere, and the place it would end up is a support email. Errors are a class name and a message. The challenge plaintext is never printed; the response is, because it is a hash and reveals nothing.
Development
composer install composer test # pest composer phpstan # level 8 composer check # both
The suite runs against the reference artifact committed in the protocol package, reached through
vendor/ rather than a sibling checkout — testing against local protocol source is how two
implementations drift apart without either suite noticing.
Licence
MIT. Unlike the rest of Manager, and deliberately: this is the tool that opens your data, and it should be the piece you are least restricted in inspecting, forking or reimplementing. If Coysh Digital stops existing, the format is documented in the protocol package and this is a working reference for it.