# ByteLyst — End-to-End Encryption & Data Protection Design

> **Purpose:** Comprehensive encryption strategy for all ByteLyst ecosystem repos and components.  
> **Status:** Design document — not yet implemented  
> **Author:** AI Architecture Review  
> **Last updated:** 2026-03-21  
> **See also:** [`AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md`](AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md), [`END_TO_END_ENCRYPTION_ROADMAP.md`](END_TO_END_ENCRYPTION_ROADMAP.md)

---

## 1. Executive Summary

The ByteLyst ecosystem spans 10 products, 3 shared services, 15 repos, and 5 client surfaces (Web, iOS, Android, macOS, watchOS). Data flows through Cosmos DB, Azure Blob Storage, SQLite (local), and in-transit via HTTPS.

This document defines a **tiered encryption strategy** that balances security, performance, and developer experience:

| Tier       | Scope                                         | Threat Model                                     | Effort         |
| ---------- | --------------------------------------------- | ------------------------------------------------ | -------------- |
| **Tier 0** | Infrastructure encryption (already done)      | DB breach at rest, network sniffing              | N/A (complete) |
| **Tier 1** | Application-layer field encryption            | DB admin access, backup leaks, insider threat    | Medium         |
| **Tier 2** | Client-side encryption with server decryption | Reduced server-side exposure window              | High           |
| **Tier 3** | True E2EE (zero-knowledge)                    | Server compromise, legal subpoena, full distrust | Very High      |

**Recommendation:** Implement Tier 1 ecosystem-wide via a new `@bytelyst/field-encrypt` package. Offer Tier 3 as opt-in "Private Vaults" for JarvisJr and NoteLett only.

---

## 2. Current State Audit

### 2.1 What We Already Have (Tier 0 — Infrastructure)

| Layer                             | Protection                       | Status                              |
| --------------------------------- | -------------------------------- | ----------------------------------- |
| **Cosmos DB at rest**             | AES-256 (Microsoft-managed keys) | ✅ Enabled by default               |
| **Cosmos DB in transit**          | TLS 1.2+                         | ✅ Enforced                         |
| **Azure Blob Storage at rest**    | AES-256 (Microsoft-managed keys) | ✅ Enabled by default               |
| **Azure Blob Storage in transit** | TLS 1.2+                         | ✅ Enforced                         |
| **SQLite (LocalMemGPT)**          | No encryption (local disk)       | ⚠️ Gap                              |
| **Azure Key Vault**               | HSM-backed secret storage        | ✅ 30 secrets managed               |
| **JWT tokens**                    | HS256 signed (RS256 planned)     | ✅ Active                           |
| **HTTPS**                         | TLS 1.2+ on all endpoints        | ✅ Enforced                         |
| **iOS Keychain**                  | Hardware-backed (Secure Enclave) | ✅ via `BLKeychain`                 |
| **Android Keystore**              | Hardware-backed (TEE/StrongBox)  | ✅ via `BLSecureStore` (AES256-GCM) |
| **Secret scanning**               | Husky pre-commit + pre-push      | ✅ Active                           |

### 2.2 Existing Application-Layer Encryption

The only field-level encryption today is in SmartAuth MFA:

```
platform-service/src/modules/auth/mfa/repository.ts
├── encryptSecret()  — AES-256-GCM, key from AUTH_TOTP_ENCRYPTION_KEY (hex in AKV)
├── decryptSecret()  — AES-256-GCM with IV + authTag verification
├── hashRecoveryCode() — SHA-256 one-way hash
└── generateRecoveryCodes() — crypto.randomBytes
```

**Pattern:** `{ encrypted: string, iv: string, authTag: string }` stored alongside the document. This is the pattern we'll generalize into `@bytelyst/field-encrypt`.

### 2.3 Client-Side Secure Storage

| Platform    | Component                         | Encryption                                                      | Key Storage              |
| ----------- | --------------------------------- | --------------------------------------------------------------- | ------------------------ |
| **iOS**     | `BLKeychain`                      | Keychain Services (AES-256)                                     | Secure Enclave           |
| **Android** | `BLSecureStore`                   | EncryptedSharedPreferences (AES256-SIV keys, AES256-GCM values) | Android Keystore         |
| **Web**     | `localStorage` / `sessionStorage` | None                                                            | ⚠️ Gap — no encryption   |
| **macOS**   | `BLKeychain` (shared with iOS)    | Keychain Services                                               | Secure Enclave / T2 chip |
| **watchOS** | `BLKeychain` (shared with iOS)    | Keychain Services                                               | Secure Enclave           |

---

## 3. Threat Model

### 3.1 Threats by Severity

