# ByteLyst — End-to-End Encryption Implementation Roadmap

> **Purpose:** Phased implementation plan for encryption across the ByteLyst ecosystem.  
> **Status:** Phase 1 + Phase 2 COMPLETE (10 backends + native SDKs + client-encrypt + secure-storage-web) + Sprint 6.2 migration CLI  
> **Author:** AI Architecture Review  
> **Last updated:** 2026-03-21  
> **Design doc:** [`END_TO_END_ENCRYPTION_DESIGN.md`](END_TO_END_ENCRYPTION_DESIGN.md)

---

## Overview

This roadmap implements the tiered encryption strategy defined in the design document. Work is organized into 6 sprints across 3 phases, estimated at **12-16 weeks** total elapsed time.

### Phase Summary

| Phase                          | Sprints    | Duration  | Deliverable                                                                           |
| ------------------------------ | ---------- | --------- | ------------------------------------------------------------------------------------- |
| **Phase 1: Foundation**        | Sprint 1-2 | 4-5 weeks | `@bytelyst/field-encrypt` package + AKV infrastructure + first 2 product integrations |
| **Phase 2: Ecosystem Rollout** | Sprint 3-4 | 4-5 weeks | All 10 product backends encrypted + native SDK components + web secure storage        |
| **Phase 3: Advanced**          | Sprint 5-6 | 4-6 weeks | True E2EE for JarvisJr/NoteLett + SQLite encryption + blob CMK + audit tooling        |

### Milestone Timeline

```
Week 1-2     Week 3-4     Week 5-6     Week 7-8     Week 9-10    Week 11-14
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐
│ Sprint 1 │ │ Sprint 2 │ │ Sprint 3 │ │ Sprint 4 │ │ Sprint 5 │ │   Sprint 6   │
│ Package + │ │ LysnrAI +│ │ 4 more   │ │ Native   │ │ E2EE     │ │   Audit +    │
│ AKV infra │ │ JarvisJr │ │ products │ │ SDKs +   │ │ private  │ │   polish +   │
│          │ │          │ │          │ │ web      │ │ vaults   │ │   docs       │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────────┘
  Phase 1       Phase 1      Phase 2      Phase 2      Phase 3       Phase 3
```

---

## Phase 1: Foundation (Weeks 1-5)

### Sprint 1 — `@bytelyst/field-encrypt` Package + AKV Infrastructure (Week 1-2)

**Goal:** Ship the shared encryption package and configure Azure Key Vault for key management.

#### 1.1 Azure Key Vault Infrastructure

- [ ] **1.1.1** Enable RBAC mode on `kv-mywisprai` vault (legacy name) (`enableRbacAuthorization=true`)
  - Assign `Key Vault Crypto Officer` to deployer user
  - Assign `Key Vault Crypto User` to service managed identities (when deployed)
  - **File:** Azure Portal / `az keyvault update`
  - **Risk:** Breaking change — all existing access policies stop working. Must reassign as RBAC roles simultaneously.
  - **Rollback:** `az keyvault update --enable-rbac-authorization false` + re-create access policies

- [ ] **1.1.2** Create 10 Master Encryption Keys (MEKs) in AKV

  ```
  lysnr-mek, mindlyst-mek, jarvisjr-mek, chronomind-mek,
  nomgap-mek, peakpulse-mek, flowmonk-mek, actiontrail-mek,
  notelett-mek, localmemgpt-mek
  ```

  - RSA 4096-bit, `wrapKey` + `unwrapKey` operations enabled
  - **Script:** `scripts/create-encryption-keys.sh`

- [x] **1.1.3** Add new env vars to `.env.example` and AKV secrets doc

  ```
  AZURE_KEYVAULT_URL=https://kv-mywisprai.vault.azure.net  # legacy vault name
  FIELD_ENCRYPT_KEY_PROVIDER=akv    # 'akv' | 'env' | 'memory'
  FIELD_ENCRYPT_MEK_NAME=lysnr-mek  # product-specific
  ```

  - **Files:** `.env.example`, `docs/devops/ENVIRONMENT_VARIABLES_AND_KEYVAULT_AUDIT.md`

#### 1.2 `@bytelyst/field-encrypt` Package

- [x] **1.2.1** Create package scaffold

  ```
  packages/field-encrypt/
  ├── src/
  │   ├── index.ts
  │   ├── types.ts
  │   ├── aes-gcm.ts
  │   ├── envelope.ts
  │   ├── key-provider-akv.ts
  │   ├── key-provider-env.ts
  │   ├── key-provider-memory.ts
  │   ├── key-cache.ts
  │   ├── migration.ts
  │   └── index.test.ts
  ├── package.json
  └── tsconfig.json
  ```

  - **Dependencies:** `@azure/keyvault-keys`, `@azure/identity` (peer), `zod` (peer)
  - **Dev dependencies:** `vitest`

