Open-source toolkit for SecurePaper - encrypted paper backups. Type sensitive text, encrypt it with AES-256 locally, print it as QR codes; the paper is unreadable to anyone without the key or passphrase, and readable by you decades from now.
Everything in this repository runs entirely on your machine. No network calls, no telemetry, no server. The data format is publicly specified (with test vectors), so your printed papers do not depend on SecurePaper existing.
| Package | What it is |
|---|---|
@securepaper_org/core |
The formats (AES-256-GCM, PBKDF2 passphrase mode, legacy CBC, QR chunking). Pure WebCrypto, zero dependencies, works in browsers and Node.js ≥ 20. |
@securepaper_org/cli |
securepaper command: encrypt files/stdin into printable QR sheets, decrypt scanned codes. |
@securepaper_org/mcp |
MCP server so AI assistants (Claude, and any MCP client) can create and read SecurePaper backups for you. |
# encrypt a file into a printable sheet + a separate key sheet
npx -p @securepaper_org/cli securepaper encrypt secrets.txt --title "2FA backup codes" --out ./paper
# passphrase mode: nothing is stored anywhere - paper + your memory is the whole backup
cat .env | npx -p @securepaper_org/cli securepaper encrypt --passphrase --title "prod env"
# read a paper back (paste the QR contents scanned with any QR app)
npx -p @securepaper_org/cli securepaper decrypt scanned.txt --key 3f8a...The output is a self-contained HTML sheet - open it in a browser, print it (Ctrl+P), done. In key mode a separate key sheet is written: print both and store them in different places.
Add to Claude Code:
claude mcp add securepaper -- npx -y @securepaper_org/mcpor to Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"securepaper": { "command": "npx", "args": ["-y", "@securepaper_org/mcp"] }
}
}Then ask: "back up these recovery codes on paper with a passphrase" - the assistant calls
encrypt_to_paper and hands you a printable sheet.
Tools: encrypt_to_paper, decrypt_paper, estimate_qr_count.
Privacy note: text passed through MCP tools is visible to the AI assistant and its conversation context. That is fine for secrets the assistant already sees or generated itself (recovery codes, configs, credentials created during setup). For a pre-existing wallet seed phrase or similar, use the web app or the CLI directly - the assistant never needs to see it.
import { encrypt, encryptWithPassphrase, decrypt, chunkForQr, joinChunks } from '@securepaper_org/core';
const { code, key } = await encrypt('Bank PIN: 4729'); // AES-256-GCM, random key
const { code: pc } = await encryptWithPassphrase('hi', 'long passphrase'); // PBKDF2, nothing stored
const chunks = chunkForQr(code); // ["SP:1/2|...", "SP:2/2|..."] for QR rendering
const back = await decrypt(joinChunks(chunks), { key });- Encryption: AES-256-GCM (authenticated), 12-byte random IV per message, via the platform's WebCrypto implementation - no userland crypto.
- Passphrase mode: PBKDF2-HMAC-SHA256, 600,000 iterations, 16-byte random salt.
- Legacy AES-256-CBC documents (pre-09/2026) remain readable; new documents never use CBC.
- Losing the key / forgetting the passphrase makes a paper permanently unreadable - by design.
- Full spec with test vectors: FORMAT.md. Cross-implementation compatibility with the SecurePaper web app is pinned by fixtures in the test suite.
Vulnerability reports: support@securepaper.app
npm install
npm test # all workspaces (node:test)