# Environment Variables & Azure Key Vault Audit

> **Last Updated:** 2026-02-15  
> **Purpose:** Complete audit of environment variables, Azure Key Vault secrets, and gap analysis

---

## 🎯 Executive Summary

### Current Status:
1. ✅ **All 13 LysnrAI secrets** seeded into Azure Key Vault (completed 2026-02-15)
2. ✅ **MindLyst secrets** fully populated (12 secrets)
3. ✅ **MyWisprAI secrets** populated (5 legacy `wispr-*` secrets)
4. ⚠️ **Next action:** Rotate keys exposed in git history (see `AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md`)

---

## 📊 Key Vault Current State

### Azure Key Vault: `kv-mywisprai`
**Location:** East US  
**Total Secrets:** 17

| Secret Name | Product | Purpose | Created | Status |
|-------------|---------|---------|---------|--------|
| `mindlyst-appinsights-connection-string` | MindLyst | Application Insights telemetry | 2026-02-14 | ✅ Active |
| `mindlyst-blob-connection-string` | MindLyst | Blob Storage access | 2026-02-14 | ✅ Active |
| `mindlyst-cosmos-database` | MindLyst | Cosmos DB database name | 2026-02-14 | ✅ Active |
| `mindlyst-cosmos-endpoint` | MindLyst | Cosmos DB endpoint | 2026-02-14 | ✅ Active |
| `mindlyst-cosmos-key` | MindLyst | Cosmos DB primary key | 2026-02-14 | ✅ Active |
| `mindlyst-notification-hub-connection-string` | MindLyst | Push notifications | 2026-02-14 | ✅ Active |
| `mindlyst-openai-api-version` | MindLyst | OpenAI API version | 2026-02-14 | ✅ Active |
| `mindlyst-openai-deployment` | MindLyst | OpenAI deployment name | 2026-02-14 | ✅ Active |
| `mindlyst-openai-endpoint` | MindLyst | Azure OpenAI endpoint | 2026-02-14 | ✅ Active |
| `mindlyst-openai-key` | MindLyst | Azure OpenAI key | 2026-02-14 | ✅ Active |
| `mindlyst-speech-key` | MindLyst | Azure Speech Services key | 2026-02-14 | ✅ Active |
| `mindlyst-speech-region` | MindLyst | Azure Speech region | 2026-02-14 | ✅ Active |
| `wispr-azure-openai-deployment` | MyWisprAI | OpenAI deployment name | 2026-02-07 | ✅ Active |
| `wispr-azure-openai-endpoint` | MyWisprAI | Azure OpenAI endpoint | 2026-02-07 | ✅ Active |
| `wispr-azure-openai-key` | MyWisprAI | Azure OpenAI key | 2026-02-07 | ✅ Active |
| `wispr-azure-speech-key` | MyWisprAI | Azure Speech Services key | 2026-02-07 | ✅ Active |
| `wispr-azure-speech-region` | MyWisprAI | Azure Speech region | 2026-02-07 | ✅ Active |

---

## 🔍 Code Expectations vs Reality

### Expected Secrets (from `packages/config/src/keyvault.ts`)

The `LYSNR_SECRETS` constant defines these mappings:

| Key Vault Secret Name | Environment Variable | Status in KV | Priority |
|-----------------------|---------------------|--------------|----------|
| `lysnr-cosmos-key` | `COSMOS_KEY` | ✅ **Seeded** | 🔴 Critical |
| `lysnr-cosmos-endpoint` | `COSMOS_ENDPOINT` | ✅ **Seeded** | 🔴 Critical |
| `lysnr-jwt-secret` | `JWT_SECRET` | ✅ **Seeded** | 🔴 Critical |
| `lysnr-stripe-secret-key` | `STRIPE_SECRET_KEY` | ✅ **Seeded** | 🔴 Critical |
| `lysnr-stripe-webhook-secret` | `STRIPE_WEBHOOK_SECRET` | ✅ **Seeded** | 🔴 Critical |
| `lysnr-billing-internal-key` | `BILLING_INTERNAL_KEY` | ✅ **Seeded** | 🟠 High |
| `lysnr-blob-connection-string` | `AZURE_BLOB_CONNECTION_STRING` | ✅ **Seeded** | 🔴 Critical |
| `lysnr-blob-account-key` | `AZURE_BLOB_ACCOUNT_KEY` | ✅ **Seeded** | 🔴 Critical |
| `lysnr-gemini-api-key` | `GEMINI_API_KEY` | ✅ **Seeded** | 🔴 Critical |
| `lysnr-seed-secret` | `SEED_SECRET` | ✅ **Seeded** | 🟡 Medium |
| `lysnr-azure-speech-key` | `AZURE_SPEECH_KEY` | ✅ **Seeded** | 🟠 High |
| `lysnr-azure-openai-key` | `AZURE_OPENAI_KEY` | ✅ **Seeded** | 🟠 High |
| `lysnr-azure-openai-endpoint` | `AZURE_OPENAI_ENDPOINT` | ✅ **Seeded** | 🟠 High |