- [x] **1.2.2** Implement core AES-256-GCM encrypt/decrypt
  - `aes-gcm.ts` — `encryptField()`, `decryptField()` using `node:crypto`
  - Input: plaintext string + key buffer + optional AAD
  - Output: `EncryptedField` object `{ __encrypted, v, alg, ct, iv, tag, dekId }`
  - **Tests:** encrypt → decrypt roundtrip, invalid authTag rejection, empty string, unicode, large payload (1 MB)

- [x] **1.2.3** Implement envelope encryption (DEK management)
  - `envelope.ts` — `generateDek()`, `wrapDek()`, `unwrapDek()`
  - DEK: 32-byte random AES key, identified by `dek_{userId}_{context}`
  - Wrap: RSA-OAEP with MEK in AKV
  - Stored: `{ dekId, wrappedKey, mekVersion, createdAt }` in a `_encryption_keys` Cosmos container
  - **Tests:** generate → wrap → unwrap roundtrip, MEK version tracking

- [x] **1.2.4** Implement key providers
  - `key-provider-akv.ts` — Production: `@azure/keyvault-keys` `CryptographyClient`
  - `key-provider-env.ts` — Dev/staging: raw hex key from `FIELD_ENCRYPT_KEY` env var (like current MFA pattern)
  - `key-provider-memory.ts` — Unit tests: random in-memory key, no external deps
  - **Tests:** provider interface contract tests for each

- [x] **1.2.5** Implement DEK cache
  - `key-cache.ts` — In-memory LRU cache with configurable TTL (default 15 min)
  - Cache key: `dekId`, value: unwrapped DEK buffer
  - Auto-evict on TTL expiry, manual `invalidate(dekId)` for rotation
  - **Tests:** cache hit/miss, TTL expiry, manual invalidation, max size eviction

- [x] **1.2.6** Implement `createFieldEncryptor()` factory
  - Wires key provider + cache + AES-GCM
  - Public API: `encrypt()`, `decrypt()`, `encryptBatch()`, `decryptBatch()`, `isEncrypted()`
  - **Tests:** full integration test with memory provider

- [x] **1.2.7** Implement migration helpers
  - `migration.ts` — `migrateField()`: read plaintext, encrypt, write back (idempotent)
  - Supports batch size, dry run, progress callback
  - Detects already-encrypted fields via `__encrypted` sentinel
  - **Tests:** migrate plaintext → encrypted, skip already-encrypted, dry run mode

- [x] **1.2.8** Add `isEncrypted()` type guard
  - Exported utility for repositories to detect encrypted vs plaintext during migration period
  - **Tests:** true for EncryptedField, false for string, false for null/undefined

**Sprint 1 deliverable:** `@bytelyst/field-encrypt` package with all 3 key providers, ~30 tests, `pnpm build && pnpm test` passing.

**Commit:** `feat(field-encrypt): create @bytelyst/field-encrypt package with AES-256-GCM envelope encryption`

---

### Sprint 2 — First Product Integrations: LysnrAI + JarvisJr (Week 3-4)

**Goal:** Encrypt the highest-sensitivity fields in 2 products. Validate the pattern end-to-end.

#### 2.1 LysnrAI Backend (port 4015)

- [x] **2.1.1** Add `@bytelyst/field-encrypt` dependency to `backend/package.json`

  ```json
  "@bytelyst/field-encrypt": "file:../../learning_ai_common_plat/packages/field-encrypt"
  ```

- [x] **2.1.2** Create `backend/src/lib/field-encrypt.ts` — service-level encryptor singleton

  ```typescript
  import { createFieldEncryptor } from '@bytelyst/field-encrypt';
  import { config } from './config.js';

  export const encryptor = createFieldEncryptor({
    keyProvider: config.FIELD_ENCRYPT_KEY_PROVIDER ?? 'memory',
    mekName: 'lysnr-mek',
    keyVaultUrl: config.AZURE_KEYVAULT_URL,
  });
  ```

- [x] **2.1.3** Encrypt `rawText` and `cleanedText` in transcripts module
  - `repository.ts`: encrypt on `create()`, decrypt on `findById()` and `findByUserId()`
  - Keep field names but value changes from string to EncryptedField
  - **Migration:** Add `POST /api/admin/migrate-encryption` endpoint (admin-only) that batch-encrypts existing plaintext

- [x] **2.1.4** Update config schema with encryption env vars

  ```typescript
  FIELD_ENCRYPT_KEY_PROVIDER: z.enum(['akv', 'env', 'memory']).default('memory'),
  FIELD_ENCRYPT_KEY: z.string().optional(), // only for 'env' provider
  AZURE_KEYVAULT_URL: z.string().url().optional(),
  ```

