# MongoDB Schema Design for Normogen **Date**: 2026-02-14 **Focus**: Zero-knowledge encryption for all sensitive data AND metadata **Database**: MongoDB 6.0+ --- ## Table of Contents 1. [Zero-Knowledge Encryption Requirements](#zero-knowledge-encryption-requirements) 2. [Database Architecture Overview](#database-architecture-overview) 3. [Collection Schemas](#collection-schemas) 4. [Encryption Strategy](#encryption-strategy) 5. [Indexing Strategy](#indexing-strategy) 6. [Privacy-Preserving Queries](#privacy-preserving-queries) 7. [Data Migration](#data-migration) 8. [Performance Considerations](#performance-considerations) --- ## Zero-Knowledge Encryption Requirements ### Core Principle **ALL sensitive data AND metadata must be encrypted client-side before reaching MongoDB.** ### What Must Be Encrypted #### Health Data (Value + Metadata) ```javascript // Blood pressure reading - BOTH value AND metadata encrypted { value: "10", // Encrypted ❌ metadata: { type: "blood_pressure", // Encrypted ❌ unit: "mmHg" // Encrypted ❌ } } // After encryption (stored in MongoDB) { value: { encrypted: true, data: "a1b2c3d4...", iv: "e5f6g7h8...", authTag: "i9j0k1l2..." }, metadata: { encrypted: true, data: "m3n4o5p6...", iv: "q7r8s9t0...", authTag: "u1v2w3x4..." } } ``` #### What Can Be Plaintext ```javascript // ONLY non-sensitive, non-identifying fields { userId: "user-123", // Plaintext (for queries) ✅ familyId: "family-456", // Plaintext (for family queries) ✅ profileId: "profile-789", // Plaintext (for profile queries) ✅ createdAt: ISODate("2026-02-14"), // Plaintext (for sorting) ✅ updatedAt: ISODate("2026-02-14"), // Plaintext (for sorting) ✅ // ALL health data encrypted ❌ healthData: [ { encrypted: true, data: "...", iv: "...", authTag: "..." } ] } ``` #### Why Metadata Must Be Encrypted **Problem**: If metadata is plaintext, attackers can infer sensitive information. **Example**: ```javascript // BAD: Metadata plaintext (leaks information) { userId: "user-123", healthData: [ { type: "hiv_test", // Reveals HIV status result: "positive", // Reveals HIV status date: "2026-02-14", // Reveals when tested doctor: "Dr. Smith", // Reveals healthcare provider } ] } // GOOD: Metadata encrypted (privacy-preserving) { userId: "user-123", healthData: [ { encrypted: true, data: "a1b2c3d4...", // Encrypted: type + result + date + doctor iv: "e5f6g7h8...", authTag: "i9j0k1l2..." } ] } ``` --- ## Database Architecture Overview ### Database Structure ``` normogen (database) ├── users (collection) ├── families (collection) ├── profiles (collection) ├── health_data (collection) ├── lab_results (collection) ├── medications (collection) ├── appointments (collection) ├── shares (collection) └── refresh_tokens (collection) ``` ### Data Flow ``` Client (React Native / React) ├── User enters data ├── Client encrypts data (AES-256-GCM, PBKDF2) ├── Client sends encrypted data to server │ Server (Axum / Rust) ├── Server receives encrypted data ├── Server NEVER decrypts data ├── Server stores encrypted data in MongoDB │ MongoDB ├── Stores ONLY encrypted data ├── No plaintext sensitive data └── Zero-knowledge architecture maintained ``` --- ## Collection Schemas ### 1. Users Collection **Purpose**: User authentication and account data **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for authentication) userId: { type: String, unique: true, required: true }, email: { type: String, index: true, required: true }, // Plaintext for login passwordHash: { type: String, required: true }, // Plaintext (bcrypt hash) tokenVersion: { type: Number, default: 1 }, // Plaintext (for JWT revocation) // Encrypted fields (zero-knowledge) encryptedRecoveryPhrase: { encrypted: true, data: String, // Encrypted recovery phrase iv: String, authTag: String }, // Family relationships familyId: { type: String, index: true }, // Plaintext (for family queries) familyRole: { type: String }, // Plaintext (parent, child, elderly) permissions: [String], // Plaintext (for JWT permissions) // Metadata (plaintext) createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now }, lastLoginAt: Date } ``` **Encryption Notes**: - ✅ **Plaintext**: `userId`, `email`, `passwordHash`, `tokenVersion`, `familyId`, `familyRole`, `permissions` - ❌ **Encrypted**: `encryptedRecoveryPhrase` **Indexes**: ```javascript // Indexes for performance db.users.createIndex({ userId: 1 }, { unique: true }); db.users.createIndex({ email: 1 }, { unique: true }); db.users.createIndex({ familyId: 1 }); db.users.createIndex({ createdAt: -1 }); // For sorting ``` --- ### 2. Families Collection **Purpose**: Family structure and relationships **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for queries) familyId: { type: String, unique: true, required: true }, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now }, // Encrypted family name (privacy-preserving) familyName: { encrypted: true, data: String, // Encrypted family name iv: String, authTag: String }, // Encrypted family metadata familyMetadata: { encrypted: true, data: String, // Encrypted metadata (address, phone, etc.) iv: String, authTag: String }, // Plaintext family structure (for queries) members: [ { userId: String, // Plaintext (for queries) profileId: String, // Plaintext (for queries) role: String, // Plaintext (parent, child, elderly) permissions: [String] // Plaintext (for JWT) } ] } ``` **Encryption Notes**: - ✅ **Plaintext**: `familyId`, `members[*].userId`, `members[*].profileId`, `members[*].role`, `members[*].permissions` - ❌ **Encrypted**: `familyName`, `familyMetadata` --- ### 3. Profiles Collection **Purpose**: Person profiles (users can have multiple profiles) **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for queries) profileId: { type: String, unique: true, required: true }, userId: { type: String, index: true, required: true }, // Owner familyId: { type: String, index: true }, profileType: { type: String }, // self, child, elderly, pet // Encrypted profile data (privacy-preserving) profileName: { encrypted: true, data: String, // Encrypted name (e.g., "John Doe") iv: String, authTag: String }, // Encrypted profile metadata profileMetadata: { encrypted: true, data: String, // Encrypted metadata (birth date, gender, etc.) iv: String, authTag: String }, // Metadata (plaintext) createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } } ``` **Encryption Notes**: - ✅ **Plaintext**: `profileId`, `userId`, `familyId`, `profileType` - ❌ **Encrypted**: `profileName`, `profileMetadata` --- ### 4. Health Data Collection **Purpose**: Health records (weight, height, blood pressure, etc.) **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for queries) healthDataId: { type: String, unique: true, required: true }, userId: { type: String, index: true, required: true }, // Owner profileId: { type: String, index: true, required: true }, // Subject familyId: { type: String, index: true }, // Encrypted health data (value + metadata) healthData: [ { // Encrypted value + metadata encrypted: true, data: String, // Encrypted: { value: 10, type: "blood_pressure", unit: "mmHg", date: "2026-02-14" } iv: String, authTag: String } ], // Metadata (plaintext) createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now }, dataSource: String // Plaintext (e.g., "manual", "healthKit", "googleFit") } ``` **Example: Blood Pressure Reading**: ```javascript // Client-side data structure const healthData = { value: "120/80", type: "blood_pressure", unit: "mmHg", date: "2026-02-14T10:30:00Z", notes: "After morning coffee" }; // Client encrypts healthData const encryptedHealthData = encrypt(healthData, userKey); // Stored in MongoDB { _id: ObjectId("..."), healthDataId: "health-123", userId: "user-456", profileId: "profile-789", familyId: "family-012", // Encrypted (value + metadata) healthData: [ { encrypted: true, data: "a1b2c3d4...", // Contains: value, type, unit, date, notes iv: "e5f6g7h8...", authTag: "i9j0k1l2..." } ], // Metadata (plaintext) createdAt: ISODate("2026-02-14T10:30:00Z"), updatedAt: ISODate("2026-02-14T10:30:00Z"), dataSource: "healthKit" } ``` **Encryption Notes**: - ✅ **Plaintext**: `healthDataId`, `userId`, `profileId`, `familyId`, `createdAt`, `updatedAt`, `dataSource` - ❌ **Encrypted**: `healthData[*]` (value + metadata) --- ### 5. Lab Results Collection **Purpose**: Lab test results (imported via QR code) **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for queries) labResultId: { type: String, unique: true, required: true }, userId: { type: String, index: true, required: true }, // Owner profileId: { type: String, index: true, required: true }, // Subject familyId: { type: String, index: true }, // Encrypted lab data (value + metadata) labData: { encrypted: true, data: String, // Encrypted: { testType: "blood_test", results: [...], date: "...", lab: "..." } iv: String, authTag: String }, // Encrypted lab metadata labMetadata: { encrypted: true, data: String, // Encrypted: { labName: "LabCorp", doctor: "Dr. Smith", address: "..." } iv: String, authTag: String }, // Metadata (plaintext) createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now }, dataSource: String // Plaintext (e.g., "qr_code", "manual_entry") } ``` **Example: Blood Test Results**: ```javascript // Client-side data structure const labData = { testType: "blood_panel", results: [ { test: "cholesterol", value: 200, unit: "mg/dL", normalRange: "125-200" }, { test: "glucose", value: 95, unit: "mg/dL", normalRange: "70-100" } ], date: "2026-02-14T08:00:00Z", lab: "LabCorp", doctor: "Dr. Smith", notes: "Fasting for 12 hours" }; // Client encrypts labData + labMetadata const encryptedLabData = encrypt(labData, userKey); const encryptedLabMetadata = encrypt({ lab: "LabCorp", doctor: "Dr. Smith" }, userKey); // Stored in MongoDB { _id: ObjectId("..."), labResultId: "lab-123", userId: "user-456", profileId: "profile-789", // Encrypted lab data (value + metadata) labData: { encrypted: true, data: "m3n4o5p6...", iv: "q7r8s9t0...", authTag: "u1v2w3x4..." }, // Encrypted lab metadata labMetadata: { encrypted: true, data: "y5z6a7b8...", iv: "c9d0e1f2...", authTag: "g3h4i5j6..." }, // Metadata (plaintext) createdAt: ISODate("2026-02-14T08:00:00Z"), updatedAt: ISODate("2026-02-14T08:00:00Z"), dataSource: "qr_code" } ``` **Encryption Notes**: - ✅ **Plaintext**: `labResultId`, `userId`, `profileId`, `familyId`, `createdAt`, `updatedAt`, `dataSource` - ❌ **Encrypted**: `labData` (value + metadata), `labMetadata` --- ### 6. Medications Collection **Purpose**: Medication tracking and reminders **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for queries) medicationId: { type: String, unique: true, required: true }, userId: { type: String, index: true, required: true }, // Owner profileId: { type: String, index: true, required: true }, // Subject familyId: { type: String, index: true }, // Encrypted medication data (value + metadata) medicationData: { encrypted: true, data: String, // Encrypted: { name: "Aspirin", dosage: "100mg", frequency: "daily", shape: "round" } iv: String, authTag: String }, // Encrypted reminder schedule reminderSchedule: { encrypted: true, data: String, // Encrypted: { times: ["08:00", "20:00"], days: ["mon", "tue", "wed", "thu", "fri"] } iv: String, authTag: String }, // Metadata (plaintext) active: { type: Boolean, default: true }, createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } } ``` **Example: Medication**: ```javascript // Client-side data structure const medicationData = { name: "Aspirin", dosage: "100mg", frequency: "daily", shape: "round", color: "white", instructions: "Take with water after meals" }; const reminderSchedule = { times: ["08:00", "20:00"], days: ["mon", "tue", "wed", "thu", "fri", "sat", "sun"], notifications: true }; // Client encrypts medicationData + reminderSchedule const encryptedMedicationData = encrypt(medicationData, userKey); const encryptedReminderSchedule = encrypt(reminderSchedule, userKey); // Stored in MongoDB { _id: ObjectId("..."), medicationId: "med-123", userId: "user-456", profileId: "profile-789", // Encrypted medication data (value + metadata) medicationData: { encrypted: true, data: "k9l0m1n2...", iv: "o3p4q5r6...", authTag: "s7t8u9v0..." }, // Encrypted reminder schedule reminderSchedule: { encrypted: true, data: "w1x2y3z4...", iv: "a5b6c7d8...", authTag: "e9f0g1h2..." }, // Metadata (plaintext) active: true, createdAt: ISODate("2026-02-14T10:00:00Z"), updatedAt: ISODate("2026-02-14T10:00:00Z") } ``` **Encryption Notes**: - ✅ **Plaintext**: `medicationId`, `userId`, `profileId`, `familyId`, `active`, `createdAt`, `updatedAt` - ❌ **Encrypted**: `medicationData` (value + metadata), `reminderSchedule` --- ### 7. Appointments Collection **Purpose**: Medical appointments and checkups **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for queries) appointmentId: { type: String, unique: true, required: true }, userId: { type: String, index: true, required: true }, // Owner profileId: { type: String, index: true, required: true }, // Subject familyId: { type: String, index: true }, // Encrypted appointment data (value + metadata) appointmentData: { encrypted: true, data: String, // Encrypted: { type: "checkup", doctor: "Dr. Smith", date: "...", notes: "..." } iv: String, authTag: String }, // Encrypted reminder settings reminderSettings: { encrypted: true, data: String, // Encrypted: { reminder: 24, unit: "hours", method: "push_notification" } iv: String, authTag: String }, // Metadata (plaintext) createdAt: { type: Date, default: Date.now }, updatedAt: { type: Date, default: Date.now } } ``` **Encryption Notes**: - ✅ **Plaintext**: `appointmentId`, `userId`, `profileId`, `familyId`, `createdAt`, `updatedAt` - ❌ **Encrypted**: `appointmentData` (value + metadata), `reminderSettings` --- ### 8. Shares Collection **Purpose**: Time-limited access to shared data (from encryption.md) **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for queries) shareId: { type: String, unique: true, required: true }, userId: { type: String, index: true, required: true }, // Owner // References to original data documentId: { type: String, required: true }, collectionName: { type: String, required: true }, // health_data, lab_results, etc. // Encrypted shared data (encrypted with share-specific password) encryptedData: { encrypted: true, data: String, // Encrypted with share-specific password iv: String, authTag: String }, // Metadata (plaintext) createdAt: { type: Date, default: Date.now }, expiresAt: { type: Date, index: true }, accessCount: { type: Number, default: 0 }, maxAccessCount: { type: Number }, // Optional: Additional security allowedEmails: [String], isRevoked: { type: Boolean, default: false }, revokedAt: Date } ``` **Encryption Notes**: - ✅ **Plaintext**: `shareId`, `userId`, `documentId`, `collectionName`, `createdAt`, `expiresAt`, `accessCount`, `maxAccessCount`, `allowedEmails`, `isRevoked`, `revokedAt` - ❌ **Encrypted**: `encryptedData` (encrypted with share-specific password) --- ### 9. Refresh Tokens Collection **Purpose**: JWT refresh token storage (from JWT authentication) **Schema**: ```javascript { _id: ObjectId("..."), // Plaintext fields (for queries) jti: { type: String, unique: true, required: true }, // JWT ID userId: { type: String, index: true, required: true }, // Metadata (plaintext) createdAt: { type: Date, default: Date.now }, expiresAt: { type: Date, index: true, required: true }, revoked: { type: Boolean, default: false }, revokedAt: Date } ``` **Encryption Notes**: - ✅ **Plaintext**: `jti`, `userId`, `createdAt`, `expiresAt`, `revoked`, `revokedAt` - ❌ **Encrypted**: None (refresh tokens are not sensitive data) --- ## Encryption Strategy ### Client-Side Encryption (Before Sending to Server) **Encryption Flow**: ```javascript // 1. User enters health data const healthData = { value: "120/80", type: "blood_pressure", unit: "mmHg", date: "2026-02-14T10:30:00Z" }; // 2. Client derives encryption key from password const userKey = await deriveKeyFromPassword(userPassword); // PBKDF2: 100,000 iterations, SHA-256, 32-byte key // 3. Client encrypts health data const encryptedHealthData = await encryptData(healthData, userKey); // AES-256-GCM: 16-byte IV, auth tag for integrity // 4. Client sends encrypted data to server await fetch('/api/health-data', { method: 'POST', body: JSON.stringify({ userId: 'user-123', profileId: 'profile-456', familyId: 'family-789', healthData: [encryptedHealthData] // Encrypted (value + metadata) }) }); // 5. Server stores encrypted data in MongoDB // Server NEVER decrypts data ``` ### Encryption Implementation (Client-Side) **React Native / React**: ```typescript import * as crypto from 'crypto'; // Encrypted field structure interface EncryptedField { encrypted: true; data: string; // Encrypted data iv: string; // Initialization vector authTag: string; // Authentication tag (AES-256-GCM) } // Encrypt data async function encryptData(data: any, key: Buffer): Promise { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex'); encrypted += cipher.final('hex'); const authTag = cipher.getAuthTag(); return { encrypted: true, data: encrypted, iv: iv.toString('hex'), authTag: authTag.toString('hex') }; } // Decrypt data async function decryptData(encryptedField: EncryptedField, key: Buffer): Promise { const decipher = crypto.createDecipheriv( 'aes-256-gcm', key, Buffer.from(encryptedField.iv, 'hex') ); decipher.setAuthTag(Buffer.from(encryptedField.authTag, 'hex')); let decrypted = decipher.update(encryptedField.data, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return JSON.parse(decrypted); } // Derive key from password async function deriveKeyFromPassword(password: string): Promise { const salt = crypto.randomBytes(32); return new Promise((resolve, reject) => { crypto.pbkdf2(password, salt, 100000, 32, 'sha256', (err, derivedKey) => { if (err) reject(err); else resolve(derivedKey); }); }); } ``` --- ## Indexing Strategy ### Principle **Index ONLY plaintext fields** (for performance and privacy). ### Indexes per Collection #### Users ```javascript db.users.createIndex({ userId: 1 }, { unique: true }); db.users.createIndex({ email: 1 }, { unique: true }); db.users.createIndex({ familyId: 1 }); db.users.createIndex({ createdAt: -1 }); ``` #### Families ```javascript db.families.createIndex({ familyId: 1 }, { unique: true }); db.families.createIndex({ createdAt: -1 }); ``` #### Profiles ```javascript db.profiles.createIndex({ profileId: 1 }, { unique: true }); db.profiles.createIndex({ userId: 1 }); db.profiles.createIndex({ familyId: 1 }); db.profiles.createIndex({ createdAt: -1 }); ``` #### Health Data ```javascript db.health_data.createIndex({ healthDataId: 1 }, { unique: true }); db.health_data.createIndex({ userId: 1 }); db.health_data.createIndex({ profileId: 1 }); db.health_data.createIndex({ familyId: 1 }); db.health_data.createIndex({ createdAt: -1 }); db.health_data.createIndex({ updatedAt: -1 }); ``` #### Lab Results ```javascript db.lab_results.createIndex({ labResultId: 1 }, { unique: true }); db.lab_results.createIndex({ userId: 1 }); db.lab_results.createIndex({ profileId: 1 }); db.lab_results.createIndex({ familyId: 1 }); db.lab_results.createIndex({ createdAt: -1 }); db.lab_results.createIndex({ updatedAt: -1 }); ``` #### Medications ```javascript db.medications.createIndex({ medicationId: 1 }, { unique: true }); db.medications.createIndex({ userId: 1 }); db.medications.createIndex({ profileId: 1 }); db.medications.createIndex({ familyId: 1 }); db.medications.createIndex({ active: 1 }); db.medications.createIndex({ createdAt: -1 }); db.medications.createIndex({ updatedAt: -1 }); ``` #### Appointments ```javascript db.appointments.createIndex({ appointmentId: 1 }, { unique: true }); db.appointments.createIndex({ userId: 1 }); db.appointments.createIndex({ profileId: 1 }); db.appointments.createIndex({ familyId: 1 }); db.appointments.createIndex({ createdAt: -1 }); db.appointments.createIndex({ updatedAt: -1 }); ``` #### Shares ```javascript db.shares.createIndex({ shareId: 1 }, { unique: true }); db.shares.createIndex({ userId: 1 }); db.shares.createIndex({ expiresAt: 1 }); // For TTL index db.shares.createIndex({ createdAt: -1 }); db.shares.createIndex({ isRevoked: 1 }); ``` #### Refresh Tokens ```javascript db.refresh_tokens.createIndex({ jti: 1 }, { unique: true }); db.refresh_tokens.createIndex({ userId: 1 }); db.refresh_tokens.createIndex({ expiresAt: 1 }); // For TTL index db.refresh_tokens.createIndex({ revoked: 1 }); ``` ### TTL Indexes (Auto-Expiration) ```javascript // Shares: Auto-delete expired shares db.shares.createIndex( { expiresAt: 1 }, { expireAfterSeconds: 0 } // Delete immediately after expiration ); // Refresh Tokens: Auto-delete expired tokens db.refresh_tokens.createIndex( { expiresAt: 1 }, { expireAfterSeconds: 0 } // Delete immediately after expiration ); ``` --- ## Privacy-Preserving Queries ### Challenge **How to query encrypted data without decrypting it?** ### Solutions #### 1. Plaintext Queries (Recommended) **Query by plaintext fields only**: ```javascript // GOOD: Query by plaintext fields const healthData = await db.health_data.find({ userId: 'user-123', // Plaintext ✅ profileId: 'profile-456', // Plaintext ✅ familyId: 'family-789' // Plaintext ✅ }).toArray(); // Client decrypts healthData[i].healthData[j] ``` #### 2. Tagging System (Encrypted Search) **Client adds searchable tags to encrypted data**: ```javascript // Client adds tags to encrypted data const healthData = { value: "120/80", type: "blood_pressure", unit: "mmHg", date: "2026-02-14T10:30:00Z", tags: ["cardio", "daily"] // Plaintext tags (for client-side search) }; // Stored in MongoDB { _id: ObjectId("..."), healthDataId: "health-123", userId: "user-123", profileId: "profile-456", // Encrypted health data healthData: [ { encrypted: true, data: "a1b2c3d4...", iv: "e5f6g7h8...", authTag: "i9j0k1l2..." } ], // Plaintext tags (for client-side search) tags: ["cardio", "daily"] } // Query by tags const healthData = await db.health_data.find({ userId: 'user-123', tags: { $in: ['cardio', 'daily'] } // Plaintext tags ✅ }).toArray(); // Client decrypts healthData[i].healthData[j] ``` #### 3. Date Range Queries (Plaintext Dates) **Store dates as plaintext** (for range queries): ```javascript // Client encrypts health data BUT stores date as plaintext const healthData = { value: "120/80", type: "blood_pressure", unit: "mmHg", date: "2026-02-14T10:30:00Z" // Plaintext date ✅ }; // Stored in MongoDB { _id: ObjectId("..."), healthDataId: "health-123", userId: "user-123", profileId: "profile-456", // Plaintext date (for range queries) date: ISODate("2026-02-14T10:30:00Z"), // Plaintext ✅ // Encrypted health data (without date) healthData: [ { encrypted: true, data: "a1b2c3d4...", // Encrypted: { value, type, unit } (no date) iv: "e5f6g7h8...", authTag: "i9j0k1l2..." } ] } // Query by date range const healthData = await db.health_data.find({ userId: 'user-123', date: { $gte: ISODate("2026-02-01T00:00:00Z"), $lte: ISODate("2026-02-28T23:59:59Z") } }).toArray(); // Client decrypts healthData[i].healthData[j] ``` --- ## Data Migration ### Key Rotation **Strategy**: Re-encrypt all data with new key ```javascript // 1. User changes password const newPassword = "new-secure-password"; const newKey = await deriveKeyFromPassword(newPassword); // 2. Client fetches all encrypted data const healthData = await db.health_data.find({ userId: 'user-123' }).toArray(); // 3. Client decrypts with old key const decryptedHealthData = healthData.map(d => ({ _id: d._id, decrypted: await decryptData(d.healthData[0], oldKey) })); // 4. Client re-encrypts with new key const reencryptedHealthData = decryptedHealthData.map(d => ({ _id: d._id, encrypted: await encryptData(d.decrypted, newKey) })); // 5. Client sends re-encrypted data to server for (const d of reencryptedHealthData) { await db.health_data.updateOne( { _id: d._id }, { $set: { healthData: [d.encrypted], updatedAt: new Date() } } ); } ``` --- ## Performance Considerations ### 1. Encryption Overhead - **Client-side encryption**: Minimal (10-50ms per encryption) - **Server-side storage**: No overhead (encrypted data stored directly) - **Network transfer**: Encrypted data is 20-30% larger than plaintext ### 2. Index Size - **Plaintext indexes**: Smaller (only plaintext fields) - **Encrypted data**: Not indexed (no performance impact) ### 3. Query Performance - **Plaintext queries**: Fast (indexed fields) - **Tag-based queries**: Fast (indexed plaintext tags) - **Date range queries**: Fast (indexed plaintext dates) - **Encrypted data queries**: Not possible (client-side filtering) ### 4. Storage Size - **Encrypted data**: 20-30% larger than plaintext - **MongoDB storage**: No impact (stores binary data) --- ## Summary ### Zero-Knowledge Encryption - ✅ **Client-side encryption**: All sensitive data encrypted before reaching server - ✅ **Metadata encryption**: Health data metadata (type, unit, etc.) also encrypted - ✅ **Plaintext queries**: Query by plaintext fields (userId, profileId, familyId, date, tags) - ✅ **Server blindness**: Server stores ONLY encrypted data, never decrypts ### Collections - ✅ **Users**: Authentication, profiles, family relationships - ✅ **Families**: Family structure, encrypted family name/metadata - ✅ **Profiles**: Person profiles, encrypted profile name/metadata - ✅ **Health Data**: Encrypted health records (value + metadata) - ✅ **Lab Results**: Encrypted lab data (value + metadata) - ✅ **Medications**: Encrypted medication data + reminders - ✅ **Appointments**: Encrypted appointment data + reminders - ✅ **Shares**: Time-limited access to shared data - ✅ **Refresh Tokens**: JWT refresh token storage ### Privacy Preserved - ✅ **Blood pressure**: Value + type + unit + date encrypted - ✅ **HIV test**: Test type + result + date + doctor encrypted - ✅ **Cholesterol**: Test type + result + date + lab encrypted - ✅ **All health data**: Value + metadata encrypted --- ## Next Steps 1. Create MongoDB indexes 2. Implement client-side encryption (React Native + React) 3. Implement server-side API (Axum + MongoDB) 4. Test encryption flow 5. Test data migration (key rotation) 6. Test privacy-preserving queries 7. Performance testing --- ## References - [Normogen Encryption Guide](../encryption.md) - [JWT Authentication Research](./2026-02-14-jwt-authentication-research.md) - [Technology Stack Decisions](./2026-02-14-tech-stack-decision.md) - [MongoDB Documentation](https://docs.mongodb.com/) - [AES-256-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) - [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2)