Security Architecture
Defense in depth — four layers of security from your browser to the blockchain.
Layer 1: Client-Side Key Encryption
Algorithm: AES-256-GCM (Galois/Counter Mode)
Key Derivation: PBKDF2 with SHA-256, 600,000 iterations
Salt: 32 bytes, cryptographically random, unique per wallet
IV: 12 bytes, cryptographically random, unique per encryption
Implementation: Web Crypto API (browser-native, not a JS library)
Raw Key Storage: NEVER — only encrypted blob in localStorage
Session Key: Decrypted to sessionStorage (auto-clears on tab close)
Password Storage: NEVER stored anywhere — not even temporarily
Layer 2: Per-User Isolation
Storage Scoping: All localStorage keys prefixed with Supabase user ID
User A: cv_{userA_id}_enc_pk → User A's encrypted key
User B: cv_{userB_id}_enc_pk → User B's encrypted key
Isolation: Different Gmail = different wallet, even on same browser
Session: sessionStorage key also scoped to active user ID
Layer 3: Web Application Security
Transport: HTTPS/TLS on all traffic, HSTS enabled
CSP: Content-Security-Policy restricting script/connect/frame sources
Frame Protection: X-Frame-Options: DENY — prevents clickjacking
XSS: X-XSS-Protection + CSP script-src restrictions
CSRF: SameSite cookie policy via Supabase + CSRF tokens
Rate Limiting: 30 requests/minute per IP on API routes
Input Validation: Server-side validation of addresses, amounts, hashes
Layer 4: Smart Contract Security
Time-Lock: Enforced on-chain — block.timestamp must exceed unlockTime
Admin Override: NONE — no pause, no admin withdraw, no backdoor
Owner Check: Only vault creator can withdraw their vault
Double Withdrawal: Prevented by withdrawn boolean flag
Fee Handling: Platform fee transferred atomically in createVault()
Duration Limits: 1 minute minimum, 28 days maximum, enforced in contract
Test Coverage: 18 unit tests covering all edge cases
What Our Server NEVER Has Access To
Private Keys
Seed Phrases
Wallet Passwords
Encryption Keys
Raw Transaction Data
Fund Custody
Admin Override
User Funds