@papervault/core
v0.1.1
Published
Crypto + Shamir Secret Sharing + printable HTML page generation for PaperVault disaster-recovery kits. Produces vaults that unlock at papervault.xyz.
Downloads
347
Maintainers
Readme
PaperVault Core — Crypto + Shamir + page generation library
PaperVault 📄🔐 is a free open source tool for creating offline paper-based data vaults for your foundational secrets, such as passwords, 2FA recovery codes, digital asset keys, hard drive encryption keys, and other critical data.
This is the core library — the AES-256-GCM crypto, Shamir Secret Sharing, and printable HTML page generation that powers @papervault/cli and @papervault/mcp. The browser app lives at papervault.xyz.
If you just want to back up secrets to paper, install @papervault/cli instead. This package is the building block — useful if you want to embed kit generation in your own tool.
🔐 Overview
@papervault/core is a pure-JavaScript library (no native deps) that:
- Encrypts plaintext with a fresh AES-256-GCM key (via Web Crypto API)
- Splits the key into N shares using Shamir's Secret Sharing over GF(2^8), recoverable with any M of N
- Generates standalone printable HTML pages: one vault page with the ciphertext QR and N key pages with one share QR each
- Produces the v2 vault format — byte-identical to vaults produced at papervault.xyz, so kits unlock at papervault.xyz/unlock
🚀 Quick Start
npm install @papervault/coreimport { createKit } from '@papervault/core';
const kit = await createKit({
secrets: [
{ kind: 'password', service: 'GitHub', username: 'alice', password: 's3cret' },
{ kind: 'wallet', name: 'BTC cold', seed: 'abandon abandon ... about' },
],
vaultName: 'My DR Kit',
threshold: 2,
shares: 3,
});
// kit.pages = [
// { kind: 'vault', html: '...', filename: 'vault', ... },
// { kind: 'key', html: '...', filename: 'key-1-atom-river', seq: 'Key 1', alias: 'atom-river', ... },
// ...
// ]
// kit.keyShares, kit.cipherKeyHex, kit.cipherTextHex, kit.cipherIvHex available for round-trip / verify.Requires Node.js ≥ 24 (needs global crypto and crypto.subtle).
🔑 Public API
| Export | Purpose |
|---|---|
| createKit(opts) | High-level: compress → encrypt → split → generate HTML. Returns the full Kit. |
| compress(secrets, freeText?) | Convert structured entries into the web-app's ultra-compact JSON. Byte-identical to what papervault.xyz produces. |
| countUserContent(secrets, freeText?) | Character-count for the 300-char limit (matches the web app's getUserContentLength). |
| encrypt(plaintext) | Fresh-key AES-256-GCM. Returns {cipherText, cipherKey, cipherIV} (all hex). |
| decrypt(cipherTextHex, keyHex, ivHex) | Inverse. |
| splitKey(secretKeyHex, n, t) | Shamir split. 32-byte hex key in, array of hex shares out. |
| combineShares(shares) | Inverse — give it ≥ threshold shares, get the original key back. |
| generateKeyAliases(n) | Two-word BIP-39 aliases (e.g. atom-river). |
| generateVaultId() | 6-char public hex ID. |
| generateVaultPage(opts) / buildVaultBody(opts) | Standalone vault HTML, or just its inner body fragment. |
| generateKeyPage(opts) / buildKeyBody(opts) | Same for key pages. |
| generatePrintAllPage(opts) | Single doc concatenating every kit page with CSS page breaks — one print dialog for the whole packet. |
| generateSelectorPage(opts) | Orchestration page used by the CLI's ephemeral print server. |
| LIMITS | {MAX_KEYS: 20, MAX_STORAGE: 300, MAX_QR_PAYLOAD_BYTES: 102400} |
| VAULT_VERSION | '2' |
| VAULT_IDENT | 'papervault.xyz' — sentinel in vault QR payloads. |
📄 Vault format
v2 vaults use:
- AES-256-GCM for encryption (Web Crypto API)
- Shamir Secret Sharing over GF(2^8) for key splitting (shamir-secret-sharing)
- Random 12-byte nonce per encryption, generated by
crypto.getRandomValues() - Level-M QR codes for ~15% damage tolerance
The QR payload shapes match the web app exactly:
// Vault QR (single-QR form; splits to metadata + data QRs if > 420 bytes)
{ id: 1, vault: 'papervault.xyz', version: '2', name, shares, threshold,
cipherIV, keys: [...], data: cipherText }
// Key QR
{ ident: keyAlias, key: keyShare }V1 vault decryption (legacy CryptoJS / AES-CTR / secrets.js) lives in the web app, not here — this package is v2-only by design.
🛡️ Security Notes
- All randomness via
crypto.getRandomValues(). NeverMath.random(). crypto.subtlefor AES-GCM — no JS reimplementation of the cipher.- No persistent state. The library never writes anywhere.
- The returned
Kitobject holds the raw AES key + Shamir shares so the caller can audit / round-trip. Treat it like a secret and drop the reference when done.
For the full PaperVault threat model, see the main app's README and SECURITY.md.
📦 Limits
- Maximum Keys: 20 (Shamir library constraint)
- Storage Limit: 300 characters of user content per vault (QR optimization — matches the web app)
createKit enforces both and throws clear errors.
🔗 Related Packages
@papervault/cli— Command-line front-end (most users want this)@papervault/mcp— MCP server for AI agents@papervault/init—npxsetup wizard- PaperVault web app — same crypto, browser version
- Main repo — issues, docs, SECURITY.md, full Vault-vs-key-shares explanation, AI-audit links
🤝 Contributing
Contributions are welcome! See the main repo at github.com/boazeb/papervault.
📄 License
MIT — see LICENSE.
🙏 Acknowledgments
- Shamir's Secret Sharing algorithm by Adi Shamir
- shamir-secret-sharing — v2 Shamir over GF(2^8)
- bip39 — mnemonic word lists for key aliases
- qrcode — QR generation
