Vultisig Share Decoder: From Inspector to Recovery Tool
How I rebuilt the Vultisig Share Decoder from a simple vault inspector into a full key recovery pipeline — adding DKLS support, a CLI, pure JS derivation, and 13+ blockchain networks.
Back in January I wrote about the Vultisig Share Decoder — a browser tool for inspecting .vult backup files from Vultisig multi-sig wallets. Since then, I’ve rebuilt it from the ground up. What was a simple share viewer is now a full private key recovery tool.
What changed
The original version could parse vault shares and show you what was inside. Useful for verification, but it couldn’t actually reconstruct your private keys. The new version does the full pipeline: parse shares, reconstruct the master key, and derive wallet addresses with importable private keys for 13+ blockchains.
Here’s what I added:
Full codebase restructure
The old codebase was a monolithic main.go with everything in one file. I broke it into a proper Go project structure:
internal/
vault/ # File parsing, protobuf, AES-GCM decryption
recovery/ # GG20 + DKLS key reconstruction
derive/ # BIP32/BIP44 address derivation per chain
format/ # Text and JSON output formatters
cmd/
cli/ # Native CLI binary
wasm/ # Go WASM entry point This made it possible to build multiple targets (CLI, WASM, web server) from the same codebase using Go build tags.
DKLS key recovery
Vultisig has two TSS schemes: the older GG20 (Shamir-based) and the newer DKLS. GG20 recovery was relatively straightforward — it’s well-documented secret sharing reconstruction using bnb-chain/tss-lib.
DKLS was the harder problem. The keyshares are opaque binary blobs, and reconstruction requires Vultisig’s proprietary Rust libraries. I integrated two separate Rust WASM modules for the browser — vs_wasm for ECDSA key export and vs_schnorr_wasm for EdDSA/Schnorr. For the CLI, I used CGo bindings to the same Rust libraries compiled natively via vultisig/go-wrappers.
A fun bug: DKLS vaults store two keyshares per file (one ECDSA, one EdDSA), and my detection logic was tripping over reshare prefixes in vault names. Small stuff, but the kind of thing that only surfaces with real test data.
Pure JS address derivation
Originally all the address derivation happened in Go WASM (~5.5MB gzipped). I replaced it with a pure JavaScript implementation using @noble/curves and @scure/bip32 — battle-tested crypto libraries that bundle to ~27KB. The Go WASM now only handles key reconstruction, and derivation is near-instant in JS.
CLI
Not everyone wants a browser. The CLI gives you the same recovery pipeline from the terminal:
make cli
./dist/cli recover --files "share1.vult" --files "share2.vult"
./dist/cli recover --files "share1.vult" --files "share2.vult" --format json Because the CLI compiles natively with CGo, it supports both GG20 and DKLS without any WASM overhead.
13+ supported chains
The tool now derives keys for:
- UTXO: Bitcoin, Bitcoin Cash, Dogecoin, Litecoin (WIF keys for Electrum import)
- Cosmos: THORChain, MayaChain, Cosmos Hub, Kujira, dYdX, Terra Classic, Terra
- EVM: Ethereum, Tron (hex keys for MetaMask import)
- EdDSA: Solana, Sui, TON
Adding a new chain is just a derivation function in internal/derive/ and a registration in coins.go.
The security constraint
Same as before: no key material leaves your machine. The web app is a static site on Cloudflare Pages with no backend. The CLI reads files from disk and prints to stdout. You can verify by checking the network tab — the only requests are loading static assets.
Try it at vultisig-share-decoder.pages.dev, or clone it and run the CLI locally.