- [x] **2.1.5** Update tests — ensure existing tests pass with memory provider (no env vars needed)

**Commit:** `feat(lysnrai): encrypt rawText and cleanedText fields with @bytelyst/field-encrypt`

#### 2.2 JarvisJr Backend (port 4012)

- [x] **2.2.1** Add `@bytelyst/field-encrypt` dependency

- [x] **2.2.2** Create `backend/src/lib/field-encrypt.ts` encryptor singleton

- [x] **2.2.3** Encrypt fields in jarvis-sessions module
  - `transcript` field — `Array<{role,content,ts}>`, JSON-serialize before encrypting as single EncryptedField
  - `coachingNotes` field — `string[]`, JSON-serialize before encrypting as single EncryptedField
  - **Note:** Array fields use the JSON-serialize → encrypt → store pattern (see design doc §4.5)

- [x] **2.2.4** Encrypt fields in jarvis-memory module
  - `content` field — per-agent persistent memory entries

- [x] **2.2.5** Update config + tests

**Commit:** `feat(jarvisjr): encrypt session transcripts, coaching notes, and agent memory`

#### 2.3 Refactor MFA to Use Shared Package

- [ ] **2.3.1** ~~Migrate `platform-service/src/modules/auth/mfa/repository.ts`~~ — **DEFERRED**
  - MFA uses a different data format (`encrypted` + `iv` + `authTag` triple, not `EncryptedField` object)
  - Refactoring would change stored data format and break existing MFA records in production
  - Existing inline AES-256-GCM implementation already provides equivalent security
  - Will revisit when a migration-safe approach is designed

**Commit:** `refactor(auth): migrate MFA encryption to @bytelyst/field-encrypt`

**Sprint 2 deliverable:** 2 product backends fully encrypted, MFA migrated, all tests green.

---

## Phase 2: Ecosystem Rollout (Weeks 5-10)

### Sprint 3 — Four More Product Backends (Week 5-6)

**Goal:** Encrypt sensitive fields in NoteLett, MindLyst, NomGap, and ActionTrail.

#### 3.1 NoteLett Backend (port 4016)

- [x] **3.1.1** Add dependency + create encryptor singleton
- [x] **3.1.2** Encrypt `body` field in notes module
- [ ] **3.1.3** Encrypt `content` field in note-artifacts module — deferred (low priority)
- [ ] **3.1.4** Update MCP tools to decrypt before processing — deferred (artifact encryption first)
- [x] **3.1.5** Update tests — all 86 passing

**Commit:** `feat(notelett): encrypt note body and artifact content`

#### 3.2 MindLyst Backend (port 4014)

- [x] **3.2.1** Add dependency + create encryptor singleton
- [x] **3.2.2** Encrypt `rawContent` and `triageResult.summary` in memory_items module
- [x] **3.2.3** Encrypt array fields in reflections module
  - `repeatedThemes` (string[]), `postponedItems` (string[]), `roleImbalanceSignals` (string[]), `suggestedAdjustments` (string[]), `vsLastWeek.summary` (string)
  - Array fields: JSON-serialize → encrypt → store as single EncryptedField each
- [x] **3.2.4** Encrypt daily-briefs: `greeting`, `priorityItems`, `brainSummaries`, `streakMessage`, `motivationalQuote`
- [x] **3.2.5** Encrypt brains: `rolePrompt`
- [x] **3.2.6** Update tests

**Commit:** `feat(mindlyst): encrypt sensitive fields across all modules`

#### 3.3 NomGap Backend (port 4013)

- [x] **3.3.1** Add dependency + create encryptor singleton
- [x] **3.3.2** Encrypt `notes` field in meal-logs module
- [x] **3.3.3** Encrypt `notes` field in fasting-sessions module
- [x] **3.3.4** Encrypt `notes` field in weight-log module
- [x] **3.3.5** Update tests — all 209 passing

**Commit:** `feat(nomgap): encrypt notes fields across meal-log, fasting-sessions, and weight-log`

#### 3.4 ActionTrail Backend (port 4018)

- [x] **3.4.1** Add dependency + create encryptor singleton
- [x] **3.4.2** Encrypt `before` and `after` fields in actions module
- [x] **3.4.3** Encrypt `beforeState`, `afterState`, and `revertPayload` fields in reverts module
- [x] **3.4.4** Update tests — all 191 passing

**Commit:** `feat(actiontrail): encrypt action and revert snapshots`

#### 3.5 Remaining Backends (Low Priority — Defer or Skip)