**Total Expected:** 13 secrets  
**Total Seeded:** 13 secrets (100%) ✅ — Completed 2026-02-15

---

## 📋 Complete Environment Variable Inventory

### Core Platform Variables (from `.env.example`)

#### 🔐 Azure Key Vault
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `AZURE_KEYVAULT_URL` | Optional | - | All services | N/A |

**Purpose:** Enable Key Vault secret resolution. Falls back to env vars if not set.

---

#### 🗄️ Azure Cosmos DB
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `COSMOS_ENDPOINT` | ✅ Yes | - | All services | ❌ No (lysnr) |
| `COSMOS_KEY` | ✅ Yes | - | All services | ❌ No (lysnr) |
| `COSMOS_DATABASE` | No | `lysnrai` | All services | N/A |

**Purpose:** Database connection for all products (lysnrai, mindlyst, mywisprai databases).

---

#### 🔑 Authentication & Security
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `JWT_SECRET` | ✅ Yes | - | platform-service, extraction-service | ❌ No |

**Purpose:** JWT token signing and verification across all services.

---

#### 💾 Azure Blob Storage
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `AZURE_BLOB_CONNECTION_STRING` | Optional | - | platform-service | ❌ No |
| `AZURE_BLOB_ACCOUNT_NAME` | Optional | `bytelystblobs` | platform-service | N/A |
| `AZURE_BLOB_ACCOUNT_KEY` | Optional | - | platform-service | ❌ No |

**Purpose:** File uploads, avatar storage, document storage.  
**Note:** Only needed for platform-service blob module.

---

#### 💳 Stripe (Billing & Subscriptions)
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `STRIPE_SECRET_KEY` | Optional | - | platform-service | ❌ No |
| `STRIPE_WEBHOOK_SECRET` | Optional | - | platform-service | ❌ No |
| `STRIPE_PRICE_PRO` | Optional | - | platform-service | N/A |
| `STRIPE_PRICE_ENTERPRISE` | Optional | - | platform-service | N/A |
| `BILLING_INTERNAL_KEY` | Optional | - | platform-service | ❌ No |

**Purpose:** Subscription management, payment processing, webhook validation.  
**Status:** ❌ **Required for production but completely missing from Key Vault**

---

#### 🤖 AI Services - Extraction
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `GEMINI_API_KEY` | ✅ Yes | - | extraction-service (Python sidecar) | ❌ No |
| `PYTHON_SIDECAR_URL` | No | `http://localhost:4006` | extraction-service | N/A |
| `DEFAULT_MODEL_ID` | No | `gemini-2.5-flash` | extraction-service | N/A |

**Purpose:** Text extraction, content analysis, AI-powered features.  
**Status:** ❌ **Critical for extraction service functionality**

---

#### 🧠 Azure OpenAI (Optional)
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `AZURE_OPENAI_KEY` | Optional | - | extraction-service | ❌ No (lysnr) |
| `AZURE_OPENAI_ENDPOINT` | Optional | - | extraction-service | ❌ No (lysnr) |
| `AZURE_OPENAI_DEPLOYMENT_NAME` | Optional | - | extraction-service | N/A |

**Purpose:** Alternative to Gemini for text extraction.  
**Existing:** ✅ Available for `wispr` and `mindlyst` products in Key Vault.

---

