Quorum generates an age key on your device and splits it into k-of-n shares, printed as word lists with QR codes. This page cannot make network requests, so you can disconnect before you start.
The browser's own random generator is already secure. Anything you add here is hashed together with it, so it can only add randomness, never weaken the key. It guards against a faulty generator. It is not stored and never goes into the address.
Title, note, holders and k/n are kept in the page address, so you can bookmark this setup. The key never goes into the address.
To get a PDF, choose "Save as PDF" in the print dialog. Each share prints on its own A4 page. Encrypt to the public key with age -r age1… file > file.age. Download the private key only if you need a working copy, because the paper shares are the backup.
Hold the share sheets up to the camera one at a time. When enough shares have been read, the key appears. Camera frames stay in this page and are never stored or sent anywhere.
Decrypt with age -d -i age-private-key.txt file.age.
The shares are standard SLIP-39 (Shamir's Secret-Sharing for Mnemonic Codes, the scheme used by Trezor wallets), so other tools can recover them as well. Quorum uses one group, no passphrase and iteration exponent 1. Recovery works like this:
AGE-SECRET-KEY- and written in uppercase, they form the key file line. X25519 with the base point gives the public key, Bech32-encoded with the prefix age.In Python, with the reference implementation from Trezor:
pip install shamir-mnemonic bech32 cryptography
# recover.py — paste the shares (one per line), then Ctrl-D
import sys, shamir_mnemonic, bech32
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
shares = [line.strip() for line in sys.stdin if line.strip()]
secret = shamir_mnemonic.combine_mnemonics(shares) # 32 bytes
def b32(hrp, data):
return bech32.bech32_encode(hrp, bech32.convertbits(data, 8, 5))
pub = X25519PrivateKey.from_private_bytes(secret).public_key()
print("# public key:", b32("age", pub.public_bytes(Encoding.Raw, PublicFormat.Raw)))
print(b32("age-secret-key-", secret).upper())Run python3 recover.py > age-private-key.txt. The shamir recover command from the same package also works interactively and prints the secret as hex. Specification: SLIP-0039, age v1.