| Product                | Backend Port                                                                                                                                   | Encrypted Fields                                                                      | Decision |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------- |
| **FlowMonk** (4017)    | `tasks.description`                                                                                                                            | ✅ Done (Sprint 4) — 211 tests                                                        |
| **ChronoMind** (4011)  | `timers.description`, `routines.description`, `routines.steps[].notes`, `shared-timers.description`, `webhooks.secret`, `webhooks.description` | ✅ Done — 182 tests                                                                   |
| **PeakPulse** (4010)   | `peak-sessions.notes`                                                                                                                          | ✅ Done — 65 tests                                                                    |
| **MindLyst** (4014)    | `memory.rawContent`, `memory.triageResult.summary`, reflections (5 fields), daily-briefs (5 fields), `brains.rolePrompt`                       | ✅ Done                                                                               |
| **LocalMemGPT** (4019) | `messages.content`, `conversations.system_prompt`                                                                                              | Deferred — SQLite raw SQL (better-sqlite3), needs SQLite-specific encryption approach |

**Sprint 3 deliverable:** 10 product backends encrypted (LysnrAI, JarvisJr, NoteLett, NomGap, ActionTrail, FlowMonk, ChronoMind, PeakPulse, MindLyst). Only LocalMemGPT (SQLite) deferred to Sprint 5.

---

### Sprint 4 — Native SDKs + Web Secure Storage + FlowMonk (Week 7-8)

**Goal:** Add client-side encryption primitives to all SDKs. Secure web localStorage.

#### 4.1 Swift Platform SDK — `BLFieldEncrypt`

- [x] **4.1.1** Create `Sources/BLFieldEncrypt.swift` in `packages/swift-platform-sdk/`
  - `BLEncryptedField` Codable struct + `BLFieldEncrypt` enum (CryptoKit AES-256-GCM)
  - encrypt/decrypt, AAD support, generateKey, keyFromHex, isEncrypted
  - Data hex helpers (hexString, init?(hexString:))
- [x] **4.1.2** Key derivation from Keychain-stored secret
  - getOrCreateKey(service:account:), loadKey(), deleteKey()
  - Generates AES-256 key, persists as hex in BLKeychain
- [x] **4.1.3** Unit tests (XCTest) — 22 tests in BLFieldEncryptTests.swift

**Commit:** `feat(swift-sdk): add BLFieldEncrypt for client-side AES-256-GCM encryption`

#### 4.2 Kotlin Platform SDK — `BLFieldEncrypt`

- [x] **4.2.1** Create `src/main/.../BLFieldEncrypt.kt` in `packages/kotlin-platform-sdk/`
  - `BLEncryptedField` data class + `BLFieldEncrypt` object (javax.crypto AES-256-GCM)
  - encrypt/decrypt, AAD support, generateKey, keyFromHex, isEncrypted
  - ByteArray/String hex extension functions
- [x] **4.2.2** Key derivation from BLSecureStore
  - getOrCreateKey(store:account:), loadKey(), deleteKey()
  - Generates AES-256 key, persists as hex in EncryptedSharedPreferences
- [x] **4.2.3** Unit tests (JUnit5) — 21/21 passing in BLFieldEncryptTest.kt

**Commit:** `feat(kotlin-sdk): add BLFieldEncrypt for client-side AES-256-GCM encryption`

#### 4.3 TypeScript Client Package — `@bytelyst/client-encrypt`

- [x] **4.3.1** Create `packages/client-encrypt/` for browser + React Native
  - AES-256-GCM via SubtleCrypto, wire-compatible EncryptedField
  - encryptField, decryptField, generateKey, keyFromHex, keyToHex
  - isEncryptedField type guard, toHex/fromHex hex helpers
- [x] **4.3.2** PBKDF2 key derivation for web (600,000 iterations per OWASP 2023)
- [x] **4.3.3** 22 Vitest tests, all passing

**Commit:** `feat(client-encrypt): create @bytelyst/client-encrypt for browser/RN encryption`

#### 4.4 Web Secure Storage

- [x] **4.4.1** Create `@bytelyst/secure-storage-web` package
  - IndexedDB-backed storage with non-extractable AES-256-GCM CryptoKey
  - API: set, get, delete, clear, has, keys — all async
  - Namespace isolation for multi-app usage
  - Falls back to localStorage when SubtleCrypto unavailable
  - 16 Vitest tests (fake-indexeddb), all passing

- [ ] **4.4.2** Migrate auth tokens from `localStorage` to secure storage in all web apps
  - Update `@bytelyst/auth-client` to use secure storage
  - **Products affected:** All 7 web dashboards/apps

**Commit:** `feat(secure-storage-web): IndexedDB + SubtleCrypto encrypted storage for web`

#### 4.5 FlowMonk Backend (port 4017)

- [x] **4.5.1** Add dependency + encrypt `tasks.description` field
  - field-encrypt singleton (flowmonk-mek), config env vars
  - Encrypt on create/update, decrypt on read/list
  - Backward-compatible via isEncryptedField guard