#### 🎤 Azure Speech Services
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `AZURE_SPEECH_KEY` | Optional | - | LysnrAI product | ❌ No (lysnr) |
| `AZURE_SPEECH_REGION` | Optional | `eastus` | LysnrAI product | N/A |

**Purpose:** Speech-to-text, text-to-speech for voice AI features.  
**Existing:** ✅ Available for `wispr` and `mindlyst` products in Key Vault.

---

#### 🚀 Service Configuration
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `PORT` | No | varies | All services | N/A |
| `HOST` | No | `0.0.0.0` | All services | N/A |
| `NODE_ENV` | No | `development` | All services | N/A |
| `SERVICE_NAME` | No | auto | All services | N/A |
| `CORS_ORIGIN` | No | `*` (dev) | All services | N/A |
| `DEFAULT_PRODUCT_ID` | No | `lysnrai` | All services | N/A |

**Purpose:** Runtime configuration, not secrets.

---

#### 📊 Application Insights (Monitoring)
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `APPLICATIONINSIGHTS_CONNECTION_STRING` | Optional | - | All services (future) | ✅ Yes (mindlyst) |
| `APPINSIGHTS_INSTRUMENTATIONKEY` | Optional | - | All services (future) | N/A |

**Purpose:** Telemetry, logging, monitoring.  
**Note:** Only MindLyst has this configured currently.

---

#### 🔔 Notification Hubs (Mobile Push)
| Variable | Required | Default | Used By | In KV? |
|----------|----------|---------|---------|--------|
| `AZURE_NH_CONNECTION_STRING` | Optional | - | platform-service (future) | ✅ Yes (mindlyst) |
| `AZURE_NH_HUB_NAME` | Optional | - | platform-service (future) | N/A |

**Purpose:** Push notifications for mobile apps.  
**Note:** Only MindLyst has this configured currently.

---

## 🚨 Critical Gaps & Missing Secrets

### Priority 1: Critical (Service Breaking) - LysnrAI Product

These secrets are **required** for LysnrAI services to function and are **completely missing** from Key Vault:

1. ❌ **`lysnr-cosmos-key`** → `COSMOS_KEY`
   - **Impact:** Cannot connect to Cosmos DB
   - **Affects:** All services (platform, extraction)
   - **Action:** Add to Key Vault immediately

2. ❌ **`lysnr-cosmos-endpoint`** → `COSMOS_ENDPOINT`
   - **Impact:** Cannot locate Cosmos DB
   - **Affects:** All services
   - **Action:** Add to Key Vault immediately

3. ❌ **`lysnr-jwt-secret`** → `JWT_SECRET`
   - **Impact:** Cannot issue or verify JWTs
   - **Affects:** All services (auth fails)
   - **Action:** Add to Key Vault immediately

4. ❌ **`lysnr-gemini-api-key`** → `GEMINI_API_KEY`
   - **Impact:** Extraction service cannot function
   - **Affects:** extraction-service
   - **Action:** Add to Key Vault immediately

5. ❌ **`lysnr-blob-connection-string`** → `AZURE_BLOB_CONNECTION_STRING`
   - **Impact:** Cannot upload/download files
   - **Affects:** platform-service (blob module)
   - **Action:** Add to Key Vault immediately

6. ❌ **`lysnr-blob-account-key`** → `AZURE_BLOB_ACCOUNT_KEY`
   - **Impact:** Cannot generate SAS tokens
   - **Affects:** platform-service (blob module)
   - **Action:** Add to Key Vault immediately

### Priority 2: High (Business Functionality)

7. ❌ **`lysnr-stripe-secret-key`** → `STRIPE_SECRET_KEY`
   - **Impact:** Cannot process payments
   - **Affects:** platform-service (billing, subscriptions)
   - **Action:** Add to Key Vault for production

8. ❌ **`lysnr-stripe-webhook-secret`** → `STRIPE_WEBHOOK_SECRET`
   - **Impact:** Cannot validate Stripe webhooks
   - **Affects:** platform-service (payment events)
   - **Action:** Add to Key Vault for production

9. ❌ **`lysnr-billing-internal-key`** → `BILLING_INTERNAL_KEY`
   - **Impact:** Cannot authenticate internal billing calls
   - **Affects:** platform-service
   - **Action:** Add to Key Vault for production