| #   | Threat                                             | Likelihood | Impact   | Current Mitigation                                       | Gap                                                        |
| --- | -------------------------------------------------- | ---------- | -------- | -------------------------------------------------------- | ---------------------------------------------------------- |
| T1  | **Database breach** (Cosmos DB credentials leaked) | Medium     | Critical | AKV, secret rotation runbooks, Cosmos encryption at rest | No app-layer encryption — leaked key exposes all plaintext |
| T2  | **Backup/export data leak**                        | Medium     | High     | N/A                                                      | Backups contain plaintext — no field-level encryption      |
| T3  | **Insider threat** (DB admin reads data)           | Low        | High     | AKV access policies, RBAC                                | Cosmos data readable with any valid key                    |
| T4  | **Man-in-the-middle**                              | Low        | High     | TLS 1.2+ everywhere                                      | ✅ Covered                                                 |
| T5  | **Client device theft**                            | Medium     | Medium   | iOS Keychain, Android Keystore                           | Web localStorage is plaintext                              |
| T6  | **Server compromise**                              | Low        | Critical | AKV, managed identities                                  | Server can decrypt everything — no zero-knowledge          |
| T7  | **Legal subpoena / government request**            | Low        | High     | N/A                                                      | Server holds all keys — must comply                        |
| T8  | **Cross-product data leak**                        | Low        | Medium   | `productId` field isolation                              | No cryptographic isolation between products                |
| T9  | **SQLite local DB theft** (LocalMemGPT)            | Low        | Medium   | OS-level file permissions                                | SQLite has no encryption                                   |

### 3.2 What Each Tier Addresses

| Threat                | Tier 0 (Current) | Tier 1 (Field Encrypt) | Tier 2 (Server Decrypt) | Tier 3 (True E2EE) |
| --------------------- | :--------------: | :--------------------: | :---------------------: | :----------------: |
| T1 Database breach    |     Partial      |           ✅           |           ✅            |         ✅         |
| T2 Backup leak        |        ❌        |           ✅           |           ✅            |         ✅         |
| T3 Insider threat     |        ❌        |        Partial         |           ✅            |         ✅         |
| T4 MITM               |        ✅        |           ✅           |           ✅            |         ✅         |
| T5 Device theft       |     Partial      |        Partial         |           ✅            |         ✅         |
| T6 Server compromise  |        ❌        |           ❌           |           ❌            |         ✅         |
| T7 Legal subpoena     |        ❌        |           ❌           |           ❌            |         ✅         |
| T8 Cross-product leak |        ❌        |           ✅           |           ✅            |         ✅         |
| T9 SQLite theft       |        ❌        |           ❌           |           ✅            |         ✅         |

---

## 4. Tier 1 — Application-Layer Field Encryption

### 4.1 Architecture

```
┌─────────────┐         ┌──────────────────┐         ┌─────────────┐
│   Client     │──HTTPS──│  Backend Service  │──TLS──→│  Cosmos DB   │
│ (Web/iOS/    │         │  (Fastify 5)      │         │              │
│  Android)    │         │                    │         │  Stores:     │
└─────────────┘         │  encrypt(field)    │         │  { encrypted,│
                        │  before write      │         │    iv,       │
                        │                    │         │    authTag } │
                        │  decrypt(field)    │         │              │
                        │  after read        │         │              │
                        └──────────────────┘         └─────────────┘
                                │
                                │ DEK wrapped by MEK
                                ▼
                        ┌──────────────────┐
                        │  Azure Key Vault  │
                        │  (MEK storage)    │
                        └──────────────────┘
```

### 4.2 Key Hierarchy

```
Master Encryption Key (MEK)
├── Stored in Azure Key Vault (HSM-backed)
├── Never leaves AKV — used only for wrapping/unwrapping DEKs
├── One MEK per product (e.g., lysnr-mek, mindlyst-mek, jarvisjr-mek)
└── Rotation: quarterly, with old MEK retained for decryption

    └── Data Encryption Keys (DEKs)
        ├── Generated per-user or per-workspace
        ├── AES-256-GCM symmetric key (32 bytes)
        ├── Wrapped (encrypted) by MEK before storage
        ├── Stored alongside data in Cosmos (as wrapped blob)
        └── Cached in memory for session duration (max 15 min TTL)
```

**Why envelope encryption (MEK → DEK)?**

- MEK rotation doesn't require re-encrypting all data — just re-wrap the DEKs
- Per-user DEKs provide cryptographic isolation between users
- DEK caching avoids an AKV round-trip on every read/write
- AKV rate limits (5,000 ops/10s) are not hit because DEK unwrap is infrequent

### 4.3 `@bytelyst/field-encrypt` Package Design

```
packages/field-encrypt/
├── src/
│   ├── index.ts              # Public API exports
│   ├── types.ts              # EncryptedField, FieldEncryptOptions, KeyProvider
│   ├── aes-gcm.ts            # AES-256-GCM encrypt/decrypt (Node.js crypto)
│   ├── envelope.ts           # DEK generation, MEK wrapping/unwrapping
│   ├── key-provider-akv.ts   # Azure Key Vault key provider (production)
│   ├── key-provider-env.ts   # Env-var key provider (dev/test — like current MFA)
│   ├── key-provider-memory.ts # In-memory key provider (unit tests)
│   ├── key-cache.ts          # TTL cache for unwrapped DEKs
│   ├── migration.ts          # Helpers for encrypting existing plaintext fields
│   └── index.test.ts         # Tests
├── package.json
└── tsconfig.json
```