- [x] **4.5.2** 211/211 tests passing

**Commit:** `feat(flowmonk): encrypt task notes`

**Sprint 4 deliverable:** All native SDKs have encryption primitives. Web secure storage created. 7 backends encrypted (6 prior + FlowMonk). Only 4.4.2 (auth-client migration) deferred to Sprint 5.

---

## Phase 3: Advanced Encryption (Weeks 9-14)

### Sprint 5 — True E2EE Private Vaults + SQLite Encryption (Week 9-10)

**Goal:** Implement zero-knowledge encryption for opt-in private spaces in JarvisJr and NoteLett. Encrypt LocalMemGPT SQLite.

#### 5.1 E2EE Key Management Infrastructure

- [ ] **5.1.1** Design E2EE key exchange protocol
  - User generates X25519 keypair on device
  - Public key uploaded to platform-service `user_public_keys` container
  - Private key stored in Keychain (iOS/Mac) / Keystore (Android) / derived from passphrase (Web)
  - Recovery: 12-word BIP-39 mnemonic → seed → keypair

- [ ] **5.1.2** Create `@bytelyst/e2ee-core` package

  ```typescript
  export function generateKeypair(): { publicKey: Uint8Array; privateKey: Uint8Array };
  export function generateRecoveryPhrase(): string; // 12 BIP-39 words
  export function keypairFromRecovery(phrase: string): { publicKey; privateKey };
  export function encryptForUser(
    plaintext: string,
    recipientPublicKey: Uint8Array
  ): EncryptedPayload;
  export function decryptForUser(payload: EncryptedPayload, privateKey: Uint8Array): string;
  ```

  - Uses X25519 for key agreement, HKDF-SHA256 for derivation, AES-256-GCM for encryption
  - **Dependencies:** `@noble/curves` (X25519), `@noble/hashes` (HKDF, SHA256)
  - **Tests:** ~20 tests

- [ ] **5.1.3** Add `user_public_keys` container to platform-service
  - Schema: `{ id, userId, productId, publicKey (base64), deviceId, createdAt }`
  - Partition key: `/userId`
  - Routes: `POST /auth/e2ee/register-key`, `GET /auth/e2ee/public-key/:userId`

- [ ] **5.1.4** Multi-device key sync
  - iOS: Share private key via iCloud Keychain (requires entitlement)
  - Android: Share via Google Backup (EncryptedSharedPreferences)
  - Cross-platform: QR code containing encrypted private key (encrypted with passphrase)

**Commit:** `feat(e2ee): create @bytelyst/e2ee-core + platform key registration`

#### 5.2 JarvisJr E2EE Private Agents

- [ ] **5.2.1** Add `isPrivate` boolean field to agent config
  - When `true`, all session transcripts and memory for this agent are E2EE
  - Server stores only ciphertext — cannot decrypt
  - AI features (PromptBuilder, memory retrieval) disabled for private agents
  - UI shows "Private Agent" badge + "AI features unavailable" warning

- [ ] **5.2.2** Client-side encrypt before API calls (iOS + Web)
  - Session transcript → `e2eeCore.encryptForUser(transcript, userPublicKey)`
  - Memory content → `e2eeCore.encryptForUser(content, userPublicKey)`
  - Client-side decrypt after reads

- [ ] **5.2.3** Update JarvisJr web + iOS to handle E2EE toggle

**Commit:** `feat(jarvisjr): E2EE private agents with zero-knowledge encryption`

#### 5.3 NoteLett E2EE Private Workspaces

- [ ] **5.3.1** Add `isEncrypted` boolean field to workspace config
  - When `true`, all notes in workspace are E2EE
  - MCP tools return encrypted content (agent cannot read)
  - Search disabled for encrypted workspaces

- [ ] **5.3.2** Client-side encrypt before API calls (Web + Mobile)
- [ ] **5.3.3** Update UI with "Encrypted Workspace" badge + trade-off warnings

**Commit:** `feat(notelett): E2EE private workspaces with zero-knowledge encryption`

#### 5.4 LocalMemGPT SQLite Encryption

**Decision: SQLCipher (full-database encryption)**

SQLCipher encrypts the entire SQLite file with AES-256-CBC, preserving FTS5 search, triggers, and all SQL features transparently. Application-level encryption was rejected because it breaks FTS5 full-text search on `messages.content`.

**Sensitive fields protected:** `messages.content`, `conversations.system_prompt`, `document_chunks.content`, `document_chunks.embedding`

