A small Rust + Svelte service that encrypts a Bitcoin multisig descriptor
using any cosigner xpub, producing a .bed file (binary, ASCII-armored, or
QR PNG) that can only be decrypted by someone holding that same xpub.
Implements the draft BIP "Bitcoin Encrypted Backup" (PR
bitcoin/bips#1951) and is
interoperable with Liana wallet v13+ via
the bitcoin-encrypted-backup
crate v0.0.2.
- Install on StartOS as the BED s9pk.
- Open the Tor onion or LAN
.localURL the StartOS dashboard provides. - Paste your descriptor → download the
.bed(or armored block, or QR). - Distribute one
.bedcopy per cosigner location.
Golden rule: The cardinal rule of BED — never co-locate a
.bedfile and a cosigner xpub of the same multisig. If an attacker finds both, they can decrypt the descriptor and learn your full wallet structure. See Threat Model for the complete model.
- Open the Cifrar tab.
- Paste a multisig descriptor that uses the multipath wildcard
<0;1>/*. Descriptors without this wildcard are rejected — see Common Pitfalls. - Click Cifrar. The page returns three outputs simultaneously:
- A binary
.bedfile (download). - An ASCII-armored block with
-----BEGIN BITCOIN ENCRYPTED BACKUP-----headers, copyable to clipboard. - A QR PNG of the armored payload (download), if the payload fits within QR ECC-L capacity (~2,900 bytes).
- A binary
- Optionally enable the history toggle. With history enabled the
.bedis also saved to/data/encrypted/<YYYYMMDDTHHMMSSZ>-<8hex>.bedfor later listing and deletion. Default is OFF — the cleartext descriptor and the.bedare returned to the browser and immediately forgotten.
- Open the Descifrar tab.
- Either paste the armored block or upload the binary
.bedfile. - Either paste the cosigner xpub (bare, e.g.
xpub6...) or upload a file containing it. - Click Descifrar. The recovered descriptor appears with a Copy-to-clipboard button. The descriptor is never persisted on disk.
Beyond a bare descriptor string, BED also encrypts and decrypts two wallet-export formats round-trip byte-identical:
- Liana JSON. Paste or load a Liana wallet export (the JSON file
with the nested descriptor and metadata). BED detects it by the
leading
{, encrypts the JSON in full, and on decrypt returns the original JSON intact. Useful when you want a single.bedto reconstruct a Liana wallet (descriptor + metadata) instead of just the descriptor. - Sparrow Wallet BIP-329 JSONL. Paste or load a Sparrow labels
export (multi-line JSONL where each line is
{type, ref, label}per BIP-329). BED detects it by the leading{on the first line, encrypts the JSONL in full, and on decrypt returns the original labels intact — byte for byte, including line endings. The decrypt panel visually distinguishes Sparrow exports from classic / Liana payloads so you know which file to re-import where.
Both formats coexist with the classic descriptor flow. The encrypt
file picker accepts .txt, .descriptor, .json, and .jsonl
(drag-and-drop also supported).
Workflow example. Recover a multisig wallet from cold storage:
recreate the wallet in Liana from the decrypted JSON (descriptor +
metadata in one file), then import the decrypted Sparrow JSONL into
Sparrow if you also keep label history. Two .bed files cover both
the wallet structure and the label history.
- Descriptor privacy. A
.bedfile reveals nothing about the wallet's structure, xpubs, derivation paths, or spending policy to an attacker who only has the file. AES-256-GCM authenticated encryption guarantees that any tampering is detected on decryption. - xpub distribution. Each cosigner can safely store a
.bedbackup without exposing the full wallet policy — they only need their own xpub to decrypt it. This enables redundant distribution of the descriptor (the only piece needed to fully recover a multisig wallet) without coupling that distribution to the privacy of the policy. - Cleartext-on-disk leakage. The cleartext descriptor never touches
disk. It is wrapped in
secrecy::SecretStringfrom parse through encryption, zeroized after the operation, and excluded from logs by an explicit tracing skip-all guard. A CI test grep-asserts the descriptor string does not appear in any log output.
Golden rule (repeated): The security model requires that you never co-locate a
.bedfile with any cosigner xpub of the same multisig. If an attacker finds both, they can decrypt the.bedand learn the full wallet structure. Each.bedcopy must live in a different physical location than the xpub needed to decrypt it.
- Compromise of the StartOS device during an active encrypt session. The descriptor passes through the device's memory in cleartext during encryption. If the device is compromised at that moment (malicious app, physical access, kernel-level attacker) the descriptor may be exposed. BED cannot protect against an attacker who controls the execution environment.
- Loss of all cosigner xpubs simultaneously. If every xpub needed to
decrypt is lost or destroyed, the
.bedbecomes undecryptable. BED provides redundancy for distribution, not a substitute for independent xpub backups. - An attacker who already holds one cosigner xpub. A
.bedencrypted with xpub A can be decrypted by anyone holding xpub A. The model is sound only when each co-location (.bed+xpub_N) is in a different physical location. - Side-channel and traffic analysis. BED runs locally on StartOS over Tor or LAN. It does not defend against advanced traffic-analysis adversaries who already control the local network.
- Hardware wallet support. USB device support is intentionally omitted (StartOS 0.4.0 does not pass USB into containers). Use a software wallet to extract the descriptor; sign with hardware after.
- The StartOS device is trusted during the session.
- Each
.bedcopy lives in a different physical location than any xpub that could decrypt it. - The
.bedfile format integrity is guaranteed by AES-256-GCM authentication — any tampering is detected. - The cosigner xpub used to encrypt is not also the only one that
decrypts; in a 2-of-3 multisig you typically encrypt one
.bedper cosigner using a different cosigner's xpub each time.
| Property | Value |
|---|---|
| Encryption | AES-256-GCM |
| Magic header | BEB (binary identifier) |
| KDF | Per BIP draft (xpub-derived; see PR for details) |
| Spec | bitcoin/bips PR #1951 — draft |
| Implementation | bitcoin-encrypted-backup crate, pinned at v0.0.2 (rev cd7ee382bf5ca0798d4f81697e2f9efb5e32fe40) |
| Interop | Liana wallet v13+ reads .bed files produced here, and BED reads .bed files produced by Liana |
| Descriptor requirement | Must use the BIP-380 multipath wildcard <0;1>/*. Without it, spending from address 0 exposes the xpub on-chain and breaks the encryption model. |
The .bed format is an external contract: BED will not break it
without a new milestone. If a future BIP revision changes the format
(e.g. ChaCha20-Poly1305 instead of AES-GCM), the old version is
archived in CHANGELOG and a separate "BED v0.x archive" branch is kept
available for decrypting legacy files.
- Descriptor without
<0;1>/*. BED rejects single-path descriptors with a typed validation error. Convert your descriptor to multipath form before encrypting. - xpub vs descriptor-style format. The Decrypt tab expects a bare
xpub (e.g.
xpub6FHa3...). Descriptor-style xpubs with a[fingerprint/path]prefix or a/*suffix are rejected. Strip the prefix and suffix before pasting. - QR size limit (~2,900 bytes ECC-L). Multisig descriptors with five or more cosigners may produce armored payloads that exceed the QR ECC-L capacity. Since v0.3.0 the QR panel degrades cleanly to "QR not available — payload too large"; binary and ASCII-armored downloads remain available for those cases. Use them with cold-storage paper for QR-bound transport.
- History mode default is OFF. The history toggle is opt-in. If
you cifrar without enabling it, the
.bedis returned to the browser and the server forgets it immediately. Enable the toggle explicitly if you want the file persisted to/data/encrypted/.
- BIP draft PR bitcoin/bips#1951
- Crate
bitcoin-encrypted-backup(tagv0.0.2) - Delving Bitcoin thread
- Liana wallet documentation
- StartOS s9pk wrapper:
semillabitcoin/bed-startos
MIT. See LICENSE.