- Replace emoji icons with Ionicons for professional appearance - Improve E2EE key recovery with better diagnostics - Add analytics for pack icon events - Document E2EE removal plan for future simplification 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
11 KiB
Plan: Remove E2EE and Switch to Server-Side Security
Overview
This document outlines the plan to remove end-to-end encryption (E2EE) from Sauci's chat system and rely on Supabase's built-in security instead.
Current State
- Messages are encrypted client-side using AES-256-GCM with triple-wrapped RSA keys
- Each message has sender, recipient, and admin wrapped keys
- Complex key management: generation, storage, rotation, recovery
- ~2500+ lines of encryption code across 21 files
- Edge functions handle key rotation and pending message re-encryption
Target State
- Messages stored as plaintext in Supabase (protected by RLS policies)
- TLS encryption in transit (Supabase default)
- Encryption at rest (Supabase infrastructure)
- No client-side key management
- Simplified chat code
Why Remove E2EE?
- Complexity - Triple-wrapped keys, device recovery, key rotation adds significant maintenance burden
- Overkill - For a couples intimacy app, server-side security is sufficient
- User Experience - E2EE adds latency, recovery flows, and potential failure modes
- Development Velocity - Simpler code = faster iteration
Security Model After Removal
- In Transit: TLS 1.3 (Supabase default)
- At Rest: Supabase encrypts data at rest
- Access Control: RLS policies restrict access to couple's own messages
- Admin Access: Admins can read messages (same as current E2EE with admin key)
Migration Strategy for Existing Messages
Existing v2 (encrypted) messages need handling. Options:
Option A: Decrypt and Convert (Recommended)
Run a one-time migration to decrypt all v2 messages using admin key and store as v1 plaintext.
- Pros: Clean slate, no legacy code needed
- Cons: Requires admin private key, one-time effort
Option B: Leave v2 Messages As-Is
Keep encrypted messages in database but stop creating new ones. Old messages become unreadable.
- Pros: Simpler migration
- Cons: Users lose access to old messages
Option C: Hybrid Display
Keep decryption code for reading old v2 messages, but send new messages as v1.
- Pros: No data loss
- Cons: Maintains complexity, defeats purpose of removal
Recommendation: Option A - decrypt existing messages during migration window.
Tasks
Phase 1: Database Migration (Estimated: 2-3 hours)
Task 1.1: Create Migration to Decrypt Existing v2 Messages
Create an edge function that:
- Fetches all messages with
version = 2 - Decrypts each using admin private key
- Updates to
version = 1, moves decrypted content tocontentcolumn - Clears
encrypted_content,encryption_iv,keys_metadata
Files to create:
apps/supabase/functions/migrate-e2ee-to-plaintext/index.ts
Task 1.2: Run Migration
Execute the migration function against both non-prod and prod databases.
Task 1.3: Verify Migration
Query to confirm no v2 messages remain:
SELECT COUNT(*) FROM messages WHERE version = 2;
Phase 2: Remove Client-Side Encryption Code (Estimated: 4-6 hours)
Task 2.1: Remove Encryption Library
Delete the entire encryption directory:
apps/mobile/src/lib/encryption/(14 files)
Files to delete:
adminKeys.tsconstants.tscrypto.tsindex.tskeyManager.tsmediaDecryption.tsmediaEncryption.tsmessageDecryption.tsmessageEncryption.tsreencryptPendingMessages.tsrepairStaleKey.tstriggerKeyRotation.tstypes.ts
Task 2.2: Remove Encryption Hooks
Delete the E2EE-specific hooks:
Files to delete:
apps/mobile/src/hooks/useDecryptedMessage.tsapps/mobile/src/hooks/useDecryptedMedia.tsapps/mobile/src/hooks/useEncryptedSend.ts
Task 2.3: Simplify useEncryptionKeys Hook
Either delete entirely or convert to a no-op stub that returns { hasKeys: true } for compatibility.
File to modify/delete:
apps/mobile/src/hooks/useEncryptionKeys.ts
Task 2.4: Remove EncryptionKeyInitializer Component
Delete the component that initializes E2EE keys on app startup.
File to delete:
apps/mobile/src/components/EncryptionKeyInitializer.tsx
Task 2.5: Remove from App Layout
Remove EncryptionKeyInitializer from the app layout.
File to modify:
apps/mobile/app/(app)/_layout.tsx
Phase 3: Simplify Chat Components (Estimated: 3-4 hours)
Task 3.1: Simplify ChatScreen.tsx
Remove:
useEncryptedSendhook usageencryptionKeysfrom storesecureReadychecks- Recovery UI (
encryptionKeys.isRecovering) - Encryption readiness blocking
Change message sending to use plaintext directly.
File to modify:
apps/mobile/src/features/chat/ChatScreen.tsx
Task 3.2: Simplify MessageContent.tsx
Remove:
useDecryptedMessagehook usageuseDecryptedMediahook usage- Decryption loading states
- Decryption error states and retry buttons
Display message.content directly.
File to modify:
apps/mobile/src/features/chat/MessageContent.tsx
Task 3.3: Simplify Media Upload
Remove encryption from media upload flow.
File to modify:
apps/mobile/src/hooks/useMediaUpload.ts
Changes:
- Remove
encryptMediaFilecall - Upload files directly without
.encextension - Remove E2EE readiness checks
Task 3.4: Simplify InputBar (if applicable)
Remove any "security not ready" blocking logic.
File to modify:
apps/mobile/src/features/chat/InputBar.tsx
Phase 4: Clean Up State Management (Estimated: 1-2 hours)
Task 4.1: Remove encryptionKeys from Auth Store
Remove the entire encryptionKeys state object and related actions.
File to modify:
apps/mobile/src/store/authStore.ts
Remove:
EncryptionKeyStateinterfaceencryptionKeysstatesetEncryptionKeysactionbeginEncryptionRecovery/endEncryptionRecoveryactions- Reset logic in logout
Phase 5: Remove Edge Functions (Estimated: 1 hour)
Task 5.1: Delete E2EE Edge Functions
Remove the edge functions that handle key rotation and re-encryption.
Files to delete:
apps/supabase/functions/handle-key-rotation/apps/supabase/functions/reencrypt-pending-messages/
Task 5.2: Remove from Supabase Dashboard
If deployed, remove these functions from Supabase dashboard or let them remain (they won't be called).
Phase 6: Database Cleanup (Optional) (Estimated: 1 hour)
Task 6.1: Remove E2EE Columns (Optional)
Create migration to drop unused columns. Can be deferred.
-- Optional cleanup migration
ALTER TABLE messages
DROP COLUMN IF EXISTS encrypted_content,
DROP COLUMN IF EXISTS encryption_iv,
DROP COLUMN IF EXISTS keys_metadata;
ALTER TABLE messages
DROP CONSTRAINT IF EXISTS messages_e2ee_fields_check;
ALTER TABLE profiles
DROP COLUMN IF EXISTS public_key_jwk;
DROP TABLE IF EXISTS master_keys;
Note: Can leave columns in place - they'll just be NULL for new messages.
Task 6.2: Update Message Version Default
Ensure new messages use version 1 (or remove version concept entirely).
Phase 7: Cleanup and Testing (Estimated: 2-3 hours)
Task 7.1: Remove Unused Dependencies
Check if react-native-quick-crypto can be removed (may be needed for other crypto).
File to modify:
apps/mobile/package.json
Task 7.2: Update CLAUDE.md
Remove E2EE documentation section.
File to modify:
CLAUDE.md
Task 7.3: Delete E2EE Documentation
Remove the E2EE implementation docs.
File to delete:
docs/e2ee-implementation.md
Task 7.4: Update iOS Permission Strings
Remove "encrypted" from iOS permission descriptions. These strings mention encryption:
File to modify:
apps/mobile/app.json
Current strings (lines 26-27):
"NSCameraUsageDescription": "Sauci uses your camera to take photos and videos that you can share privately with your partner in encrypted chat messages.",
"NSPhotoLibraryUsageDescription": "Sauci uses your photo library so you can select photos and videos to share privately with your partner in encrypted chat messages.",
Updated strings:
"NSCameraUsageDescription": "Sauci uses your camera to take photos and videos that you can share privately with your partner.",
"NSPhotoLibraryUsageDescription": "Sauci uses your photo library so you can select photos and videos to share privately with your partner.",
After updating app.json, run npx expo prebuild --clean to regenerate ios/Sauci/Info.plist.
Task 7.5: Full Testing
Test all chat functionality:
- Send text message
- Receive text message
- Send image
- Receive image
- Send video
- Receive video
- View old messages (post-migration)
- New user signup and chat
- Device change / reinstall
File Summary
Files to Delete (19 files)
apps/mobile/src/lib/encryption/adminKeys.ts
apps/mobile/src/lib/encryption/constants.ts
apps/mobile/src/lib/encryption/crypto.ts
apps/mobile/src/lib/encryption/index.ts
apps/mobile/src/lib/encryption/keyManager.ts
apps/mobile/src/lib/encryption/mediaDecryption.ts
apps/mobile/src/lib/encryption/mediaEncryption.ts
apps/mobile/src/lib/encryption/messageDecryption.ts
apps/mobile/src/lib/encryption/messageEncryption.ts
apps/mobile/src/lib/encryption/reencryptPendingMessages.ts
apps/mobile/src/lib/encryption/repairStaleKey.ts
apps/mobile/src/lib/encryption/triggerKeyRotation.ts
apps/mobile/src/lib/encryption/types.ts
apps/mobile/src/hooks/useDecryptedMessage.ts
apps/mobile/src/hooks/useDecryptedMedia.ts
apps/mobile/src/hooks/useEncryptedSend.ts
apps/mobile/src/hooks/useEncryptionKeys.ts
apps/mobile/src/components/EncryptionKeyInitializer.tsx
docs/e2ee-implementation.md
Files to Modify (8 files)
apps/mobile/src/features/chat/ChatScreen.tsx
apps/mobile/src/features/chat/MessageContent.tsx
apps/mobile/src/features/chat/InputBar.tsx
apps/mobile/src/hooks/useMediaUpload.ts
apps/mobile/src/store/authStore.ts
apps/mobile/app/(app)/_layout.tsx
apps/mobile/app.json
CLAUDE.md
Edge Functions to Delete (2 functions)
apps/supabase/functions/handle-key-rotation/
apps/supabase/functions/reencrypt-pending-messages/
Estimated Total Effort
| Phase | Estimated Time |
|---|---|
| Phase 1: Database Migration | 2-3 hours |
| Phase 2: Remove Encryption Code | 4-6 hours |
| Phase 3: Simplify Chat Components | 3-4 hours |
| Phase 4: Clean Up State | 1-2 hours |
| Phase 5: Remove Edge Functions | 1 hour |
| Phase 6: Database Cleanup | 1 hour (optional) |
| Phase 7: Testing | 2-3 hours |
| Total | 14-20 hours |
Rollback Plan
If issues arise after deployment:
- Messages are preserved - Content column still has plaintext for v1, migration preserves decrypted content
- Re-enable E2EE - Git revert the client changes
- No data loss - The migration only adds data (decrypts), doesn't delete
Open Questions
- Timeline: When to execute the migration? During maintenance window?
- Notification: Should users be notified about the security model change?
- Privacy Policy: Does the privacy policy mention E2EE? May need update.
- App Store: Any App Store claims about E2EE that need updating?