**Key management strategy:**
| Source | Priority | Description |
|--------|----------|-------------|
| Azure Key Vault secret `localmemgpt-sqlite-key` | 1st | Production — resolved via `@bytelyst/config` `resolveKeyVaultSecrets()` at startup |
| `SQLITE_ENCRYPTION_KEY` env var | 2nd | CI / staging / Docker override |
| `~/.LysnrAI/localmemgpt-sqlite.key` file | 3rd | Auto-generated on first run (64-char hex) for local dev |
| No key | 4th | Skip encryption — unencrypted dev mode for backwards compat |

> **Why AKV?** Consistent with all other ByteLyst products (MEKs stored in `kv-mywisprai`). Key never touches the repo, survives container restarts, supports rotation via AKV versioning.

**Implementation steps:**

- [ ] **5.4.1** Swap `better-sqlite3` → `better-sqlite3-sqlcipher` in `backend/package.json`
  - Native module — requires OpenSSL + C toolchain for build
  - Verify Docker build (`backend/Dockerfile`) still compiles
  - Verify CI (`ci.yml`) has build tools available

- [ ] **5.4.2** Add key management to `backend/src/lib/sqlite.ts`
  - Read key from `SQLITE_ENCRYPTION_KEY` env var, fall back to `~/.LysnrAI/localmemgpt-sqlite.key`
  - If neither exists: auto-generate 64-char hex key, write to key file, log warning with backup reminder
  - If key exists: `db.pragma(\`key = '\${key}'\`)`as first statement after`new Database(dbPath)`
  - In-memory test DB (`getTestDb()`) skips encryption

- [ ] **5.4.3** Existing database migration (one-time, on startup)
  - Detect if `db/memory.db` is unencrypted (try open without key, check if readable)
  - If unencrypted + key available: run SQLCipher export migration:
    ```sql
    ATTACH DATABASE 'memory-encrypted.db' AS encrypted KEY 'the-key';
    SELECT sqlcipher_export('encrypted');
    DETACH DATABASE encrypted;
    ```
  - Swap `memory.db` → `memory-unencrypted.db.bak`, `memory-encrypted.db` → `memory.db`
  - Log: "Database encrypted successfully. Backup at memory-unencrypted.db.bak"

- [ ] **5.4.4** Update `backend/.env.example` with `SQLITE_ENCRYPTION_KEY` documentation
- [ ] **5.4.5** Verify FTS5 search, document upload/chunking, and export/import still work
- [ ] **5.4.6** Update `docker-compose.yml` to mount key file or pass env var

**Commit:** `feat(localmemgpt): SQLCipher full-database encryption with auto-key management`

**Sprint 5 deliverable:** E2EE private vaults for JarvisJr + NoteLett. SQLite encryption for LocalMemGPT.

---

### Sprint 6 — Blob CMK + Audit Tooling + Documentation (Week 11-14)

**Goal:** Complete the encryption story with blob storage, monitoring, migration tooling, and docs.

#### 6.1 Azure Blob Storage Customer-Managed Key (CMK)

- [ ] **6.1.1** Create `bytelyst-blob-mek` RSA key in AKV
- [ ] **6.1.2** Configure `bytelystblobs` storage account to use CMK from AKV
  - Azure Portal → Storage account → Encryption → Customer-managed keys
  - Select `bytelyst-blob-mek` from `kv-mywisprai` (legacy vault name)
- [ ] **6.1.3** Verify all blob operations still work (upload, download, SAS tokens)
- [ ] **6.1.4** Update `AZURE_RESOURCE_INVENTORY.md`

**Commit:** `feat(infra): enable customer-managed encryption key for Azure Blob Storage`

#### 6.2 Encryption Migration CLI

- [x] **6.2.1** Create `scripts/encrypt-migrate.ts` — CLI tool for batch encrypting existing plaintext

  ```bash
  npx tsx scripts/encrypt-migrate.ts --product lysnrai --dry-run
  npx tsx scripts/encrypt-migrate.ts --product all --dry-run
  npx tsx scripts/encrypt-migrate.ts --product chronomind --container timers
  npx tsx scripts/encrypt-migrate.ts --product mindlyst --batch-size 50 --verbose
  ```

  - Uses `@bytelyst/field-encrypt` `migrateDocuments()` helper (idempotent via `__encrypted` sentinel)
  - Connects directly to Cosmos DB via `@azure/cosmos`
  - Per-product configs embedded: 10 products, 20 containers, 40+ fields
  - Dry-run mode (count without modifying), verbose per-doc progress
  - Nested field support (e.g., `triageResult.summary`)
  - JSON-serialized arrays/objects (e.g., reflections string arrays, brainSummaries Record)
  - Per-field breakdown table in summary output

- [x] **6.2.2** Embedded per-product migration configs (no separate shell scripts needed)
  - Products: lysnrai, jarvisjr, notelett, nomgap, actiontrail, flowmonk, chronomind, peakpulse, mindlyst
  - Each config specifies: productId, mekName, containers[], fields[] with context + userIdField + jsonSerialize
  - Run `--product all --dry-run` for full ecosystem audit

