Files
ALwrity/docs/API_KEY_FLOW_DIAGRAM.md
2025-10-10 23:19:28 +05:30

371 lines
19 KiB
Markdown

# API Key Management Flow Diagrams
## 🏠 Local Development Mode
```
┌─────────────────────────────────────────────────────────────────────┐
│ LOCAL DEVELOPMENT │
│ (DEBUG=true) │
└─────────────────────────────────────────────────────────────────────┘
Developer completes onboarding
├─> Frontend: Save API keys
│ └─> POST /api/onboarding/api-keys (gemini, exa, copilotkit)
├─> Backend: Process API keys
│ │
│ ├─> Save to PostgreSQL database
│ │ └─> onboarding_sessions (user_id)
│ │ └─> api_keys (provider, key)
│ │
│ └─> Save to backend/.env file [DEV MODE ONLY]
│ ├─> GEMINI_API_KEY=xxx
│ ├─> EXA_API_KEY=xxx
│ └─> COPILOTKIT_API_KEY=xxx
└─> Frontend: Save CopilotKit to frontend/.env
└─> REACT_APP_COPILOTKIT_API_KEY=xxx
Developer generates content
├─> Service calls user_api_keys(user_id=None)
│ │
│ └─> Detects DEV mode (DEBUG=true)
│ └─> Reads from backend/.env file
│ └─> Returns all keys
└─> Content generated using developer's keys
└─> All costs → Developer's API account
✅ Advantages:
• Quick setup (keys persist in .env)
• No database required for basic dev
• Single developer = single set of keys
• Keys survive server restarts
```
---
## 🌐 Production Mode (Multi-User)
```
┌─────────────────────────────────────────────────────────────────────┐
│ PRODUCTION (VERCEL + RENDER) │
│ (DEBUG=false, DEPLOY_ENV=render) │
└─────────────────────────────────────────────────────────────────────┘
Alpha Tester A visits https://alwrity-ai.vercel.app
├─> Completes onboarding
│ └─> Enters API keys:
│ ├─> GEMINI_API_KEY=tester_a_key
│ ├─> EXA_API_KEY=tester_a_exa
│ └─> COPILOTKIT_API_KEY=tester_a_copilot
├─> Frontend: Save API keys
│ ├─> POST /api/onboarding/api-keys (gemini, exa, copilotkit)
│ └─> Save to localStorage (CopilotKit)
└─> Backend: Process API keys
├─> Save to PostgreSQL database ONLY [PROD MODE]
│ └─> onboarding_sessions
│ ├─> user_id = "user_clerk_tester_a"
│ └─> api_keys
│ ├─> (session_id, "gemini", "tester_a_key")
│ ├─> (session_id, "exa", "tester_a_exa")
│ └─> (session_id, "copilotkit", "tester_a_copilot")
└─> [SKIP] ❌ Do NOT save to .env file (multi-user conflict!)
Alpha Tester A generates blog content
├─> Request to /api/blog/generate
│ └─> Headers: Authorization: Bearer <tester_a_clerk_token>
├─> Auth Middleware extracts user_id = "user_clerk_tester_a"
├─> BlogService calls user_api_keys("user_clerk_tester_a")
│ │
│ ├─> Detects PROD mode (DEPLOY_ENV=render)
│ │
│ └─> Query database:
│ SELECT key FROM api_keys
│ WHERE session_id = (
│ SELECT id FROM onboarding_sessions
│ WHERE user_id = 'user_clerk_tester_a'
│ )
│ └─> Returns: {"gemini": "tester_a_key", "exa": "tester_a_exa"}
└─> Content generated using Tester A's Gemini key
└─> All costs → Tester A's Gemini account
────────────────────────────────────────────────────────────────────────
SIMULTANEOUSLY...
Alpha Tester B visits https://alwrity-ai.vercel.app
├─> Completes onboarding
│ └─> Enters API keys:
│ ├─> GEMINI_API_KEY=tester_b_key
│ ├─> EXA_API_KEY=tester_b_exa
│ └─> COPILOTKIT_API_KEY=tester_b_copilot
└─> Backend: Save to database
└─> onboarding_sessions
├─> user_id = "user_clerk_tester_b"
└─> api_keys
├─> (session_id, "gemini", "tester_b_key") [SEPARATE!]
├─> (session_id, "exa", "tester_b_exa")
└─> (session_id, "copilotkit", "tester_b_copilot")
Alpha Tester B generates blog content
├─> Request to /api/blog/generate
│ └─> Headers: Authorization: Bearer <tester_b_clerk_token>
├─> Auth Middleware extracts user_id = "user_clerk_tester_b"
├─> BlogService calls user_api_keys("user_clerk_tester_b")
│ │
│ └─> Query database:
│ WHERE user_id = 'user_clerk_tester_b' [DIFFERENT!]
│ └─> Returns: {"gemini": "tester_b_key", "exa": "tester_b_exa"}
└─> Content generated using Tester B's Gemini key
└─> All costs → Tester B's Gemini account
✅ User Isolation:
• Tester A's keys ≠ Tester B's keys
• Tester A's costs ≠ Tester B's costs
• Completely isolated in database
• You (owner) pay nothing! 🎉
```
---
## 🔄 Environment Detection Logic
```
┌─────────────────────────────────────────────────────────────────────┐
│ ENVIRONMENT DETECTION │
└─────────────────────────────────────────────────────────────────────┘
When user_api_keys(user_id) is called:
┌──────────────────────────────────┐
│ Check environment variables │
└──────────────────────────────────┘
├─> DEBUG=true OR DEPLOY_ENV=None
│ │
│ ├─> DEVELOPMENT MODE
│ │ └─> Read from backend/.env file
│ │ └─> os.getenv('GEMINI_API_KEY')
│ │
│ └─> Log: "[DEV MODE] Using .env file"
└─> DEBUG=false AND DEPLOY_ENV=render
├─> PRODUCTION MODE
│ └─> Read from database
│ └─> SELECT key FROM api_keys WHERE user_id=?
└─> Log: "[PROD MODE] Using database for user {user_id}"
Example configurations:
Local Development:
┌─────────────────────────────┐
│ backend/.env │
├─────────────────────────────┤
│ DEBUG=true │
│ GEMINI_API_KEY=dev_key │
│ EXA_API_KEY=dev_exa │
└─────────────────────────────┘
Render Production:
┌─────────────────────────────┐
│ Environment Variables │
├─────────────────────────────┤
│ DEBUG=false │
│ DEPLOY_ENV=render │
│ DATABASE_URL=postgresql:// │
└─────────────────────────────┘
```
---
## 📊 Database Schema Visualization
```
┌─────────────────────────────────────────────────────────────────────┐
│ DATABASE SCHEMA │
└─────────────────────────────────────────────────────────────────────┘
onboarding_sessions
┌────────────┬──────────────────────────┬─────────────┬──────────┐
│ id (PK) │ user_id (UNIQUE) │ current_step│ progress │
├────────────┼──────────────────────────┼─────────────┼──────────┤
│ 1 │ user_clerk_tester_a │ 6 │ 100.0 │
│ 2 │ user_clerk_tester_b │ 6 │ 100.0 │
│ 3 │ user_clerk_tester_c │ 3 │ 50.0 │
└────────────┴──────────────────────────┴─────────────┴──────────┘
api_keys
┌────────────┬────────────┬──────────────┬────────────────────────┐
│ id (PK) │ session_id │ provider │ key │
│ │ (FK) │ │ │
├────────────┼────────────┼──────────────┼────────────────────────┤
│ 1 │ 1 │ gemini │ tester_a_gemini_key │ ← Tester A
│ 2 │ 1 │ exa │ tester_a_exa_key │ ← Tester A
│ 3 │ 1 │ copilotkit │ tester_a_copilot_key │ ← Tester A
├────────────┼────────────┼──────────────┼────────────────────────┤
│ 4 │ 2 │ gemini │ tester_b_gemini_key │ ← Tester B
│ 5 │ 2 │ exa │ tester_b_exa_key │ ← Tester B
│ 6 │ 2 │ copilotkit │ tester_b_copilot_key │ ← Tester B
├────────────┼────────────┼──────────────┼────────────────────────┤
│ 7 │ 3 │ gemini │ tester_c_gemini_key │ ← Tester C
│ 8 │ 3 │ exa │ tester_c_exa_key │ ← Tester C
└────────────┴────────────┴──────────────┴────────────────────────┘
Query to get Tester A's Gemini key:
SELECT k.key
FROM api_keys k
JOIN onboarding_sessions s ON k.session_id = s.id
WHERE s.user_id = 'user_clerk_tester_a'
AND k.provider = 'gemini'
Result: 'tester_a_gemini_key'
Query to get Tester B's Gemini key:
SELECT k.key
FROM api_keys k
JOIN onboarding_sessions s ON k.session_id = s.id
WHERE s.user_id = 'user_clerk_tester_b'
AND k.provider = 'gemini'
Result: 'tester_b_gemini_key' [DIFFERENT!]
```
---
## 🔐 Security & Isolation
```
┌─────────────────────────────────────────────────────────────────────┐
│ USER ISOLATION GUARANTEE │
└─────────────────────────────────────────────────────────────────────┘
Scenario: Both Tester A and Tester B generate content simultaneously
Tester A's Request Thread:
┌────────────────────────────────────────────┐
│ 1. Auth: user_id = "user_clerk_tester_a" │
│ 2. Fetch keys: WHERE user_id = tester_a │
│ 3. Get: gemini_key = "tester_a_key" │
│ 4. Generate with tester_a_key │
│ 5. Response to Tester A │
└────────────────────────────────────────────┘
[Database]
┌────────────────────────────────────────────┐
│ 1. Auth: user_id = "user_clerk_tester_b" │
│ 2. Fetch keys: WHERE user_id = tester_b │
│ 3. Get: gemini_key = "tester_b_key" │
│ 4. Generate with tester_b_key │
│ 5. Response to Tester B │
└────────────────────────────────────────────┘
Tester B's Request Thread:
✅ Guarantees:
• Tester A NEVER sees Tester B's keys
• Tester B NEVER sees Tester A's keys
• Tester A's costs charged to Tester A
• Tester B's costs charged to Tester B
• Database enforces isolation via user_id
• Clerk auth ensures correct user_id
```
---
## 💰 Cost Distribution
```
┌─────────────────────────────────────────────────────────────────────┐
│ WHO PAYS FOR WHAT? │
└─────────────────────────────────────────────────────────────────────┘
Local Development (You):
Your API Keys → Your Costs
┌─────────────────────────────────────────────┐
│ Developer generates 100 blog posts │
│ Uses: GEMINI_API_KEY from .env │
│ Cost: $5.00 → Charged to developer's │
│ Google Cloud account │
└─────────────────────────────────────────────┘
Production (Alpha Testers):
Their API Keys → Their Costs
┌─────────────────────────────────────────────┐
│ Tester A generates 50 blog posts │
│ Uses: tester_a_gemini_key from database │
│ Cost: $2.50 → Charged to Tester A's │
│ Google Cloud account │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Tester B generates 200 blog posts │
│ Uses: tester_b_gemini_key from database │
│ Cost: $10.00 → Charged to Tester B's │
│ Google Cloud account │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ You (owner) host infrastructure │
│ Render: Free tier / $7/month │
│ Vercel: Free tier │
│ Database: Render free tier │
│ Cost: $0 - $7/month (infrastructure only) │
└─────────────────────────────────────────────┘
Total monthly cost for you with 100 alpha testers:
Infrastructure: $0 - $7
API usage: $0 (testers pay their own!)
────────────────────────────
Total: $0 - $7/month 🎉
```
---
## 🎯 Summary
| Aspect | Local Dev | Production |
|--------|-----------|------------|
| **Environment** | `DEBUG=true` | `DEPLOY_ENV=render` |
| **Key Storage** | `.env` file + DB | Database only |
| **Key Retrieval** | `os.getenv()` | Database query |
| **User Isolation** | Not needed | Full isolation |
| **Cost Bearer** | You (developer) | Each tester |
| **Scalability** | 1 developer | Unlimited users |
| **Setup Effort** | Low (persist .env) | Low (onboard once) |
**Architecture Principle:**
> Development convenience with `.env` files, production isolation with database. Best of both worlds! 🚀