Introduction
ursalock provides end-to-end encrypted document storage using passkey-derived keys.
The Problem
Section titled “The Problem”You’re building a web app and want to:
- Store user data securely
- Sync data across devices
- Keep user data truly private (zero-knowledge)
Traditional solutions either:
- Store data in plaintext (Firebase, Supabase)
- Require complex key management (PGP, manual encryption)
- Vendor lock-in with proprietary E2EE (1Password, Bitwarden)
The Solution
Section titled “The Solution”ursalock provides:
Passkey-Based E2EE
- Your passkey derives the encryption key via WebAuthn PRF
- No recovery key to store — your passkey IS the key
- Same passkey = same data on any device
Document-Level Storage
- Store encrypted documents in collections
- Each document independently encrypted
- Efficient syncing (only changed documents)
Zero-Knowledge Architecture
- Server stores only encrypted ciphertext
- Server never sees your plaintext
- All crypto happens client-side
Self-Hostable
- Single Docker image
- SQLite storage (no external DB)
- Your server, your data
How It Works
Section titled “How It Works”┌──────────────────────────────────────────────────────┐│ CLIENT ││ ││ Passkey → PRF → cipherJwk → deriveVaultKeys() ││ ↓ ││ encryptionKey + hmacKey ││ ↓ ││ Document → AES-256-GCM → Ciphertext ││ │└────────────────────────┬─────────────────────────────┘ │ HTTPS (encrypted documents) ▼┌──────────────────────────────────────────────────────┐│ SERVER ││ ││ Receives encrypted documents → Stores in SQLite ││ Server CANNOT read your data ││ Server only knows document metadata (uid, version) ││ │└──────────────────────────────────────────────────────┘- User authenticates with their passkey
- WebAuthn PRF derives a
cipherJwk(master key) - Vault-specific keys derived via HKDF
- Documents encrypted/decrypted with vault keys
- Server stores and syncs encrypted documents
The server never sees your encryption key or plaintext data.
Why Passkeys?
Section titled “Why Passkeys?”- Your biometric or security key IS the encryption key
- Synced by your password manager (iCloud, Google, Proton Pass)
- No separate secret to manage or lose
- One tap to authenticate — no password to type
Re-Authentication
Section titled “Re-Authentication”One tradeoff: the cipherJwk lives only in memory. After a page refresh:
- JWT (auth token) persists ✓
- cipherJwk is gone ✗
Solution: prompt for passkey on refresh. It’s a quick tap — no password to type.
Next Steps
Section titled “Next Steps”- Quick Start — Get up and running in 5 minutes
- Authentication — Passkey flows and hooks
- Self-Hosting — Deploy your own server
- Security Model — Understand the cryptography