### 4.4 API Design

```typescript
// ── Setup (once per service) ──────────────────────────────
import { createFieldEncryptor } from '@bytelyst/field-encrypt';

const encryptor = createFieldEncryptor({
  keyProvider: 'akv', // 'akv' | 'env' | 'memory'
  keyVaultUrl: process.env.AZURE_KEYVAULT_URL,
  mekName: 'lysnr-mek', // product-specific MEK in AKV
  dekCacheTtlMs: 15 * 60 * 1000, // 15 min DEK cache
});

// ── Encrypt a field before writing to Cosmos ──────────────
const encrypted = await encryptor.encrypt('Hello, this is a transcript', {
  userId: 'user_123', // DEK scope
  context: 'transcripts', // additional authenticated data (AAD)
});
// Returns: { ciphertext: string, iv: string, authTag: string, dekId: string, version: 1 }

// ── Decrypt a field after reading from Cosmos ─────────────
const plaintext = await encryptor.decrypt(encrypted, {
  userId: 'user_123',
  context: 'transcripts',
});
// Returns: 'Hello, this is a transcript'

// ── Batch encrypt/decrypt (optimized — single DEK unwrap) ─
const results = await encryptor.encryptBatch(fields, { userId, context });
const plaintexts = await encryptor.decryptBatch(encryptedFields, { userId, context });

// ── Re-wrap DEKs after MEK rotation ──────────────────────
await encryptor.rewrapDeks({ oldMekName: 'lysnr-mek-v1', newMekName: 'lysnr-mek-v2' });

// ── Migration: encrypt existing plaintext in-place ────────
await encryptor.migrateField(cosmosContainer, {
  fieldName: 'transcriptText',
  partitionKey: '/userId',
  batchSize: 100,
  dryRun: false,
});
```

### 4.5 Encrypted Document Schema

Before encryption:

```json
{
  "id": "transcript_abc123",
  "userId": "user_123",
  "productId": "lysnrai",
  "transcriptText": "Hello, this is a very sensitive transcript...",
  "createdAt": "2026-03-21T08:00:00Z"
}
```

After encryption:

```json
{
  "id": "transcript_abc123",
  "userId": "user_123",
  "productId": "lysnrai",
  "transcriptText": {
    "__encrypted": true,
    "v": 1,
    "alg": "aes-256-gcm",
    "ct": "a1b2c3d4e5f6...",
    "iv": "0102030405060708090a0b0c",
    "tag": "f1e2d3c4b5a6...",
    "dekId": "dek_user_123_transcripts"
  },
  "createdAt": "2026-03-21T08:00:00Z"
}
```

**Key design decisions:**

- `__encrypted: true` sentinel allows code to detect encrypted vs plaintext fields during migration
- `v: 1` version field enables future algorithm changes without re-encryption
- `dekId` identifies which DEK to unwrap — enables per-user key isolation
- Non-sensitive fields (`userId`, `productId`, `createdAt`) remain in plaintext for querying
- Cosmos DB queries on encrypted fields are impossible by design — use indexed plaintext metadata fields for filtering
- For array fields (e.g., JarvisJr `transcript[]`, MindLyst `repeatedThemes[]`), JSON-serialize the entire array before encryption, producing a single `EncryptedField` object. On decrypt, JSON-parse back to the original array.

> **¹ Array field encryption:** Fields marked with ¹ in §4.6 are arrays or structs. The pattern is: `JSON.stringify(array)` → encrypt → store as `EncryptedField`. On read: decrypt → `JSON.parse()` → typed array. This keeps the schema simple (one encrypted blob per field) at the cost of losing per-element access.

### 4.6 Fields to Encrypt by Product