10. ❌ **`lysnr-azure-speech-key`** → `AZURE_SPEECH_KEY`
    - **Impact:** No speech features (if needed)
    - **Affects:** LysnrAI product (voice features)
    - **Action:** Add if voice features are required

11. ❌ **`lysnr-azure-openai-key`** → `AZURE_OPENAI_KEY`
    - **Impact:** Cannot use Azure OpenAI (Gemini is alternative)
    - **Affects:** extraction-service
    - **Action:** Add if Azure OpenAI is preferred over Gemini

12. ❌ **`lysnr-azure-openai-endpoint`** → `AZURE_OPENAI_ENDPOINT`
    - **Impact:** Cannot use Azure OpenAI
    - **Affects:** extraction-service
    - **Action:** Add if Azure OpenAI is preferred over Gemini

### Priority 3: Medium (Optional)

13. ❌ **`lysnr-seed-secret`** → `SEED_SECRET`
    - **Impact:** Cannot run database seeding securely
    - **Affects:** Development/testing
    - **Action:** Add for secure seeding

---

## 📦 Product-Specific Secret Patterns

### MindLyst Product (12 secrets) ✅
**Prefix:** `mindlyst-*`  
**Status:** Fully populated  
**Secrets:**
- Cosmos DB (endpoint, key, database)
- Blob Storage (connection string)
- Azure OpenAI (endpoint, key, deployment, api-version)
- Azure Speech (key, region)
- Notification Hub (connection string)
- Application Insights (connection string)

### MyWisprAI Product (5 secrets) ⚠️
**Prefix:** `wispr-*`  
**Status:** Partially populated (legacy — pre-rebrand desktop app secrets)  
**Secrets:**
- Azure OpenAI (endpoint, key, deployment)
- Azure Speech (key, region)

**Note:** These are legacy secrets from the original MyWisprAI product. LysnrAI services use `lysnr-*` prefixed secrets instead.

### LysnrAI Product (0 secrets) ❌
**Prefix:** `lysnr-*`  
**Status:** Completely missing  
**Expected:** 13 secrets  
**Actual:** 0 secrets

**This is the primary gap!** The platform services are designed for LysnrAI but have zero secrets in Key Vault.

---

## 🔧 Remediation Plan

### Step 1: Seed LysnrAI Secrets (Immediate)

Use the provided script with populated .env file:

```bash
# 1. Create .env with all secrets
cp .env.example .env
# Edit .env and fill in real values

# 2. Run the seeding script
./scripts/seed-keyvault.sh

# 3. Verify secrets were created
az keyvault secret list --vault-name kv-mywisprai --output table
```

The script will create all 13 `lysnr-*` secrets automatically.

### Step 2: Update Services to Use Key Vault

Add to each service's startup (already implemented in code):

```typescript
import { resolveKeyVaultSecrets, LYSNR_SECRETS } from '@bytelyst/config';

// Before loading config — pick the subset your service needs
await resolveKeyVaultSecrets([
  LYSNR_SECRETS.COSMOS_KEY,
  LYSNR_SECRETS.COSMOS_ENDPOINT,
  LYSNR_SECRETS.JWT_SECRET,
  // ... add service-specific secrets
]);

// Then load config (will use resolved KV values from process.env)
const config = loadConfig();
```

**Admin Dashboard:** The Secrets Manager UI at `/ops/secrets` provides live CRUD access to all Key Vault secrets (list, read, set, rotate, delete).

### Step 3: Test Key Vault Integration

```bash
# Set Key Vault URL
export AZURE_KEYVAULT_URL=https://kv-mywisprai.vault.azure.net

# Remove local .env secrets to test KV
mv .env .env.backup

# Start service (should fetch from KV)
pnpm --filter @lysnrai/platform-service dev

# Check logs for KV resolution
# Should see: "[keyvault] Resolving 13 secrets from Key Vault"
```

### Step 4: Verify Each Service

| Service | Port | Test Endpoint | Expected Status |
|---------|------|---------------|-----------------|
| platform-service | 4003 | GET /health | 200 OK |
| extraction-service | 4005 | GET /health | 200 OK |