**Commit:** `feat(devops): encryption migration CLI with embedded product configs`

#### 6.3 Monitoring & Audit

- [ ] **6.3.1** Add encryption metrics to telemetry
  - `field_encrypt_count` — encryptions performed
  - `field_decrypt_count` — decryptions performed
  - `dek_cache_hit_rate` — DEK cache efficiency
  - `dek_unwrap_latency_ms` — AKV round-trip time
  - `migration_progress` — % of documents encrypted per container

- [ ] **6.3.2** Add AKV diagnostic logging
  - Enable AKV diagnostics → Log Analytics workspace
  - Alert on: unexpected IP access, high error rate, key deletion attempts

- [ ] **6.3.3** Add encryption audit endpoint to platform-service
  - `GET /api/admin/encryption-status` — returns per-product encryption coverage

  ```json
  {
    "products": [
      {
        "productId": "lysnrai",
        "containers": [
          {
            "name": "transcripts",
            "field": "transcriptText",
            "total": 10000,
            "encrypted": 9500,
            "coverage": "95%"
          }
        ]
      }
    ]
  }
  ```

- [ ] **6.3.4** Add encryption status to admin dashboard Ops page
  - Table showing encryption coverage per product/container
  - Migration trigger button (admin-only)

**Commit:** `feat(monitoring): encryption telemetry, AKV diagnostics, audit endpoint`

#### 6.4 Key Rotation Runbooks

- [ ] **6.4.1** Write MEK rotation runbook

  ```
  1. Create new MEK version in AKV (az keyvault key rotate)
  2. Run DEK re-wrap script: scripts/rotate-mek.ts --product lysnrai
  3. Verify decryption still works (run tests)
  4. Update AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md
  ```

- [ ] **6.4.2** Write DEK rotation runbook (per-user key refresh)
- [ ] **6.4.3** Write emergency key revocation runbook
- [ ] **6.4.4** Add rotation schedule to `AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md`

**Commit:** `docs(devops): encryption key rotation runbooks`

#### 6.5 Documentation Updates

- [ ] **6.5.1** Update all product `AGENTS.md` files with encryption conventions

  ```markdown
  ### Encryption

  - Sensitive fields encrypted via `@bytelyst/field-encrypt`
  - Encryption singleton in `backend/src/lib/field-encrypt.ts`
  - Tests use `memory` key provider (no env vars needed)
  - Production uses `akv` key provider with product-specific MEK
  ```

- [ ] **6.5.2** Update `AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md` with MEK section
- [ ] **6.5.3** Update `ENVIRONMENT_VARIABLES_AND_KEYVAULT_AUDIT.md` with new env vars
- [ ] **6.5.4** Update `AZURE_RESOURCE_INVENTORY.md` with new AKV keys + containers
- [ ] **6.5.5** Write `docs/BEST_PRACTICES/ENCRYPTION.md` — developer guide for adding encryption to new fields

**Commit:** `docs: comprehensive encryption documentation across all repos`

**Sprint 6 deliverable:** Complete encryption story — blob CMK, migration tooling, monitoring, key rotation runbooks, full documentation.

---

## Test Plan

### Package Tests

| Package                        | Tests (estimated) | Coverage                                                    |
| ------------------------------ | :---------------: | ----------------------------------------------------------- |
| `@bytelyst/field-encrypt`      |        30         | AES-GCM, envelope, providers, cache, migration, type guard  |
| `@bytelyst/client-encrypt`     |        15         | Web Crypto encrypt/decrypt, PBKDF2, roundtrip               |
| `@bytelyst/e2ee-core`          |        20         | Keypair gen, recovery phrase, encrypt/decrypt, key exchange |
| `@bytelyst/secure-storage-web` |        10         | IndexedDB CRUD, fallback, CryptoKey persistence             |

### Integration Tests (per product)

Each product backend gets 3-5 new tests:

- Encrypt → store → read → decrypt roundtrip
- Migration: plaintext → encrypted (idempotent)
- Encrypted field detection (`isEncrypted()`)
- Batch encrypt/decrypt
- Config: memory provider works without env vars

**Total estimated new tests: ~120**

### E2E Verification

- [ ] Manual: Create encrypted note in NoteLett, verify ciphertext in Cosmos Data Explorer
- [ ] Manual: Create private agent in JarvisJr, verify server cannot read transcript
- [ ] Manual: Rotate MEK, verify all reads still work
- [ ] Manual: Run migration script on test container, verify 100% coverage

---

## Risk Register