| Product         | Container          | Field(s) to Encrypt                                                             | Sensitivity | Priority        |
| --------------- | ------------------ | ------------------------------------------------------------------------------- | ----------- | --------------- |
| **LysnrAI**     | `transcripts`      | `rawText`, `cleanedText`                                                        | Critical    | P0              |
| **LysnrAI**     | `sessions`         | (no sensitive free-text fields)                                                 | —           | Skip            |
| **JarvisJr**    | `jarvis_sessions`  | `transcript` (array¹), `coachingNotes` (array¹)                                 | Critical    | P0              |
| **JarvisJr**    | `jarvis_memory`    | `content`                                                                       | Critical    | P0              |
| **NoteLett**    | `notes`            | `body`                                                                          | Critical    | P0              |
| **NoteLett**    | `note_artifacts`   | `content`                                                                       | High        | P1              |
| **MindLyst**    | `memory_items`     | `content`, `voiceTranscriptText`                                                | Critical    | P0              |
| **MindLyst**    | `brains`           | `description`                                                                   | Medium      | P2              |
| **MindLyst**    | `reflections`      | `repeatedThemes`, `suggestedAdjustments`, `vsLastWeek.summary` (arrays/struct¹) | High        | P1              |
| **NomGap**      | `meal_logs`        | `notes`                                                                         | Low         | P3              |
| **NomGap**      | `fasting_sessions` | `notes`, `moodCheckins[].notes`                                                 | Low         | P3              |
| **NomGap**      | `weight_logs`      | `notes`                                                                         | Low         | P3              |
| **ChronoMind**  | `timers`           | (none — low sensitivity)                                                        | Low         | Defer           |
| **PeakPulse**   | `peak_sessions`    | (none — GPS/stats not sensitive)                                                | Low         | Defer           |
| **FlowMonk**    | `tasks`            | `description`                                                                   | Low         | P3              |
| **ActionTrail** | `trail_actions`    | `before`, `after`                                                               | Medium      | P2              |
| **ActionTrail** | `trail_reverts`    | `beforeState`, `afterState`                                                     | Medium      | P2              |
| **LocalMemGPT** | SQLite `messages`  | `content`                                                                       | High        | P1              |
| **SmartAuth**   | `auth_mfa`         | `encryptedSecret`                                                               | Critical    | ✅ Already done |

### 4.7 Query Impact Analysis

**Fields that are encrypted cannot be queried via Cosmos SQL.** This impacts:

| Product     | Current Query Pattern      | Mitigation                                                                                   |
| ----------- | -------------------------- | -------------------------------------------------------------------------------------------- |
| NoteLett    | FTS on `body`              | Maintain plaintext search index (separate collection) or use extraction-service              |
| LocalMemGPT | FTS5 on `content`          | FTS5 index operates on plaintext — encrypt at SQLite column level with `sqlcipher`           |
| MindLyst    | Search on `content`        | Use extraction-service for entity extraction; store extracted entities as plaintext metadata |
| LysnrAI     | Search on `transcriptText` | Same: extraction → plaintext keywords                                                        |

**Pattern:** Extract searchable metadata before encryption, store as plaintext alongside encrypted content.

---

## 5. Tier 2 — Client-Side Encryption with Server Decryption

### 5.1 When to Use

Tier 2 reduces the **exposure window** — data is encrypted on the client, transmitted encrypted, and only decrypted on the server when actively needed (e.g., AI processing). At rest, it's stored encrypted.

This is relevant for:

- **JarvisJr voice sessions** — encrypt audio/transcript on-device, decrypt only during PromptBuilder execution
- **LysnrAI dictation** — encrypt transcript on-device, decrypt only during cloud processing
- **LocalMemGPT RAG** — encrypt documents on upload, decrypt only during embedding generation

### 5.2 Architecture

```
┌───────────────┐                  ┌──────────────────┐              ┌───────────┐
│    Client      │    encrypted     │    Backend        │   encrypted  │ Cosmos DB  │
│                │───payload───────→│                   │─────────────→│            │
│  1. Generate   │                  │  2. Store as-is   │              │ (encrypted │
│     session    │                  │     (no decrypt)  │              │  at rest + │
│     key (SK)   │                  │                   │              │  at app    │
│  3. Encrypt    │                  │  4. When AI       │              │  layer)    │
│     with SK    │                  │     needed:       │              │            │
│  5. Send SK    │                  │     unwrap SK     │              │            │
│     wrapped    │                  │     from AKV,     │              │            │
│     by MEK     │                  │     decrypt,      │              │            │
│                │                  │     process,      │              │            │
│                │                  │     re-encrypt    │              │            │
└───────────────┘                  └──────────────────┘              └───────────┘
```

### 5.3 Client-Side Encryption Primitives

| Platform    | API                             | Algorithm         |
| ----------- | ------------------------------- | ----------------- |
| **Web**     | `SubtleCrypto` (Web Crypto API) | AES-256-GCM       |
| **iOS**     | `CryptoKit` (`AES.GCM`)         | AES-256-GCM       |
| **Android** | `javax.crypto.Cipher`           | AES/GCM/NoPadding |
| **Node.js** | `node:crypto`                   | AES-256-GCM       |

All platforms support AES-256-GCM natively — no external dependencies needed.

### 5.4 New SDK Components

| SDK                        | Component                 | Purpose                                                                                               |
| -------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------- |
| `@bytelyst/client-encrypt` | `createClientEncryptor()` | Web + React Native client-side encryption (separate package — uses Web Crypto API, not `node:crypto`) |
| `swift-platform-sdk`       | `BLFieldEncrypt`          | iOS/macOS/watchOS client-side encryption                                                              |
| `kotlin-platform-sdk`      | `BLFieldEncrypt`          | Android/Wear OS client-side encryption                                                                |

