# 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<EncryptedField> {
  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<any> {
  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<Buffer> {
  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)