### Step 5: Production Checklist

- [ ] Seed all `lysnr-*` secrets to Key Vault
- [ ] Add Stripe secrets (test keys first, then prod)
- [ ] Add Gemini API key
- [ ] Configure managed identity for services
- [ ] Remove connection strings from code/env files
- [ ] Enable Key Vault audit logging
- [ ] Set up secret rotation schedule
- [ ] Document secret access patterns

---

## 🔄 Secret Rotation Strategy

### Recommended Rotation Schedule:

| Secret Type | Rotation Frequency | Method |
|-------------|-------------------|--------|
| Cosmos DB keys | Quarterly | Azure Portal → rotate keys → update KV |
| JWT secret | Quarterly | Generate new → update KV → rolling deploy |
| Stripe keys | Annually or on breach | Stripe Dashboard → update KV |
| API keys (Gemini, OpenAI) | Annually or on breach | Provider portal → update KV |
| Blob storage keys | Quarterly | Azure Portal → rotate keys → update KV |

### Rotation Procedure:
1. Generate new secret in Azure Portal / provider
2. Update Key Vault with new value
3. Services auto-fetch new secret on next restart (or use Key Vault rotation event)
4. Monitor for errors
5. Revoke old secret after 24h grace period

---

## 🔗 Related Files & Documentation

| File | Purpose |
|------|---------|
| `packages/config/src/keyvault.ts` | Key Vault integration code (`resolveKeyVaultSecrets` + `LYSNR_SECRETS`) |
| `scripts/seed-keyvault.sh` | Script to populate Key Vault from `.env` values |
| `.env.example` | Template for required environment variables |
| `services/platform-service/src/server.ts` | Platform service — resolves KV secrets at startup |
| `services/extraction-service/src/server.ts` | Extraction service — resolves KV secrets at startup |
| `admin-dashboard-web/src/instrumentation.ts` | Admin dashboard — resolves KV secrets via Next.js hook |
| `user-dashboard-web/src/instrumentation.ts` | User dashboard — resolves KV secrets via Next.js hook |
| `tracker-dashboard-web/src/instrumentation.ts` | Tracker dashboard — resolves KV secrets via Next.js hook |
| `backend/src/secrets/keyvault.py` | Python backend SecretResolver |
| `src/secrets/keyvault.py` | Desktop app SecretResolver |
| `docs/devops/AZURE_PORTAL_SETUP.md` | Azure portal configuration guide |
| `docs/devops/AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md` | Key Vault setup + rotation runbooks |
| `docs/devops/AZURE_RESOURCE_INVENTORY.md` | Complete Azure resource inventory |

---

## 📊 Summary Statistics

### Environment Variables:
- **Total Unique Variables:** 35+
- **Required for Core Functionality:** 6
- **Optional/Feature-Specific:** 29+

### Key Vault Secrets:
- **Total Secrets in KV:** 30
- **MindLyst Secrets:** 12 ✅
- **MyWisprAI Secrets:** 5 ⚠️ (legacy `wispr-*` prefix)
- **LysnrAI Secrets:** 13 ✅ (`lysnr-*` prefix)
- **Expected LysnrAI Secrets:** 13
- **Coverage Gap:** 0%

### Priority Actions:
- ✅ All 13 `lysnr-*` secrets seeded (2026-02-15)
- ⚠️ **Next:** Rotate keys that were exposed in git history (see `AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md`)

---

## 🔁 Key Vault Export Audit (v2)

`scripts/export-lysnr-kv.sh` was run under the temporary Azure config (`/tmp/azure`) to capture the live `lysnr-*` secret values into `kv_azure.txt`. The command sequence was:

```bash
AZURE_CONFIG_DIR=/tmp/azure AZURE_CORE_LOG_DIR=/tmp/azure AZURE_CORE_DISABLE_COMMAND_LOGGING=1 bash scripts/export-lysnr-kv.sh
```

While the script succeeded locally, the Azure CLI could not resolve `kv-mywisprai.vault.azure.net`, producing:

```
ERROR: HTTPSConnection(host='kv-mywisprai.vault.azure.net', port=443): Failed to resolve 'kv-mywisprai.vault.azure.net' ([Errno 8] nodename nor servname provided, or not known)
```