---

## 6. Tier 3 — True End-to-End Encryption (Zero-Knowledge)

### 6.1 When to Use

True E2EE means the **server never sees plaintext** and **cannot decrypt** the data. This is the Signal/ProtonMail model.

**Applicable products:** JarvisJr (opt-in private agents), NoteLett (opt-in private workspaces).

**NOT applicable to:**

- Products requiring server-side AI (NomGap coaching, MindLyst triage, FlowMonk scheduling)
- Products requiring server-side search (LocalMemGPT FTS5)
- Products where the server IS the authority (ActionTrail oversight, PeakPulse leaderboards)

### 6.2 Architecture

```
┌───────────────┐                  ┌──────────────────┐              ┌───────────┐
│    Client      │    encrypted     │    Backend        │   encrypted  │ Cosmos DB  │
│                │───payload───────→│                   │─────────────→│            │
│  1. Generate   │                  │  2. Store as-is   │              │ Ciphertext │
│     user       │                  │     (CANNOT       │              │ only —     │
│     keypair    │                  │      decrypt)     │              │ no keys    │
│  2. Encrypt    │                  │                   │              │            │
│     with       │                  │  3. Return        │              │            │
│     user's     │◀──ciphertext────│     ciphertext    │              │            │
│     public key │                  │     to client     │              │            │
│  3. Decrypt    │                  │                   │              │            │
│     with       │                  │  NO MEK/DEK       │              │            │
│     private    │                  │  access at all    │              │            │
│     key        │                  │                   │              │            │
└───────────────┘                  └──────────────────┘              └───────────┘
     │
     │ Private key stored in:
     │ - iOS Keychain (Secure Enclave)
     │ - Android Keystore (StrongBox)
     │ - Web: derived from passphrase (PBKDF2)
     │
     │ Multi-device sync via:
     │ - iCloud Keychain (iOS/Mac)
     │ - Google Backup (Android)
     │ - QR code transfer (cross-platform)
```

### 6.3 Key Management Challenges

| Challenge                | Solution                                                            | Complexity |
| ------------------------ | ------------------------------------------------------------------- | ---------- |
| **Key loss = data loss** | Recovery phrase (BIP-39 mnemonic, 12 words)                         | Medium     |
| **Multi-device sync**    | Platform keychain sync (iCloud/Google) + QR fallback                | High       |
| **Cross-platform**       | Export keypair via QR code / encrypted backup file                  | High       |
| **Key rotation**         | Re-encrypt all data with new key — expensive                        | High       |
| **Shared workspaces**    | Group key distribution (like Signal group protocol)                 | Very High  |
| **Search**               | Client-side search only (download + decrypt + search)               | Medium     |
| **AI processing**        | Not possible — would need homomorphic encryption or secure enclaves | Blocker    |

### 6.4 Trade-offs

| Feature                     | With E2EE                            | Without E2EE                 |
| --------------------------- | ------------------------------------ | ---------------------------- |
| Server-side AI              | ❌ Impossible                        | ✅ Works                     |
| Server-side search          | ❌ Impossible                        | ✅ Works                     |
| MCP tool access             | ❌ Impossible                        | ✅ Works                     |
| Data recovery (lost device) | ⚠️ Recovery phrase only              | ✅ Server can restore        |
| Multi-device sync           | ⚠️ Complex key distribution          | ✅ Automatic                 |
| Regulatory compliance       | ✅ Strong (GDPR Art. 32)             | ⚠️ Depends on other controls |
| Marketing / trust           | ✅ "Zero-knowledge encryption" claim | ⚠️ Standard                  |

---

## 7. SQLite Encryption (LocalMemGPT)

LocalMemGPT uses SQLite as its primary datastore. SQLite databases are unencrypted files on disk.

### 7.1 Options

| Option                | Library                          | License             | Effort                    |
| --------------------- | -------------------------------- | ------------------- | ------------------------- |
| **SQLCipher**         | `better-sqlite3-sqlcipher`       | BSD                 | Low — drop-in replacement |
| **SEE**               | SQLite Encryption Extension      | Commercial ($2,000) | Low                       |
| **Application-level** | Encrypt each field before INSERT | N/A                 | Medium                    |

**Recommendation:** Use `better-sqlite3` with application-level field encryption via `@bytelyst/field-encrypt` (env key provider). This avoids a native dependency change and is consistent with the Cosmos DB approach.

### 7.2 FTS5 Impact

If message `content` is encrypted, FTS5 cannot index it. Options:

1. **Plaintext FTS5 index** — Store plaintext in FTS5 virtual table, encrypted in main table. Risk: FTS5 table is also on disk unencrypted.
2. **Client-side search** — Download + decrypt + search in memory. Acceptable for local-first app.
3. **SQLCipher** — Encrypts entire database file. FTS5 works normally. Best option if willing to change native dep.