| #   | Risk                                                  | Likelihood | Impact   | Mitigation                                                                                     |
| --- | ----------------------------------------------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------- |
| R1  | AKV RBAC migration breaks existing services           | Medium     | High     | Test in staging first. Keep access policy backup. Immediate rollback plan.                     |
| R2  | DEK cache memory pressure on high-user-count services | Low        | Medium   | Configurable max cache size. LRU eviction. Monitor memory usage.                               |
| R3  | Encryption migration corrupts data                    | Low        | Critical | Dry-run mode first. Backup before migration. Idempotent (skip already-encrypted).              |
| R4  | AKV outage blocks all reads/writes                    | Low        | Critical | DEK cache (15 min) provides buffer. Env key provider as emergency fallback.                    |
| R5  | Performance regression on high-throughput endpoints   | Low        | Medium   | AES-256-GCM is hardware-accelerated. DEK cache avoids AKV round-trips. Benchmark before/after. |
| R6  | E2EE key loss = permanent data loss                   | Medium     | Critical | Recovery phrase (12 words). Prominent UI warnings. Confirmation flow before enabling.          |
| R7  | Cosmos RU increase from larger encrypted documents    | Low        | Low      | ~15-20% doc size increase. Monitor RU consumption.                                             |
| R8  | FTS5/search breaks on encrypted fields                | Medium     | Medium   | Maintain plaintext search index or use extraction-service for keywords.                        |

---

## Success Metrics

| Metric                   | Target                                                   | Measured By                 |
| ------------------------ | -------------------------------------------------------- | --------------------------- |
| **Encryption coverage**  | 100% of P0 fields encrypted within 8 weeks               | Audit endpoint              |
| **Zero plaintext leaks** | 0 unencrypted sensitive fields in Cosmos after migration | Audit scan                  |
| **Performance impact**   | < 5% latency increase on encrypted endpoints             | Backend metrics             |
| **Test coverage**        | 120+ new encryption tests                                | Vitest/XCTest/JUnit reports |
| **DEK cache hit rate**   | > 95%                                                    | Telemetry                   |
| **Key rotation time**    | < 30 min for full MEK rotation                           | Runbook timing              |
| **Developer experience** | < 10 LOC to encrypt a new field                          | Code review                 |

---

## Cost Estimate

| Item                       |  Monthly Cost   | Notes                                      |
| -------------------------- | :-------------: | ------------------------------------------ |
| AKV RSA keys (10 MEKs)     |     $10-50      | $1/key (software) or $5/key (HSM)          |
| AKV operations (DEK wraps) |       $0        | Within free tier (10K/month)               |
| Cosmos RU increase         |      ~$2-5      | 15-20% larger docs on encrypted containers |
| Compute (AES-GCM)          |       ~$0       | Hardware-accelerated, negligible           |
| **Total**                  | **< $60/month** |                                            |

---

## Decision Log

| Date       | Decision                                 | Rationale                                                                                                               |
| ---------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| 2026-03-21 | Use AES-256-GCM for all field encryption | AEAD, native on all platforms, NIST approved, matches existing MFA pattern                                              |
| 2026-03-21 | Envelope encryption (MEK → DEK)          | Enables key rotation without re-encrypting all data; per-user isolation                                                 |
| 2026-03-21 | Tier 3 E2EE only for JarvisJr + NoteLett | Server-side AI in other products requires plaintext access                                                              |
| 2026-03-21 | Skip ChronoMind + PeakPulse encryption   | Timer configs and GPS stats are not sensitive data                                                                      |
| 2026-03-21 | RSA-4096 for MEKs                        | Maximum classical security margin; AKV native support. Not quantum-resistant — plan PQC migration when AKV supports it. |
| 2026-03-21 | 15-min DEK cache TTL                     | Balances AKV rate limits vs key exposure window                                                                         |
| 2026-03-21 | `__encrypted` sentinel in documents      | Enables gradual migration without schema changes                                                                        |

---

## Appendix: Sprint Checkpoint Verification

After each sprint, run:

```bash
# ── Package tests ──────────────────────────────────
cd learning_ai_common_plat && pnpm build && pnpm test

# ── All product backends ───────────────────────────
cd learning_voice_ai_agent/backend && npm test        # LysnrAI
cd learning_ai_jarvis_jr/backend && npm test           # JarvisJr
cd learning_ai_notes/backend && npm test               # NoteLett
cd learning_multimodal_memory_agents/backend && npm test # MindLyst
cd learning_ai_fastgap/backend && npm test             # NomGap
cd learning_ai_trails/backend && npm test              # ActionTrail
cd learning_ai_flowmonk/backend && npm test            # FlowMonk
cd learning_ai_local_memory_gpt/backend && npm test    # LocalMemGPT

# ── Platform service ──────────────────────────────
cd learning_ai_common_plat && pnpm --filter @lysnrai/platform-service test

# ── Type check everything ──────────────────────────
cd learning_ai_common_plat && pnpm typecheck
```