As a result, the generated `kv_azure.txt` currently contains `null` values for every `lysnr-*` secret. Once DNS/routing to the vault is available again, rerun the same command to emit the actual values and use the file as a snapshot for comparison.

📁 `kv_azure.txt` (post-run):
```
lysnr-azure-openai-endpoint=null
lysnr-azure-openai-key=null
lysnr-azure-speech-key=null
lysnr-billing-internal-key=null
lysnr-blob-account-key=null
lysnr-blob-connection-string=null
lysnr-cosmos-endpoint=null
lysnr-cosmos-key=null
lysnr-gemini-api-key=null
lysnr-jwt-secret=null
lysnr-seed-secret=null
lysnr-stripe-secret-key=null
lysnr-stripe-webhook-secret=null
```

This audit run acts as version 2; despite the values being null now, it proves the export tooling works and highlights the next blocker (Azure DNS access).

## 🚀 Quick Fix Commands

### Get Azure Resource Keys (for seeding):

```bash
# Cosmos DB
az cosmosdb keys list --name cosmos-mywisprai --resource-group rg-mywisprai --type keys

# Blob Storage
az storage account keys list --account-name bytelystblobs --resource-group rg-mywisprai

# OpenAI
az cognitiveservices account keys list --name mywisprai-openai-sweden --resource-group rg-mywisprai

# Speech Services
az cognitiveservices account keys list --name mywisprai-speech --resource-group rg-mywisprai
```

### Manually Add a Secret:

```bash
az keyvault secret set \
  --vault-name kv-mywisprai \
  --name lysnr-cosmos-key \
  --value "your-actual-cosmos-key-here"
```

### Verify Secret Resolution in Code:

```bash
# Check if KV is being used
export AZURE_KEYVAULT_URL=https://kv-mywisprai.vault.azure.net
node -e "process.env.AZURE_KEYVAULT_URL && console.log('✅ KV enabled') || console.log('❌ KV disabled')"
```

---

## 🔍 Git History Recovery (2026-02-14)

Scanned git history across `learning_voice_ai_agent` to recover actual secret values for the 13 missing `lysnr-*` secrets.

| # | KV Secret Name | Source | Status |
|---|----------------|--------|--------|
| 1 | `lysnr-cosmos-endpoint` | `.env` commits | ✅ Recovered |
| 2 | `lysnr-cosmos-key` | `.env` commits | ✅ Recovered |
| 3 | `lysnr-jwt-secret` | `.env` commits | ✅ Recovered |
| 4 | `lysnr-stripe-secret-key` | `.env` commits | ✅ Recovered (sk_test_*) |
| 5 | `lysnr-stripe-webhook-secret` | `.env` commits | ✅ Recovered (whsec_*) |
| 6 | `lysnr-billing-internal-key` | `.env` commits | ✅ Recovered |
| 7 | `lysnr-blob-connection-string` | `.env` commits | ✅ Recovered |
| 8 | `lysnr-blob-account-key` | `.env` commits | ✅ Recovered |
| 9 | `lysnr-gemini-api-key` | Provided manually | ✅ Recovered |
| 10 | `lysnr-seed-secret` | `.env` commits | ✅ Recovered |
| 11 | `lysnr-azure-speech-key` | `.env` commits | ✅ Recovered |
| 12 | `lysnr-azure-openai-key` | `.env` commits | ✅ Recovered |
| 13 | `lysnr-azure-openai-endpoint` | `.env` commits | ✅ Recovered |

**Result:** 13/13 recovered. Seed script written to `kv.txt` (gitignored, temporary).

**To seed:**
```bash
az login
bash kv.txt
rm kv.txt
```

> ⚠️ **Post-seed:** These keys were exposed in git history. Schedule rotation after seeding (see `AZURE_KEY_VAULT_AND_SECRETS_ROTATION.md`).

---

**Generated by:** Environment audit automation  
**Maintained by:** ByteLyst DevOps Team  
**Next Audit:** After LysnrAI `lysnr-*` secrets are seeded into `kv-mywisprai`  
**Admin UI:** Secrets Manager at admin dashboard → `/ops/secrets`