---

## 8. Web Client Secure Storage

Web `localStorage` stores tokens and app state in plaintext. Options:

| Approach                  | Protection                           | Complexity             |
| ------------------------- | ------------------------------------ | ---------------------- |
| **IndexedDB + CryptoKey** | AES-256-GCM with non-extractable key | Medium                 |
| **Session-only storage**  | Data lost on tab close               | Low                    |
| **Cookie httpOnly**       | Server-managed, XSS-proof            | Low (auth tokens only) |

**Recommendation:**

- Auth tokens → httpOnly cookies (already planned in SmartAuth RS256 migration)
- Sensitive cached data → IndexedDB with `SubtleCrypto` non-extractable key
- Non-sensitive state → `localStorage` as-is

---

## 9. Blob Storage Encryption

Azure Blob Storage (`bytelystblobs`) stores audio files, meal photos, GPX exports, document uploads. Currently encrypted at rest by Azure (Microsoft-managed keys).

### 9.1 Upgrade Path

| Level       | Method                               | Impact                                       |
| ----------- | ------------------------------------ | -------------------------------------------- |
| **Current** | Azure-managed encryption at rest     | Server-side transparent                      |
| **Level 1** | Customer-managed key (CMK) in AKV    | Control over key lifecycle; no code changes  |
| **Level 2** | Client-side encryption before upload | Blobs encrypted before leaving client device |

**Recommendation:** Enable CMK (Level 1) for the `bytelystblobs` storage account. This is a portal-only change with zero code impact and gives us key rotation control.

For Level 2, the existing `BLBlobClient` (Swift/Kotlin) and `@bytelyst/blob-client` (TS) would need an encrypt-before-upload wrapper — this naturally integrates with Tier 2.

---

## 10. Cross-Cutting Concerns

### 10.1 Performance Impact

| Operation                          | Overhead             | Mitigation                             |
| ---------------------------------- | -------------------- | -------------------------------------- |
| AES-256-GCM encrypt (1 KB field)   | ~0.01 ms (Node.js)   | Negligible                             |
| AES-256-GCM encrypt (100 KB field) | ~0.1 ms              | Negligible                             |
| DEK unwrap via AKV                 | ~50-200 ms           | DEK cache (15 min TTL)                 |
| MEK rotation (re-wrap DEKs)        | ~50 ms/DEK × N users | Background job, non-blocking           |
| Migration (encrypt existing data)  | ~100 docs/sec        | Batch migration with progress tracking |

### 10.2 Compliance Mapping

| Regulation                | Requirement                                | Tier 0  | Tier 1  | Tier 3  |
| ------------------------- | ------------------------------------------ | :-----: | :-----: | :-----: |
| **GDPR Art. 32**          | Encryption of personal data                | Partial |   ✅    |   ✅    |
| **GDPR Art. 34**          | Breach notification exemption if encrypted |   ❌    |   ✅    |   ✅    |
| **HIPAA** (if applicable) | PHI encryption at rest + in transit        | Partial |   ✅    |   ✅    |
| **SOC 2 Type II**         | Encryption controls                        | Partial |   ✅    |   ✅    |
| **Apple App Store**       | Encryption disclosure (ERN)                |   N/A   | Declare | Declare |
| **Google Play**           | Encryption disclosure                      |   N/A   | Declare | Declare |

**GDPR Art. 34 exemption is the key business driver** — if data is encrypted and keys are not compromised, breach notification to individual users is not required. This significantly reduces breach liability.

### 10.3 Key Rotation Strategy

| Key Type         | Rotation Frequency   | Process                                                        | Downtime                            |
| ---------------- | -------------------- | -------------------------------------------------------------- | ----------------------------------- |
| MEK (in AKV)     | Quarterly            | Create new MEK version → re-wrap DEKs → deactivate old version | Zero (old MEK retained for decrypt) |
| DEK (per-user)   | On demand / annually | Generate new DEK → re-encrypt fields → delete old DEK          | Zero (background migration)         |
| SQLite key (env) | On app update        | Re-encrypt database with new key                               | Brief (migration on startup)        |

### 10.4 Monitoring & Alerting

| Event                          | Source              | Alert    |
| ------------------------------ | ------------------- | -------- |
| MEK access from unknown IP     | AKV diagnostic logs | Critical |
| Decryption failure spike       | App telemetry       | High     |
| DEK cache miss rate > 50%      | Backend metrics     | Medium   |
| Migration progress stall       | Background job      | Medium   |
| Encrypted field count mismatch | Audit job           | Low      |

---

## 11. Decision Matrix — What to Encrypt Where

