Spaces:
Sleeping
Sleeping
| # Runbook — CloudSync Pro Customer Success FTE | |
| **Version**: 1.1 | |
| **Last Updated**: 2026-02-25 | |
| **On-call contact**: engineering@cloudsyncpro.com | |
| --- | |
| ## Table of Contents | |
| 1. [Demo Quick Start (Multi-Terminal)](#demo-quick-start-multi-terminal) | |
| 2. [Service Overview](#service-overview) | |
| 3. [Health Checks](#health-checks) | |
| 4. [Common Incidents](#common-incidents) | |
| 5. [Escalation Procedures](#escalation-procedures) | |
| 6. [Deployment & Rollback](#deployment--rollback) | |
| 7. [Database Operations](#database-operations) | |
| 8. [Channel-Specific Troubleshooting](#channel-specific-troubleshooting) | |
| 9. [Monitoring & Alerts](#monitoring--alerts) | |
| --- | |
| ## Demo Quick Start (Multi-Terminal) | |
| > Complete system startup for a live demo. Open **4 terminals** side-by-side. | |
| > Total startup time: ~30 seconds (excluding DB init). | |
| ### Prerequisites checklist | |
| - [ ] PostgreSQL running with `fte_db` + pgvector extension | |
| - [ ] `.env` file present (copy from `.env.example` and fill keys) | |
| - [ ] Python deps installed: `pip install -r requirements.txt` | |
| - [ ] Node deps installed: `cd frontend && npm install` | |
| - [ ] Knowledge base seeded: `python -m production.database.seed_knowledge_base` *(first time only)* | |
| --- | |
| ### Terminal 1 — Backend API | |
| ```bash | |
| cd "/mnt/d/The CRM Digital FTE Factory Final Hackathon 5" | |
| python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 --reload | |
| ``` | |
| **Expected output:** | |
| ``` | |
| WARNING:production.kafka_client:Kafka unavailable — running without message queue | |
| INFO: Application startup complete. | |
| INFO: Uvicorn running on http://0.0.0.0:8000 | |
| ``` | |
| > Kafka timeout (5 s) is normal in dev — system auto-bypasses it. | |
| **Verify:** `curl http://localhost:8000/health` | |
| --- | |
| ### Terminal 2 — Frontend (Next.js) | |
| ```bash | |
| cd "/mnt/d/The CRM Digital FTE Factory Final Hackathon 5/frontend" | |
| npm run dev | |
| ``` | |
| **Expected output:** | |
| ``` | |
| ▲ Next.js 14.x | |
| - Local: http://localhost:3000 | |
| ✓ Ready in 2.1s | |
| ``` | |
| **Verify:** Open `http://localhost:3000` in browser | |
| --- | |
| ### Terminal 3 — API Logs (live tail) | |
| ```bash | |
| # Watch all agent activity in real time | |
| python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 2>&1 | tee /tmp/fte.log | |
| # or if already running in Terminal 1: | |
| tail -f /tmp/fte.log | |
| ``` | |
| > Shows tool calls, ticket creation, agent reasoning, and escalation events. | |
| --- | |
| ### Terminal 4 — Load Test (optional, demo only) | |
| ```bash | |
| cd "/mnt/d/The CRM Digital FTE Factory Final Hackathon 5" | |
| # Headless — 50 users, 5/s spawn, 1 minute | |
| locust -f tests/load_test.py --host http://localhost:8000 \ | |
| --users 50 --spawn-rate 5 --run-time 1m --headless | |
| # Or with web UI at http://localhost:8089 | |
| locust -f tests/load_test.py --host http://localhost:8000 | |
| ``` | |
| --- | |
| ### End-to-End Demo Flow | |
| After all terminals are running: | |
| ```bash | |
| # 1. Submit a support ticket (web form channel) | |
| curl -s -X POST http://localhost:8000/support/submit \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "name": "Demo User", | |
| "email": "demo@example.com", | |
| "subject": "Cannot login to my account", | |
| "message": "I have been locked out since yesterday. Password reset emails are not arriving.", | |
| "category": "account", | |
| "priority": "high" | |
| }' | python3 -m json.tool | |
| # 2. Copy ticket_id from response, then check status: | |
| curl -s http://localhost:8000/support/ticket/<TICKET_ID> | python3 -m json.tool | |
| # 3. Send a follow-up reply: | |
| curl -s -X POST http://localhost:8000/support/ticket/<TICKET_ID>/reply \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"message": "Still having the issue, please help!", "customer_name": "Demo User"}' \ | |
| | python3 -m json.tool | |
| # 4. View live metrics: | |
| curl -s http://localhost:8000/metrics/summary | python3 -m json.tool | |
| # 5. Open dashboard in browser: | |
| # http://localhost:3000/dashboard | |
| ``` | |
| --- | |
| ### Shutdown (clean) | |
| ```bash | |
| # Stop API (Terminal 1) | |
| Ctrl+C | |
| # Stop frontend (Terminal 2) | |
| Ctrl+C | |
| # Kill any background uvicorn processes | |
| pkill -f uvicorn | |
| # Optional: stop PostgreSQL | |
| sudo service postgresql stop | |
| ``` | |
| --- | |
| ## Service Overview | |
| | Component | Technology | Port | Health Check | | |
| |-----------|-----------|------|-------------| | |
| | API Server | FastAPI + Uvicorn | 8000 | `GET /health` | | |
| | Frontend | Next.js | 3000 | `GET /` | | |
| | Database | PostgreSQL 15 + pgvector | 5432 | `SELECT 1` | | |
| | Message Queue | Apache Kafka | 9092 | `kafka-topics.sh --list` | | |
| | AI Agent | Groq API (LLaMA 4) | external | Groq console | | |
| --- | |
| ## Health Checks | |
| ### Quick System Status | |
| ```bash | |
| curl http://localhost:8000/health | |
| # Expected: {"status":"healthy","channels":{"email":"active","whatsapp":"active","web_form":"active"},"database":"active"} | |
| ``` | |
| ### Check All Services | |
| ```bash | |
| # API | |
| curl -s http://localhost:8000/health | python3 -m json.tool | |
| # Database | |
| psql -U fte_user -d fte_db -c "SELECT COUNT(*) FROM tickets;" | |
| # Kafka (if running) | |
| kafka-topics.sh --bootstrap-server localhost:9092 --list | |
| # Frontend | |
| curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 | |
| ``` | |
| --- | |
| ## Common Incidents | |
| ### INC-001: API Not Responding | |
| **Symptoms**: `curl http://localhost:8000/health` times out or connection refused | |
| **Steps**: | |
| ```bash | |
| # 1. Check if process is running | |
| ps aux | grep uvicorn | |
| # 2. Check what's on port 8000 | |
| lsof -i :8000 | |
| # 3. Kill stale processes if multiple | |
| pkill -f uvicorn | |
| # 4. Restart | |
| cd /path/to/project | |
| python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 & | |
| # 5. Verify | |
| curl http://localhost:8000/health | |
| ``` | |
| --- | |
| ### INC-002: Database Connection Failed | |
| **Symptoms**: Health check shows `"database":"inactive"` or DB errors in logs | |
| **Steps**: | |
| ```bash | |
| # 1. Check PostgreSQL is running | |
| pg_isready -h localhost -p 5432 | |
| # 2. If not running, start it | |
| sudo service postgresql start | |
| # or | |
| docker start postgres-container | |
| # 3. Verify credentials | |
| psql -U fte_user -d fte_db -h localhost -c "SELECT 1;" | |
| # 4. Check connection pool settings in .env | |
| grep POSTGRES .env | |
| # 5. Restart API to reset pool | |
| pkill -f uvicorn && python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 & | |
| ``` | |
| --- | |
| ### INC-003: AI Agent Not Responding (Groq API Failure) | |
| **Symptoms**: Tickets created but status stays `open` with no agent reply after 5+ minutes | |
| **Steps**: | |
| ```bash | |
| # 1. Test Groq API directly | |
| curl https://api.groq.com/openai/v1/models \ | |
| -H "Authorization: Bearer $GROQ_API_KEY" | |
| # 2. Check rate limits (Groq console) | |
| # https://console.groq.com/usage | |
| # 3. Check .env model name | |
| grep AGENT_MODEL .env | |
| # Should be: meta-llama/llama-4-scout-17b-16e-instruct | |
| # 4. Verify API key | |
| grep GROK_API_KEY .env | |
| # 5. If rate limited: wait 1 minute, resubmit ticket manually | |
| curl -X POST http://localhost:8000/support/submit \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"name":"Test","email":"test@test.com","subject":"Test","message":"API test after rate limit"}' | |
| ``` | |
| --- | |
| ### INC-004: Kafka Unavailable | |
| **Symptoms**: Log shows `WARNING: Kafka unavailable — running without message queue` | |
| **Impact**: System continues working via direct processor fallback. **Not critical.** | |
| **Steps**: | |
| ```bash | |
| # 1. This is expected in dev — no action needed | |
| # 2. In production, restart Kafka: | |
| docker-compose restart kafka zookeeper | |
| # 3. Restart API to reconnect | |
| pkill -f uvicorn && python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 & | |
| # 4. Verify messages flowing | |
| kafka-consumer-groups.sh --bootstrap-server localhost:9092 --describe --group fte-workers | |
| ``` | |
| --- | |
| ### INC-005: Gmail Channel Inactive | |
| **Symptoms**: Health check shows `"email":"inactive"` | |
| **Steps**: | |
| ```bash | |
| # 1. Check credentials file exists | |
| ls -la credentials/gmail-credentials.json | |
| # 2. Test credentials | |
| python3 -c " | |
| from google.oauth2.credentials import Credentials | |
| from googleapiclient.discovery import build | |
| creds = Credentials.from_authorized_user_file('credentials/gmail-credentials.json') | |
| service = build('gmail', 'v1', credentials=creds) | |
| print(service.users().getProfile(userId='me').execute()) | |
| " | |
| # 3. If token expired, regenerate: | |
| python credentials/generate_gmail_token.py | |
| # 4. Check GMAIL_CREDENTIALS_PATH in .env | |
| grep GMAIL .env | |
| # 5. Restart API | |
| pkill -f uvicorn && python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 & | |
| ``` | |
| --- | |
| ### INC-006: WhatsApp Messages Not Received | |
| **Symptoms**: WhatsApp messages sent but no ticket created | |
| **Steps**: | |
| ```bash | |
| # 1. Check ngrok tunnel is running | |
| curl http://localhost:4040/api/tunnels | python3 -m json.tool | |
| # 2. If ngrok down, restart | |
| /tmp/ngrok http 8000 & | |
| sleep 3 | |
| NGROK_URL=$(curl -s http://localhost:4040/api/tunnels | python3 -c "import sys,json; print(json.load(sys.stdin)['tunnels'][0]['public_url'])") | |
| echo "New URL: $NGROK_URL/whatsapp/webhook" | |
| # 3. Update Twilio webhook URL | |
| # Go to: https://console.twilio.com/us1/develop/phone-numbers/manage/sandbox | |
| # Update "When a message comes in" URL to new ngrok URL | |
| # 4. Test webhook manually | |
| curl -X POST http://localhost:8000/whatsapp/webhook \ | |
| -d "From=whatsapp:+1234567890&Body=Test+message&MessageSid=TEST123" | |
| ``` | |
| --- | |
| ### INC-007: Web Form Submissions Hanging | |
| **Symptoms**: POST /support/submit never returns (curl exit 28) | |
| **Root cause history**: | |
| 1. `email-validator` DNS MX lookup blocking event loop → Fixed: `check_deliverability=False` | |
| 2. Kafka partial-init: `_producer` set but not started → Fixed: `_producer = None` after timeout | |
| 3. `publish()` blocking forever → Fixed: `asyncio.wait_for(..., timeout=5.0)` | |
| **Steps**: | |
| ```bash | |
| # 1. Quick test | |
| curl -m 10 -X POST http://localhost:8000/support/submit \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"name":"Test","email":"t@t.com","subject":"Test","message":"Test message here"}' | |
| # 2. If hanging, check for stale processes | |
| ps aux | grep uvicorn | awk '{print $2}' | xargs -r kill | |
| python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 & | |
| # 3. Check Kafka producer state in logs | |
| grep "Kafka" /tmp/fte.log | tail -10 | |
| ``` | |
| --- | |
| ### INC-008: High Memory Usage | |
| **Symptoms**: OOMKilled in Kubernetes, or system slow | |
| **Steps**: | |
| ```bash | |
| # 1. Check memory usage | |
| ps aux --sort=-%mem | head -10 | |
| # 2. Common cause: fastembed model loaded multiple times | |
| # Verify singleton in production/agent/tools.py: | |
| grep "_embedding_model" production/agent/tools.py | |
| # 3. Force restart API | |
| pkill -f uvicorn | |
| python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 --workers 2 & | |
| # 4. In Kubernetes, trigger rolling restart | |
| kubectl rollout restart deployment/fte-api -n fte-production | |
| ``` | |
| --- | |
| ## Escalation Procedures | |
| ### Tier 1 — Automated (AI Agent) | |
| - Resolvable queries: KB match above 0.70 cosine similarity | |
| - Response time: < 15 seconds | |
| ### Tier 2 — Human Support Agent | |
| Triggered when AI escalates (check tickets with `status = 'escalated'`): | |
| ```bash | |
| # Find escalated tickets | |
| psql -U fte_user -d fte_db -c " | |
| SELECT id, subject, priority, created_at | |
| FROM tickets WHERE status = 'escalated' | |
| ORDER BY created_at DESC LIMIT 20;" | |
| ``` | |
| ### Tier 3 — Engineering On-Call | |
| - Email: engineering@cloudsyncpro.com | |
| - Trigger: API down > 5 min, data loss, security incident | |
| ### Tier 4 — Legal / Compliance | |
| - Email: legal@cloudsyncpro.com | |
| - Trigger: Any ticket with `escalation_reason = 'legal_language'` | |
| --- | |
| ## Deployment & Rollback | |
| ### Deploy New Version | |
| ```bash | |
| # 1. Pull latest | |
| git pull origin main | |
| # 2. Install dependencies | |
| pip install -r requirements.txt | |
| cd frontend && npm install && cd .. | |
| # 3. Run migration if schema changed | |
| psql -U fte_user -d fte_db -f production/database/migrations/latest.sql | |
| # 4. Run transition gate (MUST PASS) | |
| pytest production/tests/test_transition.py -v | |
| # If any test fails — DO NOT deploy | |
| # 5. Restart API | |
| pkill -f uvicorn | |
| python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 --workers 2 & | |
| # 6. Restart frontend | |
| cd frontend && npm run build && npm start & | |
| ``` | |
| ### Rollback | |
| ```bash | |
| # 1. Identify last good commit | |
| git log --oneline -10 | |
| # 2. Rollback | |
| git checkout <commit-hash> | |
| # 3. Restart services | |
| pkill -f uvicorn | |
| python -m uvicorn production.api.main:app --host 0.0.0.0 --port 8000 & | |
| ``` | |
| ### Kubernetes Rollback | |
| ```bash | |
| kubectl rollout undo deployment/fte-api -n fte-production | |
| kubectl rollout status deployment/fte-api -n fte-production | |
| ``` | |
| --- | |
| ## Database Operations | |
| ### Backup | |
| ```bash | |
| pg_dump -U fte_user -d fte_db -F c -f backups/fte_$(date +%Y%m%d).dump | |
| ``` | |
| ### Restore | |
| ```bash | |
| pg_restore -U fte_user -d fte_db -F c backups/fte_20260224.dump | |
| ``` | |
| ### Common Queries | |
| ```sql | |
| -- Active open tickets | |
| SELECT id, subject, channel, priority, created_at | |
| FROM tickets WHERE status = 'open' | |
| ORDER BY priority DESC, created_at ASC; | |
| -- Tickets by channel today | |
| SELECT channel, COUNT(*) as count | |
| FROM tickets WHERE created_at > NOW() - INTERVAL '24 hours' | |
| GROUP BY channel; | |
| -- Average resolution time | |
| SELECT AVG(EXTRACT(EPOCH FROM (resolved_at - created_at))/60) as avg_minutes | |
| FROM tickets WHERE status = 'resolved' AND resolved_at IS NOT NULL; | |
| -- Escalation rate | |
| SELECT | |
| COUNT(*) FILTER (WHERE status = 'escalated') * 100.0 / COUNT(*) as escalation_pct | |
| FROM tickets WHERE created_at > NOW() - INTERVAL '7 days'; | |
| ``` | |
| --- | |
| ## Channel-Specific Troubleshooting | |
| ### Gmail Watch Expiry | |
| Gmail Pub/Sub watch expires every 7 days. Renew: | |
| ```bash | |
| curl -X POST http://localhost:8000/gmail/setup-watch | |
| ``` | |
| Set a cron job: `0 0 */6 * * curl -X POST http://localhost:8000/gmail/setup-watch` | |
| ### Twilio Sandbox Reset | |
| If Twilio sandbox resets (monthly): | |
| 1. Go to https://console.twilio.com/us1/develop/phone-numbers/manage/sandbox | |
| 2. Re-join sandbox: WhatsApp `join <your-word>` | |
| 3. Update webhook URL if ngrok URL changed | |
| --- | |
| ## Monitoring & Alerts | |
| ### Key Metrics to Watch | |
| | Metric | Warning | Critical | Action | | |
| |--------|---------|----------|--------| | |
| | API response time (p95) | > 500ms | > 2000ms | Scale up workers | | |
| | Ticket backlog (open) | > 50 | > 200 | Check agent health | | |
| | Escalation rate | > 30% | > 60% | Check KB quality | | |
| | DB connection errors | > 5/min | > 20/min | Restart API + check PG | | |
| | Groq API errors | > 3/min | > 10/min | Check API key + rate limits | | |
| ### Log Locations | |
| ```bash | |
| # API logs (if started with redirect) | |
| tail -f /tmp/fte.log | |
| # System logs | |
| journalctl -u fte-api -f | |
| # Kubernetes logs | |
| kubectl logs -f deployment/fte-api -n fte-production | |
| ``` | |