# Cloud Provider Migration Analysis — ByteLyst Ecosystem > **Author:** AI Analysis (Cascade) > **Date:** 2026-03-01 > **Scope:** All 7 repos — LysnrAI, MindLyst, ChronoMind, NomGap, PeakPulse, Common Platform, JarvisJr > **Purpose:** Evaluate current Azure investment, assess migration feasibility to AWS / GCP / MongoDB Atlas / multi-cloud, and provide actionable recommendations. --- ## Table of Contents 1. [Executive Summary](#1-executive-summary) 2. [Current Azure Investment Inventory](#2-current-azure-investment-inventory) 3. [Dependency Depth Analysis](#3-dependency-depth-analysis) 4. [Migration Target Comparison](#4-migration-target-comparison) 5. [Per-Service Migration Analysis](#5-per-service-migration-analysis) 6. [Migration Scenario Scoring](#6-migration-scenario-scoring) 7. [Cost Comparison](#7-cost-comparison) 8. [Abstraction Layer Assessment](#8-abstraction-layer-assessment) 9. [Risk Analysis](#9-risk-analysis) 10. [Recommendations](#10-recommendations) 11. [Migration Playbook (If Chosen)](#11-migration-playbook-if-chosen) 12. [Appendix A: File-Level Azure Dependency Map](#appendix-a-file-level-azure-dependency-map) 13. [Appendix B: SDK & Package Inventory](#appendix-b-sdk--package-inventory) --- ## 1. Executive Summary The ByteLyst ecosystem is **moderately coupled** to Azure. The coupling is concentrated in **3 packages** (`@bytelyst/cosmos`, `@bytelyst/blob`, `@bytelyst/config`) and **2 Python modules** (`azure_stt.py`, `cosmos_client.py`). The architecture already uses an internal abstraction layer — most application code never imports Azure SDKs directly. ### Key Findings | Dimension | Assessment | | ------------------------------ | ------------------------------------------------------------------------------------------- | | **Overall Azure lock-in** | **Medium** — concentrated in ~15 files, but those files are foundational | | **Easiest to migrate** | Blob Storage, Key Vault, OpenAI, Application Insights | | **Hardest to migrate** | Cosmos DB (SQL API queries in 56+ repository files), Azure Speech SDK | | **Best alternative DB** | MongoDB Atlas (closest query model to Cosmos SQL API) | | **Best alternative cloud** | AWS (broadest service parity, mature SDK ecosystem) | | **Estimated migration effort** | 4–8 weeks for full cloud swap (Cosmos DB is the long pole) | | **Recommendation** | **Stay on Azure** for now, but invest in abstraction layers to reduce future switching cost | ### Azure Services Used (8 total) | # | Azure Service | Monthly Cost | Lock-in Risk | Files Affected | | --- | ------------------------------------------- | ------------- | ------------ | ------------------------------------------------- | | 1 | **Cosmos DB** (SQL/NoSQL API) | ~$4–10 | **HIGH** | 56+ repository files, 3 databases, ~45 containers | | 2 | **Blob Storage** | ~$0.20 | LOW | 2 packages + 1 Python module | | 3 | **Azure OpenAI** | ~$5–10 | LOW | 3 files (already supports OpenAI fallback) | | 4 | **Speech Services** | $0 (F0) | **HIGH** | 2 files (deep SDK integration, streaming) | | 5 | **Key Vault** | ~$0.06 | LOW | 2 files (1 TS, 1 Python) | | 6 | **Notification Hubs** | $0 (Free) | MEDIUM | Planned, not yet deeply integrated | | 7 | **Application Insights** | $0 (5GB free) | LOW | 1 file (custom telemetry already built) | | 8 | **Azure Identity** (DefaultAzureCredential) | $0 | LOW | Used by Key Vault + Secrets Manager | --- ## 2. Current Azure Investment Inventory ### 2.1 Azure Resources (from Azure Portal) | Resource | Azure Name | Region | SKU | Status | | ----------------- | ------------------------- | -------------- | ---------------- | ------------------------------- | | Resource Group | `rg-mywisprai` | East US | — | Active | | Cosmos DB | `cosmos-mywisprai` | West US 2 | Serverless | Active — 3 DBs, ~45 containers | | Blob Storage | `bytelystblobs` | West US 2 | StorageV2, RAGRS | Active — 9+ containers | | Azure OpenAI | `mywisprai-openai-sweden` | Sweden Central | S0 | Active — gpt-4o-mini deployment | | Speech Service | `mywisprai-speech` | East US | F0 (Free) | Active | | Key Vault | `kv-mywisprai` | East US | Standard | Active — ~25 secrets | | Notification Hubs | `lysnnai` namespace | East US | Free | Active — 2 hubs | | App Insights | `bytelyst-appinsights` | East US | Classic | Active | ### 2.2 Cosmos DB Databases & Containers | Database | Containers | Products Using | | ----------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------- | | `lysnrai` | ~27 containers (users, subscriptions, feature_flags, audit_log, tracker_items, telemetry_events, etc.) | LysnrAI, platform-service (all products) | | `mindlyst` | ~20 containers (brains, memory_items, streaks, reflections, etc.) | MindLyst | | `mywisprai` | 10 containers (legacy, pre-rebrand) | Legacy / migration target | **Total: ~57 containers across 3 databases**, all using Cosmos SQL (NoSQL) API with SQL-like queries (`SELECT`, `WHERE`, `ORDER BY`, `OFFSET/LIMIT`, aggregate functions). ### 2.3 Code Investment by Language | Language | Azure SDK Packages | Files Using Azure | Lines of Azure-Specific Code | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | ---------------------------- | | **TypeScript** | `@azure/cosmos`, `@azure/storage-blob`, `@azure/identity`, `@azure/keyvault-secrets` | ~65 files | ~500 lines | | **Python** | `azure-cognitiveservices-speech`, `azure-cosmos`, `azure-storage-blob`, `azure-identity`, `azure-keyvault-secrets`, `openai` (AzureOpenAI) | ~8 files | ~400 lines | | **Swift** | `MicrosoftCognitiveServicesSpeech` (SPX framework) | ~3 files | ~150 lines | | **Kotlin** | None directly (uses platform-service REST API) | 0 files | 0 lines | --- ## 3. Dependency Depth Analysis ### 3.1 Cosmos DB — DEEP (56+ files) This is the **most deeply embedded** Azure dependency. Every repository module follows the pattern: ``` types.ts → repository.ts → routes.ts ↑ Uses @azure/cosmos SDK SQL queries: SELECT c.id, c.name FROM c WHERE c.productId = @pid ``` **Touchpoints:** - `packages/cosmos/` — shared client singleton (`@azure/cosmos` peer dep) - `services/platform-service/src/modules/*/repository.ts` — **56 repository files** with Cosmos SQL queries - `services/extraction-service/src/modules/*/repository.ts` — 2 repository files - `dashboards/admin-web/src/lib/cosmos.ts` — direct `@azure/cosmos` import - `dashboards/admin-web/src/lib/repositories/*.ts` — 4 repository files - `mindlyst-native/web/src/lib/cosmos.ts` — direct `@azure/cosmos` import - `learning_voice_ai_agent/src/cloud/cosmos_client.py` — Python Cosmos client - `learning_voice_ai_agent/backend/src/cloud/cosmos.py` — Python backend Cosmos client **Query patterns used:** - `container.items.query()` with parameterized SQL - `container.items.create()`, `.replace()`, `.delete()`, `.read()` - `container.items.upsert()` - Partition key routing (`/userId`, `/productId`, `/id`) - Cross-partition queries (admin/analytics) - `SELECT VALUE COUNT(1)` aggregates - `OFFSET ... LIMIT` pagination - `ORDER BY` sorting - `ARRAY_CONTAINS()` for array queries ### 3.2 Azure Speech SDK — DEEP (3 files, streaming integration) The Speech SDK is used for **real-time streaming speech-to-text** with features that are tightly coupled to the Azure SDK's event-driven architecture: - `src/audio/azure_stt.py` — 248 lines. Uses `PushAudioInputStream`, `SpeechRecognizer`, continuous recognition with `recognizing`/`recognized`/`canceled`/`session_stopped` event callbacks, `PhraseListGrammar`, auto-language detection (10 languages), auto-reconnect - `src/ui/settings.py` + `src/ui/unified_window.py` — connection testing - `mindlyst-native/iosApp/Services/AzureSpeechTranscriber.swift` — iOS Swift SPX framework - `mobile_app/ios/LysnrAI/` — iOS keyboard extension uses SPX framework ### 3.3 Blob Storage — SHALLOW (3 files) - `packages/blob/src/blob.ts` — 162 lines, singleton client, SAS URL generation - `src/cloud/blob_client.py` — 190 lines, Python equivalent - `services/platform-service/src/modules/blob/` — REST API wrapper ### 3.4 Azure OpenAI — SHALLOW (3 files, already abstracted) - `src/llm/text_cleaner.py` — uses `openai.AzureOpenAI` (OpenAI SDK with Azure endpoint) - `backend/src/clients/openai_client.py` — uses `openai.AsyncAzureOpenAI` - `mindlyst-native/web/src/lib/llm.ts` — **already has OpenAI fallback** (resolves provider dynamically) The `openai` Python/JS SDK supports both Azure and OpenAI endpoints with minimal config change. MindLyst web already handles this automatically. ### 3.5 Key Vault — SHALLOW (2 files) - `packages/config/src/keyvault.ts` — 90 lines, `resolveKeyVaultSecrets()` with graceful fallback - `src/secrets/keyvault.py` — 69 lines, `SecretResolver` class with env var fallback Both implementations already fall back to environment variables when Key Vault is unavailable. Migration = just stop using Key Vault and use the env var path. ### 3.6 Notification Hubs — NOT YET INTEGRATED Planned but not deeply wired. Only namespace/hub exists in Azure. Mobile apps use `BLPlatformClient` (REST) to talk to platform-service, which would route push notifications. ### 3.7 Application Insights — SHALLOW (1 file) - `opencensus-ext-azure` in Python requirements (optional telemetry) - Custom telemetry system already built (`@bytelyst/telemetry-client`, platform-service telemetry module with Cosmos storage) The custom telemetry system means App Insights is supplementary, not critical. --- ## 4. Migration Target Comparison ### 4.1 Database: Cosmos DB → Alternatives | Feature | Azure Cosmos DB (current) | MongoDB Atlas | AWS DynamoDB | Google Firestore | PostgreSQL (Supabase/Neon) | | ----------------------- | ----------------------------------- | ------------------------------------ | ---------------------------------- | --------------------- | -------------------------- | | **Data model** | Document (JSON) | Document (JSON) | Key-Value + Document | Document (JSON) | Relational + JSONB | | **Query language** | SQL-like | MQL (MongoDB Query) | PartiQL / API | GQL-like API | SQL | | **Partition keys** | Required | Shard keys (optional) | Required | Collection groups | Not applicable | | **Serverless** | Yes | Yes (Atlas Serverless) | Yes | Yes | Yes (Neon) | | **SQL queries** | `SELECT c.id FROM c WHERE c.x = @y` | `db.collection.find({x: y})` | `SELECT id FROM table WHERE x = ?` | Client SDK queries | Standard SQL | | **Aggregates** | Basic (`COUNT`, `SUM`, `AVG`) | Full (`$group`, `$match`, `$lookup`) | Limited | Limited | Full SQL | | **Cross-partition** | Yes (expensive) | Yes (scatter-gather) | Scan (expensive) | Yes | N/A | | **Change feed** | Yes | Change Streams | DynamoDB Streams | Real-time listeners | Logical replication | | **Global distribution** | Built-in multi-region | Atlas Global Clusters | Global Tables | Multi-region | Manual / Citus | | **Max doc size** | 2 MB | 16 MB | 400 KB | 1 MB | Unlimited (JSONB) | | **Free tier** | 1000 RU/s + 25 GB | 512 MB | 25 GB + 25 WCU/RCU | 1 GiB + 50K reads/day | 0.5 GB (Neon) | | **Migration effort** | — | **Medium** (query rewrite) | **Hard** (paradigm shift) | **Hard** (no SQL) | **Hard** (schema design) | ### 4.2 Object Storage: Blob → Alternatives | Feature | Azure Blob (current) | AWS S3 | GCP Cloud Storage | Cloudflare R2 | MinIO (self-hosted) | | --------------------- | -------------------- | ----------------- | ------------------- | ------------------ | ------------------- | | **API compatibility** | Azure Blob API | S3 API | GCS API / S3-compat | S3-compatible | S3-compatible | | **SAS tokens** | Yes (Azure SAS) | Pre-signed URLs | Signed URLs | Pre-signed URLs | Pre-signed URLs | | **CDN integration** | Azure CDN | CloudFront | Cloud CDN | Built-in | Manual | | **Cost (per GB)** | $0.018 (Cool) | $0.023 (Standard) | $0.020 | $0.015 (no egress) | Self-hosted | | **Migration effort** | — | **Easy** | **Easy** | **Easy** | **Easy** | ### 4.3 Speech-to-Text: Azure Speech → Alternatives | Feature | Azure Speech (current) | AWS Transcribe | Google Speech-to-Text | Deepgram | Whisper (local) | | --------------------- | ----------------------- | --------------------- | ------------------------ | ---------------------- | ----------------------- | | **Streaming STT** | Yes (push stream) | Yes (WebSocket) | Yes (streaming) | Yes (WebSocket) | No (batch only) | | **Languages** | 100+ | 100+ | 125+ | 36+ | 99+ | | **Auto-detect lang** | Up to 10 at-once | Yes | Yes | Yes | Yes | | **Custom vocabulary** | PhraseListGrammar | Custom vocabulary | Speech adaptation | Keywords | No | | **Native SDK** | Python, Swift (SPX), JS | Python, no iOS SDK | Python, iOS, JS | REST/WebSocket | Python only | | **iOS native SDK** | SPX framework (ObjC) | No native SDK | Yes (gRPC) | No native SDK | No | | **Free tier** | 5 hrs/month (F0) | 60 min/month | 60 min/month | None | Free (local GPU) | | **Latency** | ~200ms | ~300ms | ~200ms | ~100ms | ~500ms+ (local) | | **Migration effort** | — | **Hard** (no iOS SDK) | **Medium** (has iOS SDK) | **Medium** (REST only) | **Hard** (no streaming) | ### 4.4 LLM / AI: Azure OpenAI → Alternatives | Feature | Azure OpenAI (current) | OpenAI API (direct) | Google Gemini | AWS Bedrock | Anthropic Claude | | ---------------------- | ------------------------ | ----------------------------- | -------------------- | -------------------- | -------------------- | | **Models** | GPT-4o, GPT-4o-mini | Same models | Gemini 2.5 | Claude, Llama, Titan | Claude 3.5/4 | | **API compatibility** | OpenAI SDK (azure mode) | OpenAI SDK (native) | Google SDK | AWS SDK | Anthropic SDK | | **Data residency** | Azure regions | US only | Google regions | AWS regions | US/EU | | **Cost (GPT-4o-mini)** | $0.15/$0.60 per M tokens | $0.15/$0.60 per M tokens | ~$0.10/$0.40 (Flash) | Varies | ~$0.25/$1.25 (Haiku) | | **Migration effort** | — | **Trivial** (change endpoint) | **Easy** (SDK swap) | **Medium** | **Easy** (SDK swap) | ### 4.5 Secrets Management: Key Vault → Alternatives | Feature | Azure Key Vault (current) | AWS Secrets Manager | GCP Secret Manager | HashiCorp Vault | Doppler / Infisical | | -------------------- | ------------------------- | --------------------------------- | ------------------------------ | --------------- | ------------------- | | **Cost** | $0.03/10K ops | $0.40/secret/month | $0.06/10K ops | Free (OSS) | Free tier | | **SDK** | `@azure/keyvault-secrets` | `@aws-sdk/client-secrets-manager` | `@google-cloud/secret-manager` | HTTP API | SDK / CLI | | **Migration effort** | — | **Easy** | **Easy** | **Medium** | **Easy** | **Note:** The codebase already falls back to env vars when Key Vault is unavailable. This means Key Vault can be replaced by **any** secrets manager or simply .env files without code changes to application logic. ### 4.6 Push Notifications: Notification Hubs → Alternatives | Feature | Azure NH (current) | AWS SNS | Firebase Cloud Messaging | OneSignal | Expo Push | | -------------------- | ------------------ | ------------ | ------------------------ | --------------- | --------------------------- | | **APNs + FCM** | Yes | Yes | FCM only (APNs via FCM) | Yes | Yes | | **Free tier** | 1M pushes/month | 1M publishes | Unlimited | 10K subscribers | Unlimited | | **Migration effort** | — | **Easy** | **Easy** | **Easy** | **Easy** (NomGap uses Expo) | --- ## 5. Per-Service Migration Analysis ### 5.1 Cosmos DB → MongoDB Atlas **Difficulty: MEDIUM-HIGH** | **Effort: 3–5 weeks** | **Risk: MEDIUM** This is the **single largest migration task**. Here's why: #### What needs to change | Layer | Current (Cosmos SQL API) | Target (MongoDB) | Files | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | ----------------------------------- | | Client package | `@azure/cosmos` → `CosmosClient` | `mongodb` → `MongoClient` | `packages/cosmos/src/client.ts` | | Container registry | `getContainer(name)` | `db.collection(name)` | `packages/cosmos/src/containers.ts` | | All repository files | `container.items.query('SELECT...')` | `collection.find({...})` | **56+ files** in platform-service | | Dashboard Cosmos clients | `@azure/cosmos` direct | `mongodb` direct | 2 files (admin, MindLyst) | | Python clients | `azure.cosmos.CosmosClient` | `pymongo.MongoClient` | 2 files | | Query syntax | SQL-like (`SELECT c.id FROM c WHERE c.productId = @pid AND c.userId = @uid ORDER BY c.createdAt DESC OFFSET 0 LIMIT 20`) | MQL (`collection.find({productId: pid, userId: uid}).sort({createdAt: -1}).skip(0).limit(20)`) | All repository files | | Partition keys | Explicit partition key in every query | Shard key (auto-routed) | All repository files | | Upsert | `container.items.upsert(doc)` | `collection.updateOne({_id: id}, {$set: doc}, {upsert: true})` | ~20 files | | Read by ID | `container.item(id, partitionKey).read()` | `collection.findOne({_id: id})` | All repository files | #### What stays the same - Document structure (JSON documents with `id`, `productId`, partition keys) - Data model (no schema changes needed — MongoDB is also schemaless) - Partition key concept maps to shard key - Serverless pricing model available on both #### Key migration steps 1. Update `@bytelyst/cosmos` package to export MongoDB-compatible API 2. Rewrite all SQL queries to MQL (56+ files) 3. Replace `container.items.query()` → `collection.find()` 4. Replace `container.item(id, pk).read()` → `collection.findOne({_id: id})` 5. Replace `container.items.create()` → `collection.insertOne()` 6. Replace `container.items.replace()` → `collection.replaceOne()` 7. Replace `container.items.upsert()` → `collection.updateOne({upsert: true})` 8. Update Python clients similarly 9. Migrate data (use Azure Data Factory or custom script) 10. Update all test mocks #### Why MongoDB Atlas is the best DB alternative - **Closest query model** to Cosmos SQL API (both are document DBs) - **MongoDB has a Cosmos DB compatibility mode** (but going native is better) - Cosmos DB was originally inspired by MongoDB's document model - MongoDB's `find()` queries map closely to Cosmos SQL `SELECT` queries - Both support partition/shard keys, TTL indexes, change streams - MongoDB Atlas Serverless pricing is competitive - MongoDB has excellent TypeScript and Python SDKs ### 5.2 Azure Speech → Google Cloud Speech-to-Text **Difficulty: HIGH** | **Effort: 2–3 weeks** | **Risk: HIGH** #### Why this is hard - The Azure Speech SDK uses a **push-stream architecture** (`PushAudioInputStream`) that is deeply integrated into the audio pipeline - The `SpeechRecognizer` has event-driven callbacks (`recognizing`, `recognized`, `canceled`, `session_stopped`) that the code relies on for real-time partial/final transcript delivery - Custom vocabulary via `PhraseListGrammar` is Azure-specific - Auto-language detection config is Azure-specific - The **iOS SPX framework** (Objective-C) is used in LysnrAI keyboard extension and MindLyst — there's no direct equivalent for most alternatives #### Best alternative: Google Cloud Speech-to-Text - Has streaming recognition with similar event model - Has an iOS SDK (gRPC-based) - Supports custom vocabulary (speech adaptation) - Supports auto-language detection - Similar pricing and free tier #### What needs to change - `src/audio/azure_stt.py` — complete rewrite (~248 lines) - `iosApp/Services/AzureSpeechTranscriber.swift` — complete rewrite - `LysnrAI/LysnrKeyboard/` — keyboard extension STT integration - Audio format handling (may differ between providers) - Connection test code in settings UI ### 5.3 Blob Storage → AWS S3 or Cloudflare R2 **Difficulty: LOW** | **Effort: 2–3 days** | **Risk: LOW** #### Why this is easy - `@bytelyst/blob` package is a thin wrapper (162 lines) - Only 3 files need changes - S3 API is the de facto standard — R2, MinIO, GCS all support S3-compatible API - SAS tokens → Pre-signed URLs (same concept, different implementation) #### What needs to change - `packages/blob/src/blob.ts` — swap `@azure/storage-blob` → `@aws-sdk/client-s3` + `@aws-sdk/s3-request-presigner` - `src/cloud/blob_client.py` — swap `azure.storage.blob` → `boto3` - `services/platform-service/src/modules/blob/` — update routes for pre-signed URL format - Environment variables: `AZURE_BLOB_*` → `AWS_S3_*` or `S3_*` ### 5.4 Azure OpenAI → OpenAI API (direct) or Gemini **Difficulty: TRIVIAL** | **Effort: < 1 day** | **Risk: VERY LOW** #### Why this is trivial - The `openai` Python SDK supports both Azure and OpenAI endpoints — just change config - MindLyst web `llm.ts` **already auto-detects** Azure vs OpenAI and builds the correct URL - LysnrAI desktop uses `AzureOpenAI` class from `openai` SDK — switch to `OpenAI` class - Same models, same API shape, same pricing #### What needs to change - Set `OPENAI_API_KEY` instead of `AZURE_OPENAI_*` env vars - Change `AzureOpenAI(azure_endpoint=..., api_key=..., api_version=...)` → `OpenAI(api_key=...)` - Change `AsyncAzureOpenAI(...)` → `AsyncOpenAI(...)` - Remove `api_version` parameter - That's it. The `openai` SDK handles the rest. ### 5.5 Key Vault → Environment Variables / Any Secrets Manager **Difficulty: TRIVIAL** | **Effort: < 1 day** | **Risk: VERY LOW** Both `keyvault.ts` and `keyvault.py` already implement graceful fallback: - If `AZURE_KEYVAULT_URL` is not set → uses env vars directly - If Key Vault is unreachable → falls back to env vars **To migrate:** Simply stop setting `AZURE_KEYVAULT_URL`. Everything works via env vars. Then optionally adopt any other secrets manager (AWS Secrets Manager, Doppler, Infisical, etc.). ### 5.6 Notification Hubs → Firebase Cloud Messaging **Difficulty: LOW** | **Effort: 1–2 days** | **Risk: LOW** Not yet deeply integrated. The platform-service notification module sends via REST API. Swap the push provider client. ### 5.7 Application Insights → Self-hosted / Grafana **Difficulty: TRIVIAL** | **Effort: Already done** | **Risk: NONE** The ecosystem already has: - Custom telemetry system (`@bytelyst/telemetry-client` → platform-service → Cosmos) - Loki + Grafana in `services/monitoring/` - App Insights is supplementary, can be dropped with zero code changes --- ## 6. Migration Scenario Scoring ### Scenario A: Stay on Azure (Status Quo) | Dimension | Score (1-5) | Notes | | ---------------- | ------------ | ---------------------------------------- | | Migration effort | **5** (none) | No work needed | | Cost | **4** | ~$15/month at current scale, competitive | | Vendor diversity | **1** | Single cloud vendor | | Feature parity | **5** | Everything works today | | **Total** | **15/20** | | ### Scenario B: Full Migration to AWS | Dimension | Score (1-5) | Notes | | ---------------- | ----------- | ---------------------------------------------------------------- | | Migration effort | **2** | 6–8 weeks, Cosmos→DynamoDB is painful | | Cost | **3** | Similar or slightly higher at small scale | | Vendor diversity | **1** | Still single cloud, just different | | Feature parity | **3** | No native iOS Speech SDK, DynamoDB query model is very different | | **Total** | **9/20** | | ### Scenario C: Multi-Cloud (MongoDB Atlas + OpenAI + R2 + Google STT) | Dimension | Score (1-5) | Notes | | ---------------- | ----------- | -------------------------------------------------------- | | Migration effort | **2** | 5–7 weeks, Cosmos→MongoDB is medium | | Cost | **4** | MongoDB Atlas free tier, R2 no egress fees | | Vendor diversity | **5** | No single-vendor dependency | | Feature parity | **4** | MongoDB is a better document DB than Cosmos in many ways | | **Total** | **15/20** | | ### Scenario D: Stay Azure + Add Abstraction Layers | Dimension | Score (1-5) | Notes | | ---------------- | ----------- | --------------------------------------------- | | Migration effort | **4** | 1–2 weeks to add repository interface pattern | | Cost | **4** | No change | | Vendor diversity | **3** | Ready to switch, but still on Azure | | Feature parity | **5** | Everything works today | | **Total** | **16/20** | **Winner** | ### Scenario E: Migrate DB Only (Cosmos → MongoDB Atlas, keep rest on Azure) | Dimension | Score (1-5) | Notes | | ---------------- | ----------- | --------------------------------------------- | | Migration effort | **3** | 3–5 weeks for DB migration | | Cost | **4** | MongoDB Atlas Serverless may be cheaper | | Vendor diversity | **3** | DB is independent, other services still Azure | | Feature parity | **5** | MongoDB is very capable | | **Total** | **15/20** | | --- ## 7. Cost Comparison ### Current Azure Costs (MVP / Low Usage) | Service | Monthly Cost | Notes | | -------------------------- | ----------------- | --------------------------- | | Cosmos DB (Serverless) | ~$4–10 | 3 databases, ~45 containers | | Blob Storage (Cool, RAGRS) | ~$0.20 | 9+ containers | | Azure OpenAI (GPT-4o-mini) | ~$5–10 | Pay per token | | Speech (F0) | $0 | 5 hrs/month free | | Key Vault | ~$0.06 | ~25 secrets | | Notification Hubs (Free) | $0 | 1M pushes/month | | App Insights | $0 | 5 GB/month free | | **Total** | **~$10–20/month** | | ### Equivalent AWS Costs | Service | AWS Equivalent | Monthly Cost | | -------------------------------- | --------------- | ------------------------- | | Cosmos DB → DynamoDB (On-Demand) | DynamoDB | ~$5–15 | | Blob → S3 Standard | S3 | ~$0.25 | | Azure OpenAI → OpenAI API | Same pricing | ~$5–10 | | Speech → Transcribe | Transcribe | ~$1–3 | | Key Vault → Secrets Manager | Secrets Manager | ~$10 (per-secret pricing) | | Notification Hubs → SNS | SNS | ~$0.50 | | App Insights → CloudWatch | CloudWatch | ~$3 | | **Total** | | **~$25–42/month** | ### Equivalent Multi-Cloud Costs | Service | Provider | Monthly Cost | | ------------------------------------ | ------------ | ------------------ | | Cosmos DB → MongoDB Atlas Serverless | MongoDB | ~$3–8 | | Blob → Cloudflare R2 | Cloudflare | ~$0.15 (no egress) | | Azure OpenAI → OpenAI API (direct) | OpenAI | ~$5–10 | | Speech → Google STT | Google Cloud | ~$1–3 | | Key Vault → Doppler (free tier) | Doppler | $0 | | Push → Firebase FCM | Google | $0 | | Monitoring → Grafana Cloud (free) | Grafana | $0 | | **Total** | | **~$10–22/month** | ### Cost Summary | Scenario | Monthly Cost | vs Current | | ------------------------------ | ------------ | ---------- | | **Azure (current)** | ~$10–20 | Baseline | | **Full AWS** | ~$25–42 | +50–110% | | **Multi-cloud** | ~$10–22 | ~Same | | **MongoDB Atlas + Azure rest** | ~$10–18 | ~Same | **Verdict:** At current scale, cost is not a compelling reason to migrate. All options are under $50/month. Cost becomes more significant at scale (10K+ users), where MongoDB Atlas and R2 would likely be cheaper due to no egress fees and better serverless pricing. --- ## 8. Abstraction Layer Assessment ### Current State: Partially Abstracted The codebase already has meaningful abstraction: | Layer | Abstraction Level | Notes | | ---------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------- | | **Cosmos DB** | **Partial** — `@bytelyst/cosmos` package | Application code still writes raw SQL queries and uses `@azure/cosmos` types | | **Blob Storage** | **Good** — `@bytelyst/blob` package | Thin wrapper, easy to swap internals | | **OpenAI/LLM** | **Good** — MindLyst has provider auto-detection | LysnrAI desktop/backend hardcodes `AzureOpenAI` | | **Key Vault** | **Excellent** — graceful fallback to env vars | Already cloud-agnostic in practice | | **Speech** | **None** — raw SDK usage | Deep Azure SDK coupling in 3 files | | **Auth (JWT)** | **Excellent** — uses `jose` library | No cloud dependency | | **Push notifications** | **Good** — platform-service abstraction | Swap provider client only | ### What's Missing: Repository Interface Pattern The biggest gap is that repository files directly use `@azure/cosmos` types and SQL query syntax. To make the DB layer swappable, you'd need: ```typescript // Proposed: packages/cosmos/src/repository.ts export interface DocumentRepository { findById(id: string, partitionKey: string): Promise; findMany(filter: Record, opts?: QueryOptions): Promise; create(doc: T): Promise; replace(id: string, doc: T, partitionKey: string): Promise; upsert(doc: T): Promise; delete(id: string, partitionKey: string): Promise; count(filter: Record): Promise; } ``` This would allow swapping Cosmos → MongoDB → PostgreSQL behind the interface without touching 56+ repository files. **Effort to add:** 1–2 weeks. This is the **highest-ROI investment** regardless of migration decision. --- ## 9. Risk Analysis ### 9.1 Risks of Staying on Azure | Risk | Likelihood | Impact | Mitigation | | -------------------------------- | ---------- | ------ | ---------------------------------------------------------- | | Azure pricing increases | Low | Medium | Add abstraction layer for future portability | | Azure outage | Low | High | Multi-region already possible (Cosmos global distribution) | | Feature stagnation | Very Low | Low | Azure is investing heavily in AI services | | Vendor lock-in deepens over time | Medium | Medium | Add abstraction layers proactively | ### 9.2 Risks of Migrating | Risk | Likelihood | Impact | Mitigation | | --------------------------------------- | ---------- | -------- | ---------------------------------------------------------- | | Data loss during migration | Low | Critical | Test migration on staging first, keep Azure as backup | | Query performance differences | Medium | Medium | Benchmark before committing | | Feature gaps in new provider | Medium | Medium | Prototype critical features first | | Wasted engineering time | Medium | High | Only migrate if there's a clear business driver | | Regression bugs in 56+ repository files | High | Medium | Comprehensive test suite (1,029 tests) catches most issues | | Speech quality degradation | Medium | High | A/B test both providers before committing | ### 9.3 Azure-Specific Lock-in Risks (ranked) | # | Component | Lock-in Level | Escape Hatch | | --- | ------------------------------------------- | ------------- | ---------------------------------------------------------- | | 1 | **Cosmos DB SQL API** | High | Rewrite queries to MongoDB MQL or add repository interface | | 2 | **Azure Speech SDK (streaming)** | High | Google STT has comparable streaming API | | 3 | **Azure Identity (DefaultAzureCredential)** | Medium | Only used by Key Vault, which is already optional | | 4 | **Blob Storage SAS tokens** | Low | Pre-signed URLs are equivalent across all providers | | 5 | **Azure OpenAI** | Very Low | OpenAI SDK works with both — 1-line config change | | 6 | **Key Vault** | Very Low | Already has env var fallback | | 7 | **Notification Hubs** | Very Low | Not deeply integrated yet | | 8 | **Application Insights** | None | Custom telemetry already built | --- ## 10. Recommendations ### Recommended Strategy: **Stay on Azure + Invest in Abstraction** (Scenario D) This is the highest-scoring approach. Here's the prioritized action plan: #### Phase 1: Add Repository Interface (1–2 weeks) - Create `DocumentRepository` interface in `@bytelyst/cosmos` - Implement `CosmosDocumentRepository` that wraps current `@azure/cosmos` calls - Gradually migrate the 56 repository files to use the interface - This makes future DB migration a matter of implementing `MongoDocumentRepository` — no application code changes needed #### Phase 2: Normalize LLM Abstraction (2–3 days) - Move LysnrAI desktop/backend from `AzureOpenAI` → auto-detecting provider pattern (like MindLyst web already does) - Support `OPENAI_PROVIDER=azure|openai|gemini` across all repos - This makes LLM provider swappable via config #### Phase 3: Speech Abstraction Layer (1 week, optional) - Create `SpeechTranscriber` protocol/interface - Implement `AzureSpeechTranscriber` (current code, extracted) - Prepare `GoogleSpeechTranscriber` stub for future use - This is lower priority since Azure Speech F0 tier is free #### Phase 4: Document Decision Criteria for Future Migration - Define triggers that would justify migration (e.g., cost > $X/month, Azure outage > Y hours, need for feature Z) - Review annually ### Why NOT Migrate Now 1. **Cost is negligible** — ~$10–20/month doesn't justify weeks of engineering 2. **No business driver** — Azure isn't blocking any feature development 3. **Risk/reward is unfavorable** — 4–8 weeks of migration work for ~$0 cost savings 4. **Test coverage is good but not perfect** — 1,029 tests cover most paths, but query-level changes in 56 files still risk regressions 5. **Azure free tiers are generous** — Speech F0, Notification Hubs Free, App Insights free tier ### When Migration WOULD Make Sense - **Cosmos DB costs exceed $100/month** → Consider MongoDB Atlas Serverless - **Azure Speech quality is insufficient** → Evaluate Google STT or Deepgram - **Enterprise customer requires specific cloud** → Build the repository interface, then implement their cloud backend - **Azure has extended outage affecting your region** → Multi-region or multi-cloud - **You want to go fully open-source** → PostgreSQL (Supabase) + Whisper + MinIO (significant rewrite) --- ## 11. Migration Playbook (If Chosen) If you decide to migrate in the future, here's the execution order (shortest critical path): ### Week 1–2: Database Abstraction 1. Create `DocumentRepository` interface 2. Implement `CosmosDocumentRepository` (wraps current code) 3. Migrate all 56 repository files to use interface 4. Verify all 1,029 tests pass ### Week 3–4: Database Migration (Cosmos → MongoDB) 1. Implement `MongoDocumentRepository` 2. Set up MongoDB Atlas Serverless cluster 3. Write data migration script (Cosmos → MongoDB) 4. Run migration on staging, verify data integrity 5. Switch repository implementation via config flag 6. Run full test suite against MongoDB ### Week 5: Storage + Secrets 1. Swap `@bytelyst/blob` internals to S3-compatible client 2. Migrate blobs (azcopy → aws s3 sync or similar) 3. Replace Key Vault with new secrets manager (or just env vars) 4. Update all environment variable names ### Week 6: LLM + Speech (if needed) 1. Switch OpenAI from Azure endpoint to direct (config change only) 2. If migrating Speech: rewrite `azure_stt.py` and Swift `AzureSpeechTranscriber` 3. A/B test new speech provider against Azure ### Week 7–8: Cleanup + Verification 1. Remove all `@azure/*` npm packages 2. Remove all `azure-*` pip packages 3. Update Docker configs, CI/CD 4. Update documentation 5. Monitor production for 2 weeks --- ## Appendix A: File-Level Azure Dependency Map ### TypeScript — `@azure/cosmos` (CRITICAL) | File | Repo | Direct Import | | ------------------------------------------------------------------- | ----------- | ---------------------- | | `packages/cosmos/src/client.ts` | common-plat | `@azure/cosmos` | | `packages/cosmos/src/containers.ts` | common-plat | `@azure/cosmos` | | `services/platform-service/src/modules/*/repository.ts` (56 files) | common-plat | Via `@bytelyst/cosmos` | | `services/extraction-service/src/modules/*/repository.ts` (2 files) | common-plat | Via `@bytelyst/cosmos` | | `dashboards/admin-web/src/lib/cosmos.ts` | common-plat | `@azure/cosmos` | | `dashboards/admin-web/src/lib/repositories/*.ts` (4 files) | common-plat | Via cosmos.ts | | `mindlyst-native/web/src/lib/cosmos.ts` | MindLyst | `@azure/cosmos` | ### TypeScript — `@azure/storage-blob` | File | Repo | Direct Import | | --------------------------- | ----------- | --------------------- | | `packages/blob/src/blob.ts` | common-plat | `@azure/storage-blob` | ### TypeScript — `@azure/identity` + `@azure/keyvault-secrets` | File | Repo | Direct Import | | ------------------------------------------------------- | ----------- | ------------------------- | | `packages/config/src/keyvault.ts` | common-plat | Dynamic import (both) | | `dashboards/admin-web/src/app/api/ops/secrets/route.ts` | common-plat | Both (Secrets Manager UI) | ### Python — Azure SDKs | File | Repo | SDK | | -------------------------------------- | ------- | ------------------------------------------ | | `src/audio/azure_stt.py` | LysnrAI | `azure.cognitiveservices.speech` | | `src/cloud/cosmos_client.py` | LysnrAI | `azure.cosmos` | | `src/cloud/blob_client.py` | LysnrAI | `azure.storage.blob` | | `src/secrets/keyvault.py` | LysnrAI | `azure.identity`, `azure.keyvault.secrets` | | `backend/src/secrets/keyvault.py` | LysnrAI | `azure.identity`, `azure.keyvault.secrets` | | `backend/src/cloud/cosmos.py` | LysnrAI | `azure.cosmos` | | `src/llm/text_cleaner.py` | LysnrAI | `openai.AzureOpenAI` | | `backend/src/clients/openai_client.py` | LysnrAI | `openai.AsyncAzureOpenAI` | ### Swift — Azure Speech SDK | File | Repo | SDK | | ---------------------------------------------------- | -------- | ---------------------------------- | | `iosApp/Services/AzureSpeechTranscriber.swift` | MindLyst | `MicrosoftCognitiveServicesSpeech` | | `LysnrAI/LysnrKeyboard/KeyboardViewController.swift` | LysnrAI | SPX framework (via CocoaPods) | --- ## Appendix B: SDK & Package Inventory ### npm packages (TypeScript) | Package | Version | Used By | Swappable | | ------------------------- | ------- | ------------------------------------------- | ---------------------- | | `@azure/cosmos` | ≥4.0.0 | `@bytelyst/cosmos`, admin-web, MindLyst web | Medium (query rewrite) | | `@azure/storage-blob` | ≥12.0.0 | `@bytelyst/blob` | Easy (S3 compat) | | `@azure/identity` | latest | `@bytelyst/config`, admin-web secrets | Easy (remove) | | `@azure/keyvault-secrets` | latest | `@bytelyst/config`, admin-web secrets | Easy (remove) | ### pip packages (Python) | Package | Version | Used By | Swappable | | -------------------------------- | -------- | ---------------------------------- | --------------------------- | | `azure-cognitiveservices-speech` | ≥1.42.0 | Desktop STT | Hard (deep SDK integration) | | `azure-cosmos` | latest | Desktop + backend Cosmos client | Medium (pymongo swap) | | `azure-storage-blob` | ≥12.24.0 | Desktop blob client | Easy (boto3 swap) | | `azure-identity` | ≥1.19.0 | Key Vault auth | Easy (remove) | | `azure-keyvault-secrets` | ≥4.9.0 | Secrets resolver | Easy (remove) | | `openai` | ≥1.60.0 | `AzureOpenAI` / `AsyncAzureOpenAI` | Trivial (change class name) | | `opencensus-ext-azure` | ≥1.1.0 | Optional telemetry | Trivial (remove) | ### Swift packages / CocoaPods | Package | Used By | Swappable | | ---------------------------------------- | ------------------------- | ------------------------------------- | | `MicrosoftCognitiveServicesSpeech` (SPX) | LysnrAI iOS, MindLyst iOS | Hard (need alternative streaming STT) | --- _Document generated by automated codebase analysis. Numbers are accurate as of 2026-03-01. Update as the codebase evolves._