| Product         |   Server-Side AI?   | Server-Side Search? |     Recommended Tier     | Justification                                                |
| --------------- | :-----------------: | :-----------------: | :----------------------: | ------------------------------------------------------------ |
| **LysnrAI**     |  Yes (extraction)   |  Yes (transcripts)  |          Tier 1          | Server needs plaintext for extraction; field-encrypt at rest |
| **JarvisJr**    | Yes (PromptBuilder) |         No          |  Tier 1 + Tier 3 opt-in  | Default field-encrypt; offer E2EE private agents             |
| **NoteLett**    |   Yes (MCP tools)   |  Yes (note search)  |  Tier 1 + Tier 3 opt-in  | Default field-encrypt; offer E2EE private workspaces         |
| **MindLyst**    |    Yes (triage)     | Yes (memory search) |          Tier 1          | Server needs plaintext for AI triage pipeline                |
| **NomGap**      |   Yes (AI coach)    |         No          | Tier 1 (meal notes only) | Low sensitivity overall; encrypt meal notes                  |
| **LocalMemGPT** |    Yes (Ollama)     |     Yes (FTS5)      |    Tier 1 + SQLCipher    | Local-first; SQLCipher for full-DB encryption                |
| **ChronoMind**  |         No          |         No          |     Tier 0 (current)     | Timer configs are not sensitive                              |
| **PeakPulse**   |         No          |         No          |     Tier 0 (current)     | GPS/stats are not sensitive                                  |
| **FlowMonk**    |   Yes (scheduler)   |         No          | Tier 1 (task notes only) | Backend is authoritative; encrypt task notes                 |
| **ActionTrail** |         No          | Yes (action search) | Tier 1 (snapshots only)  | Oversight requires readable actions; encrypt snapshots       |

---

## 12. Dependencies & Prerequisites

| Prerequisite                                              | Status                                 | Blocks                             |
| --------------------------------------------------------- | -------------------------------------- | ---------------------------------- |
| Azure Key Vault RBAC mode                                 | ⚠️ Planned (currently access policies) | Tier 1 MEK management              |
| AKV `encrypt`/`decrypt`/`wrapKey`/`unwrapKey` permissions | Not configured                         | Tier 1 envelope encryption         |
| `@azure/keyvault-keys` npm package                        | Available (not installed)              | Tier 1 AKV key provider            |
| `_encryption_keys` Cosmos container (per product)         | Not created                            | Tier 1 DEK storage                 |
| RS256 JWT migration (SmartAuth Phase 4)                   | Planned                                | Tier 2 key distribution            |
| iCloud Keychain sharing entitlements                      | Not configured                         | Tier 3 iOS key sync                |
| QR code library (iOS/Android)                             | Not installed                          | Tier 3 cross-platform key transfer |
| `better-sqlite3-sqlcipher` or app-level encrypt           | Not installed                          | LocalMemGPT SQLite encryption      |
| CMK configuration on `bytelystblobs`                      | Not configured                         | Blob storage Level 1               |

---

## 13. Related Documents

| Document                                                                                     | Purpose                                   |
| -------------------------------------------------------------------------------------------- | ----------------------------------------- |
| [`END_TO_END_ENCRYPTION_ROADMAP.md`](END_TO_END_ENCRYPTION_ROADMAP.md)                       | Phased implementation plan with timelines |
| [`AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md`](AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md)         | Current AKV setup and rotation runbooks   |
| [`AZURE_RESOURCE_INVENTORY.md`](AZURE_RESOURCE_INVENTORY.md)                                 | Azure resource inventory                  |
| [`ENVIRONMENT_VARIABLES_AND_KEYVAULT_AUDIT.md`](ENVIRONMENT_VARIABLES_AND_KEYVAULT_AUDIT.md) | Env var and AKV audit                     |
| `../architecture/ECOSYSTEM_ARCHITECTURE.md`                                                  | ByteLyst ecosystem architecture           |
| `../../services/platform-service/src/modules/auth/mfa/repository.ts`                         | Existing AES-256-GCM pattern              |
| `../../packages/kotlin-platform-sdk/src/main/kotlin/.../BLSecureStore.kt`                    | Android EncryptedSharedPreferences        |
| `../../packages/swift-platform-sdk/Sources/BLKeychain.swift`                                 | iOS Keychain wrapper                      |

---

## Appendix A: Algorithm Selection Rationale

