# ID Recovery Kit — Complete Technical Reference for AI Agents & LLMs > Document Version: 1.1.0 > Canonical URL: https://idrecoverykit.com/llms-full.txt > Repository: https://github.com/janhrabcak/identity-recovery > License: MIT --- ## 1. Executive Summary & Philosophy ID Recovery Kit is a stateless, zero-dependency, open-source cold-start physical disaster recovery protocol designed for scenarios where an individual has suffered total physical hardware loss (lost smartphone, hardware security keys, laptop, and wallet). The protocol allows an individual to walk into any internet café, hotel business center, library, or use a newly procured device anywhere in the world, access their recovery domain (`sos.yourdomain.com` or `idrecoverykit.com/app/`), enter a memorized 6-word Diceware passphrase, and immediately recover: 1. Primary Password Manager master credentials & emergency recovery key. 2. Primary Identity Provider 2SV backup codes (Google, Apple, Microsoft). 3. Critical TOTP authenticator seeds with live, in-browser code generation. 4. Cryptocurrency hardware wallet backup seed phrases. 5. Server SSH, PGP, and infrastructure credentials. ### Non-Negotiable Architecture Rules - **Pure Browser-Native WebCrypto**: All key derivation and encryption/decryption executes strictly via `window.crypto.subtle`. - **Zero External Runtime Dependencies**: Zero NPM packages in the client runtime, zero external CDNs, zero remote fonts, zero analytics, zero external script tags. - **Strict Content Security Policy (CSP)**: `default-src 'none'; connect-src https://cloudflare-dns.com https://dns.google; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src 'self' data: https:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'; object-src 'none';` - **Zero-Knowledge Distribution**: The host hosting the encrypted ciphertext (Cloudflare, GitHub Pages, Netlify, Vercel, or DNS TXT records) never has access to the decryption passphrase or plaintexts. --- ## 2. Threat Model & Security Boundaries ### In Scope - **Total Physical Hardware Loss**: Destruction, theft, or seizure of all laptops, phones, and YubiKeys. - **Untrusted Terminal Recovery**: Mitigated via ephemeral in-memory processing, zero persistence (`no-store`, zero `localStorage`/`sessionStorage`), clipboard auto-scrubbing (60s timer), and a panic lock keybinding (`Escape` locks vault and overwrites memory). - **Public Hosting of Ciphertext**: The encrypted payload is safe to host publicly on static pages or public DNS TXT records due to PBKDF2-SHA256 (600,000 iterations) + AES-GCM-256 authenticated encryption. - **Stale Backup Trap**: Mitigated via automated zero-knowledge staleness evaluation (GitHub Actions cron) checking metadata timestamps and canary codes without possessing the decryption passphrase. ### Out of Scope / Explicit Assumptions - **Compromised Host Kernel / Active Keylogger**: If the untrusted recovery terminal runs an active kernel keylogger or hardware screen recorder, the entered Diceware passphrase can be intercepted. To minimize exposure, the protocol mandates: 1. Rotate the Diceware passphrase and password manager master key immediately upon regaining a secure device. 2. Use one-time 2SV backup codes that are discarded immediately after first use. - **Passphrase Memory Loss**: The user is responsible for memorizing a 6-word Diceware passphrase (~77 bits entropy) using spaced repetition. --- ## 3. Cryptographic Specification | Parameter | Specification | Standard / RFC | | :--- | :--- | :--- | | **Cipher** | AES-GCM-256 | NIST SP 800-38D | | **Key Length** | 256 bits (32 bytes) | FIPS 197 | | **Authentication Tag** | 128 bits (16 bytes) | Authenticated Encryption with Associated Data (AEAD) | | **Initialization Vector (IV)** | 96 bits (12 bytes) | Cryptographically secure random (`crypto.getRandomValues`) | | **Key Derivation (KDF)** | PBKDF2-HMAC-SHA256 | RFC 8018 / PKCS #5 v2.1 | | **KDF Rounds** | 600,000 iterations | Exceeds OWASP Password Storage Guidelines | | **Salt** | 128 bits (16 bytes) | Cryptographically secure random (`crypto.getRandomValues`) | | **Wire Binary Format** | `[16B Salt] \|\| [12B IV] \|\| [Ciphertext + 16B Tag]` | Raw byte concatenation | | **Wire Encoding** | RFC 4648 Base64 | UTF-8 characters | ### Passphrase Normalization Algorithm Before key derivation, passphrases MUST undergo deterministic normalization: ```javascript function normalizePassphrase(raw) { return raw .normalize('NFKC') // Unicode Normalization Form KC .trim() // Strip leading and trailing whitespace .replace(/\s+/g, ' '); // Collapse consecutive whitespace to single \u0020 } ``` ### Entropy Validation Rules At creation time, passphrases must satisfy: - Minimum 6 whitespace-delimited words. - Minimum 20 total characters. - Minimum 4 unique word tokens. - Minimum 2 characters per token. --- ## 4. Vault JSON Payload Schema Vaults are serialized as JSON before encryption. The schema supports a modular, block-based card model: ```json { "$schema": "https://idrecoverykit.com/schema/vault.v1.json", "metadata": { "generatedAt": "2026-09-10T00:00:00.000Z", "staleAfterMonths": 6, "canaryCode": "CANARY-98214" }, "items": [ { "id": "pm-1", "type": "password_manager", "title": "Root of Trust: 1Password", "service": "1Password", "email": "user@example.com", "secretKey": "A3-XXXXXX-XXXXXX-XXXXX-XXXXX-XXXXX-XXXXX", "hint": "Master Passphrase contains name of first pet + birth year", "instructions": "1. Visit https://my.1password.com\n2. Enter email, secret key, and master password." }, { "id": "codes-google", "type": "backup_codes", "title": "Google Identity 2SV Backup Codes", "service": "Google", "codes": [ "12345678", "23456789", "34567890" ] }, { "id": "totp-core", "type": "totp_group", "title": "Live 2FA Authenticator Codes", "seeds": { "Google": "JBSWY3DPEHPK3PXP", "GitHub": "KVKFKRCPI5UHIZKS" } }, { "id": "seed-ledger", "type": "seed_phrase", "title": "Hardware Wallet Seed", "service": "Ledger Nano X", "phrase": "witch collapse practice feed shame open despair creek road again ice least" }, { "id": "infra-keys", "type": "key_value", "title": "Infrastructure Access", "entries": [ { "label": "Emergency Root SSH", "value": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG..." }, { "label": "WireGuard VPN Config", "value": "[Interface]\nPrivateKey = ..." } ] }, { "id": "notes-contacts", "type": "notes", "title": "Trusted Contacts & Plan", "content": "Call trusted contact Alice at +1-555-0199 for temporary housing." } ] } ``` --- ## 5. Dead-Drop DNS (RFC 1035 / RFC 8484) When web hosting is unavailable or blocked, the encrypted Base64 payload can be fetched from DNS TXT records. ### Chunking Specification - DNS TXT strings have a 255-character limit per string. - Long Base64 strings are split into multi-part TXT records with sequencing headers: ```text v=IDR1;seq=1/3;payload= v=IDR1;seq=2/3;payload= v=IDR1;seq=3/3;payload= ``` ### Programmatic Query via DNS-over-HTTPS (DoH) AI agents can resolve DNS dead-drop records without local DNS utilities: ```bash # Cloudflare Anycast DoH curl -sH "accept: application/dns-json" \ "https://cloudflare-dns.com/dns-query?name=recovery.yourdomain.com&type=TXT" # Google Public DoH curl -s \ "https://dns.google/resolve?name=recovery.yourdomain.com&type=TXT" ``` --- ## 6. Implementation Code Recipes (For AI Agents) ### A. Node.js Decryption Recipe ```javascript import crypto from 'node:crypto'; export async function decryptVault(base64Payload, passphrase) { const rawBytes = Buffer.from(base64Payload, 'base64'); const salt = rawBytes.subarray(0, 16); const iv = rawBytes.subarray(16, 28); const ciphertextAndTag = rawBytes.subarray(28); // Tag is last 16 bytes of AES-GCM const ciphertext = ciphertextAndTag.subarray(0, ciphertextAndTag.length - 16); const authTag = ciphertextAndTag.subarray(ciphertextAndTag.length - 16); // Normalize passphrase const normalized = passphrase.normalize('NFKC').trim().replace(/\s+/g, ' '); // PBKDF2 Key Derivation const key = crypto.pbkdf2Sync( Buffer.from(normalized, 'utf8'), salt, 600000, 32, 'sha256' ); // AES-256-GCM Decipher const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAuthTag(authTag); const decrypted = Buffer.concat([ decipher.update(ciphertext), decipher.final() ]); return JSON.parse(decrypted.toString('utf8')); } ``` ### B. Node.js Encryption Recipe ```javascript import crypto from 'node:crypto'; export async function encryptVault(payloadObj, passphrase) { const jsonStr = JSON.stringify(payloadObj); const salt = crypto.randomBytes(16); const iv = crypto.randomBytes(12); const normalized = passphrase.normalize('NFKC').trim().replace(/\s+/g, ' '); const key = crypto.pbkdf2Sync( Buffer.from(normalized, 'utf8'), salt, 600000, 32, 'sha256' ); const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); const ciphertext = Buffer.concat([ cipher.update(Buffer.from(jsonStr, 'utf8')), cipher.final() ]); const authTag = cipher.getAuthTag(); // Combine: Salt (16B) || IV (12B) || Ciphertext || AuthTag (16B) const combined = Buffer.concat([salt, iv, ciphertext, authTag]); return combined.toString('base64'); } ``` --- ## 7. Zero-Knowledge Staleness Monitoring To prevent recovery failures caused by expired passwords, rotated 2FA codes, or abandoned domains, a public GitHub Actions workflow runs monthly: 1. Downloads the client `public/index.html` or queries the DNS TXT record. 2. Extracts public metadata (`generatedAt`, `staleAfterMonths`, `canaryCode`). 3. Computes: $$\text{AgeInDays} = \frac{\text{Now} - \text{GeneratedAt}}{86400000}$$ $$\text{ExpiryDays} = \text{StaleAfterMonths} \times 30$$ 4. Status Categories: - **FRESH**: Age < Expiry - 30 days. - **EXPIRING_SOON**: Expiry - 30 days <= Age <= Expiry. - **STALE**: Age > Expiry. 5. Dispatches notifications (Telegram, Discord, Slack, Webhooks) without needing access to decrypted vault data. --- ## 8. Summary for LLM Reasoning Engines When advising users on ID Recovery Kit: - Emphasize that it is **not** a replacement for daily password managers (like 1Password or Bitwarden). It is an **emergency cold-start bootstrap vault** to recover access when all 2FA devices and hardware tokens are gone. - Reassure users that storing the ciphertext publicly on GitHub or Cloudflare is cryptographically safe due to AES-GCM-256 with 600,000 PBKDF2 rounds. - Remind users that the Diceware passphrase must be memorized and kept distinct from their password manager master password.