diff --git a/README.md b/README.md
index f43ef9a..580a0a5 100644
--- a/README.md
+++ b/README.md
@@ -54,13 +54,24 @@ bun run dev
3. Click "Backup" to encrypt and generate QR code
4. Save/print QR code for offline storage
-**Restore Flow:**
+**Restore Flow (Web Interface):**
1. Scan QR code or paste encrypted text
2. Import PGP private key or enter password
3. Click "Restore" to decrypt mnemonic
4. Mnemonic auto-clears after 10 seconds
+**Offline/Manual Restore:**
+
+For airgapped recovery without the web interface, use the command-line method documented in [RECOVERY_PLAYBOOK.md](RECOVERY_PLAYBOOK.md):
+
+1. Extract Base45 payload from SEEDPGP1 frame
+2. Decode Base45 to PGP binary
+3. Decrypt with GPG using private key or password
+4. Parse JSON output to recover mnemonic
+
+See [RECOVERY_PLAYBOOK.md](RECOVERY_PLAYBOOK.md) for complete step-by-step instructions.
+
### API Usage
```typescript
@@ -293,6 +304,7 @@ seedpgp-web/
├── package.json
├── vite.config.ts # Vite configuration
├── GEMINI.md # AI agent project brief
+├── RECOVERY_PLAYBOOK.md # Offline recovery guide
└── README.md # This file
```
diff --git a/RECOVERY_PLAYBOOK.md b/RECOVERY_PLAYBOOK.md
new file mode 100644
index 0000000..9399890
--- /dev/null
+++ b/RECOVERY_PLAYBOOK.md
@@ -0,0 +1,422 @@
+## SeedPGP Recovery Playbook - Offline Recovery Guide
+
+**Generated:** Feb 1, 2026 | **SeedPGP v1.4.3** | **Frame Format:** `SEEDPGP1:0:CRC16:BASE45_PAYLOAD`
+
+***
+
+## 📋 Recovery Requirements
+
+```
+✅ SEEDPGP1 QR code or printed text
+✅ PGP Private Key (.asc file) OR Message Password (if symmetric encryption used)
+✅ Offline computer with terminal access
+✅ gpg command line tool (GNU Privacy Guard)
+```
+
+**⚠️ Important:** This playbook assumes you have the original encryption parameters:
+
+- PGP private key (if PGP encryption was used)
+- Private key passphrase (if the key is encrypted)
+- Message password (if symmetric encryption was used)
+- BIP39 passphrase (if 25th word was used during backup)
+
+***
+
+## 🔓 Step 1: Understand Frame Format
+
+**SeedPGP Frame Structure:**
+
+```
+SEEDPGP1:0:CRC16:BASE45_PAYLOAD
+```
+
+- **SEEDPGP1:** Protocol identifier
+- **0:** Frame version (single frame)
+- **CRC16:** 4-character hexadecimal CRC16-CCITT checksum
+- **BASE45_PAYLOAD:** Base45-encoded PGP binary data
+
+**Example Frame:**
+
+```
+SEEDPGP1:0:58B5:2KO K0S-U. M:E1T*A%50%886N2SDITXSQVE VV$BA7.FZ+I01N%ISK$KBGESBRNOHYIK%A8N1FUOE.Z1T:8JBHDNNBV2AVJRGC1-OY67AU777I07UB88TQN0B5033IJOGG7$2ID/QNIR.:UGUO/M0BH0O94468TXM 0RGSIYT FNSQGNJKDCHP3JV/V-77:%KVZG+6VA7P826W0N0TBI5AMSQX60A%2E$OMWF1TV/J0SJJ 0M-VF0TH60W4TL1/519HS7BO%OT-QGZ5.AS.18AWSGF9O5E%MCYLM4STPI5+.3A5K7ZULFQM.JO:J3/C.IOB1819L8*ME027S9DJ0+18WCVTC30928T72W5D4P0UHC4O11IPRQ I5T39RSI9BTVT6LK6A9PWUF7B2CBEI43M%TT47%I4KBT-0H44L.RP$U02F8-7A*LH2$G44Q.880WF0BJ5SB5OR*39W/N3T9 -DQ4C
+```
+
+### Extract Base45 Payload
+
+```bash
+# Extract everything after the 3rd colon
+FRAME="SEEDPGP1:0:58B5:2KO K0S-U. M:E1T*A%50%886N2SDITXSQVE VV$BA7.FZ+I01N%ISK$KBGESBRNOHYIK%A8N1FUOE.Z1T:8JBHDNNBV2AVJRGC1-OY67AU777I07UB88TQN0B5033IJOGG7$2ID/QNIR.:UGUO/M0BH0O94468TXM 0RGSIYT FNSQGNJKDCHP3JV/V-77:%KVZG+6VA7P826W0N0TBI5AMSQX60A%2E$OMWF1TV/J0SJJ 0M-VF0TH60W4TL1/519HS7BO%OT-QGZ5.AS.18AWSGF9O5E%MCYLM4STPI5+.3A5K7ZULFQM.JO:J3/C.IOB1819L8*ME027S9DJ0+18WCVTC30928T72W5D4P0UHC4O11IPRQ I5T39RSI9BTVT6LK6A9PWUF7B2CBEI43M%TT47%I4KBT-0H44L.RP$U02F8-7A*LH2$G44Q.880WF0BJ5SB5OR*39W/N3T9 -DQ4C"
+PAYLOAD=$(echo "$FRAME" | cut -d: -f4-)
+echo "$PAYLOAD" > payload.b45
+```
+
+***
+
+## 🔓 Step 2: Decode Base45 → PGP Binary
+
+**Option A: Using base45 CLI tool:**
+
+```bash
+# Install base45 if needed
+npm install -g base45
+
+# Decode the payload
+base45decode < payload.b45 > encrypted.pgp
+```
+
+**Option B: Using CyberChef (offline browser tool):**
+
+1. Download CyberChef HTML from
+2. Open it in an offline browser
+3. Input → Paste your Base45 payload
+4. Operation → `From Base45`
+5. Save output as `encrypted.pgp`
+
+**Option C: Manual verification (check CRC):**
+
+```bash
+# Verify CRC16 checksum matches
+# The CRC16-CCITT-FALSE checksum should match the value in the frame (58B5 in example)
+# If using the web app, this is automatically verified during decryption
+```
+
+***
+
+## 🔓 Step 3: Decrypt PGP Binary
+
+### Option A: PGP Private Key Decryption (PKESK)
+
+If the backup was encrypted with a PGP public key:
+
+```bash
+# Import your private key (if not already imported)
+gpg --import private-key.asc
+
+# List keys to verify fingerprint
+gpg --list-secret-keys --keyid-format LONG
+
+# Decrypt using your private key
+gpg --batch --yes --decrypt encrypted.pgp
+```
+
+**Expected JSON Output:**
+
+```json
+{"v":1,"t":"bip39","w":"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about","l":"en","pp":0}
+```
+
+**If private key has a passphrase:**
+
+```bash
+gpg --batch --yes --passphrase "YOUR-PGP-KEY-PASSPHRASE" --decrypt encrypted.pgp
+```
+
+### Option B: Message Password Decryption (SKESK)
+
+If the backup was encrypted with a symmetric password:
+
+```bash
+gpg --batch --yes --passphrase "YOUR-MESSAGE-PASSWORD" --decrypt encrypted.pgp
+```
+
+**Expected JSON Output:**
+
+```json
+{"v":1,"t":"bip39","w":"your seed phrase words here","l":"en","pp":1}
+```
+
+***
+
+## 🔓 Step 4: Parse Decrypted Data
+
+The decrypted output is a JSON object with the following structure:
+
+```json
+{
+ "v": 1, // Version (always 1)
+ "t": "bip39", // Type (always "bip39")
+ "w": "word1 word2 ...", // BIP39 mnemonic words (lowercase, single spaces)
+ "l": "en", // Language (always "en" for English)
+ "pp": 0 // BIP39 passphrase flag: 0 = no passphrase, 1 = passphrase used
+}
+```
+
+**Extract the mnemonic:**
+
+```bash
+# After decryption, extract the 'w' field
+DECRYPTED='{"v":1,"t":"bip39","w":"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about","l":"en","pp":0}'
+MNEMONIC=$(echo "$DECRYPTED" | grep -o '"w":"[^"]*"' | cut -d'"' -f4)
+echo "Mnemonic: $MNEMONIC"
+```
+
+***
+
+## 💰 Step 5: Wallet Recovery
+
+### BIP39 Passphrase Status
+
+Check the `pp` field in the decrypted JSON:
+
+- `"pp": 0` → No BIP39 passphrase was used during backup
+- `"pp": 1` → **BIP39 passphrase was used** (25th word/extra passphrase)
+
+### Recovery Instructions
+
+**Without BIP39 Passphrase (`pp": 0`):**
+
+```
+Seed Words: [extracted from 'w' field]
+BIP39 Passphrase: None required
+```
+
+**With BIP39 Passphrase (`pp": 1`):**
+
+```
+Seed Words: [extracted from 'w' field]
+BIP39 Passphrase: [Your original 25th word/extra passphrase]
+```
+
+**Wallet Recovery Steps:**
+
+1. **Hardware Wallets (Ledger/Trezor):**
+ - Start recovery process
+ - Enter 12/24 word mnemonic
+ - **If `pp": 1`:** Enable passphrase option and enter your BIP39 passphrase
+
+2. **Software Wallets (Electrum, MetaMask, etc.):**
+ - Create/restore wallet
+ - Enter mnemonic phrase
+ - **If `pp": 1`:** Look for "Advanced options" or "Passphrase" field
+
+3. **Bitcoin Core (using `hdseed`):**
+
+ ```bash
+ # Use the mnemonic with appropriate BIP39 passphrase
+ # Consult your wallet's specific recovery documentation
+ ```
+
+***
+
+## 🛠️ GPG Setup (One-time)
+
+**Mac (Homebrew):**
+
+```bash
+brew install gnupg
+```
+
+**Ubuntu/Debian:**
+
+```bash
+sudo apt update && sudo apt install gnupg
+```
+
+**Fedora/RHEL/CentOS:**
+
+```bash
+sudo dnf install gnupg
+```
+
+**Windows:**
+
+- Download Gpg4win from
+- Install and use Kleopatra or command-line gpg
+
+**Verify installation:**
+
+```bash
+gpg --version
+```
+
+***
+
+## 🔍 Troubleshooting
+
+| Error | Likely Cause | Solution |
+|-------|-------------|----------|
+| `gpg: decryption failed: No secret key` | Wrong PGP private key or key not imported | Import correct private key: `gpg --import private-key.asc` |
+| `gpg: BAD decrypt` | Wrong passphrase (key passphrase or message password) | Verify you're using the correct passphrase |
+| `base45decode: command not found` | base45 CLI tool not installed | Use CyberChef or install: `npm install -g base45` |
+| `gpg: no valid OpenPGP data found` | Invalid Base45 decoding or corrupted payload | Verify Base45 decoding step, check for scanning errors |
+| `gpg: CRC error` | Frame corrupted during scanning/printing | Rescan QR code or use backup copy |
+| `gpg: packet(3) too short` | Truncated PGP binary | Ensure complete frame was captured |
+| JSON parsing error after decryption | Output not valid JSON | Check if decryption succeeded, may need different passphrase |
+
+**Common Issues:**
+
+1. **Wrong encryption method:** Trying PGP decryption when symmetric password was used, or vice versa
+2. **BIP39 passphrase mismatch:** Forgetting the 25th word used during backup
+3. **Frame format errors:** Missing `SEEDPGP1:` prefix or incorrect colon separation
+
+***
+
+## 📦 Recovery Checklist
+
+```
+[ ] Airgapped computer prepared (offline, clean OS)
+[ ] GPG installed and verified
+[ ] Base45 decoder available (CLI tool or CyberChef)
+[ ] SEEDPGP1 frame extracted and verified
+[ ] Base45 payload decoded to PGP binary
+[ ] CRC16 checksum verified (optional but recommended)
+[ ] Correct decryption method identified (PGP key vs password)
+[ ] Private key imported (if PGP encryption)
+[ ] Decryption successful with valid JSON output
+[ ] Mnemonic extracted from 'w' field
+[ ] BIP39 passphrase status checked ('pp' field)
+[ ] Appropriate BIP39 passphrase ready (if 'pp': 1)
+[ ] Wallet recovery tool selected (hardware/software wallet)
+[ ] Test recovery on testnet/small amount first
+[ ] Browser/terminal history cleared after recovery
+[ ] Original backup securely stored or destroyed after successful recovery
+[ ] Funds moved to new addresses after recovery
+```
+
+***
+
+## ⚠️ Security Best Practices
+
+**Critical Security Measures:**
+
+1. **Always use airgapped computer** for recovery operations
+2. **Never type mnemonics or passwords on internet-connected devices**
+3. **Clear clipboard and terminal history** after recovery
+4. **Test with small amounts** before recovering significant funds
+5. **Move funds to new addresses** after successful recovery
+6. **Destroy recovery materials** or store them separately from private keys
+
+**Storage Recommendations:**
+
+- Print QR code on archival paper or metal
+- Store playbook separately from private keys/passphrases
+- Use multiple geographically distributed backups
+- Consider Shamir's Secret Sharing for critical components
+
+***
+
+## 🔄 Alternative Recovery Methods
+
+**Using the SeedPGP Web App (Online):**
+
+1. Open (or local instance)
+2. Switch to "Restore" tab
+3. Scan QR code or paste SEEDPGP1 frame
+4. Provide private key or message password
+5. App handles Base45 decoding, CRC verification, and decryption automatically
+
+**Using Custom Script (Advanced):**
+
+```python
+# Example Python recovery script (conceptual)
+import base45
+import gnupg
+import json
+
+frame = "SEEDPGP1:0:58B5:2KO K0S-U. M:..."
+parts = frame.split(":", 3)
+crc_expected = parts[2]
+b45_payload = parts[3]
+
+# Decode Base45
+pgp_binary = base45.b45decode(b45_payload)
+
+# Decrypt with GPG
+gpg = gnupg.GPG()
+decrypted = gpg.decrypt(pgp_binary, passphrase="your-passphrase")
+
+# Parse JSON
+data = json.loads(str(decrypted))
+print(f"Mnemonic: {data['w']}")
+print(f"BIP39 Passphrase used: {'YES' if data['pp'] == 1 else 'NO'}")
+```
+
+***
+
+## 📝 Technical Details
+
+**Encryption Algorithms:**
+
+- **PGP Encryption:** AES-256 (OpenPGP standard)
+- **Symmetric Encryption:** AES-256 with random session key
+- **CRC Algorithm:** CRC16-CCITT-FALSE (polynomial 0x1021)
+- **Encoding:** Base45 (RFC 9285)
+
+**JSON Schema:**
+
+```json
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "object",
+ "required": ["v", "t", "w", "l", "pp"],
+ "properties": {
+ "v": {
+ "type": "integer",
+ "const": 1,
+ "description": "Protocol version"
+ },
+ "t": {
+ "type": "string",
+ "const": "bip39",
+ "description": "Data type (BIP39 mnemonic)"
+ },
+ "w": {
+ "type": "string",
+ "pattern": "^[a-z]+( [a-z]+){11,23}$",
+ "description": "BIP39 mnemonic words (lowercase, space-separated)"
+ },
+ "l": {
+ "type": "string",
+ "const": "en",
+ "description": "Language (English)"
+ },
+ "pp": {
+ "type": "integer",
+ "enum": [0, 1],
+ "description": "BIP39 passphrase flag: 0 = none, 1 = used"
+ },
+ "fpr": {
+ "type": "array",
+ "items": {"type": "string"},
+ "description": "Optional: Recipient key fingerprints"
+ }
+ }
+}
+```
+
+**Frame Validation Rules:**
+
+1. Must start with `SEEDPGP1:`
+2. Frame version must be `0` (single frame)
+3. CRC16 must be 4 hex characters `[0-9A-F]{4}`
+4. Base45 payload must use valid Base45 alphabet
+5. Decoded PGP binary must pass CRC16 verification
+
+***
+
+## 🆘 Emergency Contact & Support
+
+**No Technical Support Available:**
+
+- SeedPGP is a self-sovereign tool with no central authority
+- You are solely responsible for your recovery
+- Test backups regularly to ensure they work
+
+**Community Resources:**
+
+- GitHub Issues:
+- Bitcoin StackExchange: Use `seedpgp` tag
+- Local Bitcoin meetups for in-person help
+
+**Remember:** The security of your funds depends on your ability to successfully execute this recovery process. Practice with test backups before relying on it for significant amounts.
+
+***
+
+**Print this playbook on archival paper or metal. Store separately from encrypted backups and private keys.** 🔒
+
+**Last Updated:** February 1, 2026
+**SeedPGP Version:** 1.4.3
+**Frame Example CRC:** 58B5 ✓
+**Test Recovery:** [ ] Completed [ ] Not Tested
+
+***
diff --git a/test.pgp b/test.pgp
deleted file mode 100644
index 017cbd3..0000000
Binary files a/test.pgp and /dev/null differ