| Algorithm         | Use Case                                 | Why                                                                                                                                                                                                                                                |
| ----------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **AES-256-GCM**   | Field encryption (all tiers)             | AEAD — provides confidentiality + integrity + authentication. Native support on all platforms. NIST approved.                                                                                                                                      |
| **RSA-OAEP-256**  | MEK wrapping in AKV                      | Azure Key Vault natively supports RSA key wrapping. 4096-bit RSA for maximum classical security margin. **Note:** RSA is NOT quantum-resistant (Shor's algorithm). Post-quantum migration path: AKV EC keys when NIST PQC standards are supported. |
| **PBKDF2-SHA256** | Web passphrase → key derivation          | Web Crypto API native. 600,000 iterations (OWASP 2024 recommendation).                                                                                                                                                                             |
| **X25519**        | Tier 3 key exchange                      | Modern elliptic-curve DH. Used by Signal, WireGuard. Compact (32-byte keys).                                                                                                                                                                       |
| **HKDF-SHA256**   | Tier 3 key derivation from shared secret | Standard KDF for deriving symmetric keys from DH output.                                                                                                                                                                                           |

## Appendix B: Encrypted Field Detection Pattern

Repositories can use a type guard to handle both encrypted and plaintext fields during migration:

```typescript
import type { EncryptedField } from '@bytelyst/field-encrypt';

function isEncrypted(value: unknown): value is EncryptedField {
  return (
    typeof value === 'object' &&
    value !== null &&
    '__encrypted' in value &&
    (value as Record<string, unknown>).__encrypted === true
  );
}

// Usage in repository
async function getNote(id: string): Promise<Note> {
  const doc = await collection.findById(id, partitionKey);
  if (isEncrypted(doc.body)) {
    doc.body = await encryptor.decrypt(doc.body, { userId: doc.userId, context: 'notes' });
  }
  return doc;
}
```

## Appendix C: Review Findings (2026-03-21)

Systematic review against actual codebase. **14 issues found and fixed:**

| #   | Type | Issue                                                                                                                                                 | Resolution                                                                                   |
| --- | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| F1  | BUG  | LysnrAI field name: doc said `transcriptText` but actual fields are `rawText` + `cleanedText`                                                         | Fixed §4.6 field table                                                                       |
| F2  | BUG  | LysnrAI `sessions.notes` doesn't exist — `SessionDoc` has no `notes` field                                                                            | Removed from field table                                                                     |
| F3  | BUG  | ActionTrail field names: doc said `beforeSnapshot`/`afterSnapshot` but actual are `before`/`after` (actions) and `beforeState`/`afterState` (reverts) | Fixed §4.6 + split into 2 rows                                                               |
| F4  | BUG  | JarvisJr `transcript` is `Array<{role,content,ts}>` not string                                                                                        | Added array encryption pattern (JSON-serialize → encrypt)                                    |
| F5  | BUG  | JarvisJr `coachingNotes` is `string[]` not string                                                                                                     | Marked as array¹ in field table                                                              |
| F6  | BUG  | FlowMonk tasks has no `notes` field — actual field is `description`                                                                                   | Fixed §4.6                                                                                   |
| F7  | BUG  | MindLyst reflections has no single `content` field — has array fields (`repeatedThemes`, `suggestedAdjustments`, etc.)                                | Fixed §4.6 with actual field names                                                           |
| F8  | BUG  | RSA-4096 "quantum-resistant" claim is wrong — Shor's algorithm breaks all RSA                                                                         | Fixed Appendix A + Decision Log                                                              |
| F9  | BUG  | Cost section said "10K ops/month free" for AKV — AKV Standard charges per operation                                                                   | Fixed Appendix D cost table                                                                  |
| F10 | GAP  | NomGap has 3 modules with `notes` (meal-log, fasting-sessions, weight-log) — doc only listed meal_logs                                                | Added all 3 containers                                                                       |
| F11 | GAP  | Array field encryption pattern not addressed                                                                                                          | Added array encryption note in §4.5 + footnote ¹                                             |
| F12 | GAP  | `@bytelyst/client-encrypt` vs `@bytelyst/field-encrypt` naming inconsistency                                                                          | Clarified: `field-encrypt` = server (node:crypto), `client-encrypt` = browser (SubtleCrypto) |
| F13 | GAP  | DEK storage container `_encryption_keys` not in prerequisites                                                                                         | Added to §12 prerequisites table                                                             |
| F14 | GAP  | ActionTrail reverts container missing from field table                                                                                                | Added `trail_reverts` row                                                                    |

## Appendix D: Cost Impact

| Resource       | Current                 | After Tier 1                    | Notes                                                                          |
| -------------- | ----------------------- | ------------------------------- | ------------------------------------------------------------------------------ |
| AKV operations | ~100/day (secret reads) | ~500/day (+DEK unwraps)         | AKV Standard: $0.03/10K transactions. ~15K ops/month ≈ $0.05/month             |
| AKV keys       | 0 RSA keys              | 10 RSA keys (1 MEK per product) | Software-protected: $0.03/10K ops; HSM-protected: $1/key/month + $0.03/10K ops |
| Cosmos RU      | Baseline                | +5-10% (larger encrypted docs)  | Encrypted fields are ~30% larger than plaintext (base64 + metadata)            |
| Compute        | Baseline                | +1-2% CPU                       | AES-256-GCM is hardware-accelerated (AES-NI) on all modern CPUs                |
| Storage        | Baseline                | +15-20% for encrypted fields    | Base64 encoding + IV + authTag + dekId overhead                                |

**Estimated monthly cost increase: < $10** (dominated by Cosmos storage overhead for encrypted fields).
