mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-07 09:57:50 +08:00
feat: add comprehensive recovery playbook and update documentation
- Added RECOVERY_PLAYBOOK.md with complete offline recovery guide - Updated README.md to reference manual restore method - Added RECOVERY_PLAYBOOK.md to project structure - Removed test.pgp file
This commit is contained in:
14
README.md
14
README.md
@@ -54,13 +54,24 @@ bun run dev
|
|||||||
3. Click "Backup" to encrypt and generate QR code
|
3. Click "Backup" to encrypt and generate QR code
|
||||||
4. Save/print QR code for offline storage
|
4. Save/print QR code for offline storage
|
||||||
|
|
||||||
**Restore Flow:**
|
**Restore Flow (Web Interface):**
|
||||||
|
|
||||||
1. Scan QR code or paste encrypted text
|
1. Scan QR code or paste encrypted text
|
||||||
2. Import PGP private key or enter password
|
2. Import PGP private key or enter password
|
||||||
3. Click "Restore" to decrypt mnemonic
|
3. Click "Restore" to decrypt mnemonic
|
||||||
4. Mnemonic auto-clears after 10 seconds
|
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
|
### API Usage
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -293,6 +304,7 @@ seedpgp-web/
|
|||||||
├── package.json
|
├── package.json
|
||||||
├── vite.config.ts # Vite configuration
|
├── vite.config.ts # Vite configuration
|
||||||
├── GEMINI.md # AI agent project brief
|
├── GEMINI.md # AI agent project brief
|
||||||
|
├── RECOVERY_PLAYBOOK.md # Offline recovery guide
|
||||||
└── README.md # This file
|
└── README.md # This file
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
422
RECOVERY_PLAYBOOK.md
Normal file
422
RECOVERY_PLAYBOOK.md
Normal file
@@ -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 <https://gchq.github.io/CyberChef/>
|
||||||
|
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 <https://www.gpg4win.org/>
|
||||||
|
- 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 <https://seedpgp.com> (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: <https://github.com/kccleoc/seedpgp-web/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
|
||||||
|
|
||||||
|
***
|
||||||
Reference in New Issue
Block a user