# Demo Architecture - Production Demo System ## Overview This document describes the complete demo architecture for providing prospects with isolated, ephemeral demo sessions to explore the Bakery IA platform. ## Key Features - ✅ **Session Isolation**: Each prospect gets their own isolated copy of demo data - ✅ **Spanish Content**: All demo data in Spanish for the Spanish market - ✅ **Two Business Models**: Individual bakery and central baker satellite - ✅ **Automatic Cleanup**: Sessions automatically expire after 30 minutes - ✅ **Read-Mostly Access**: Prospects can explore but critical operations are restricted - ✅ **Production Ready**: Scalable to 200+ concurrent demo sessions ## Architecture Components ### 1. Demo Session Service **Location**: `services/demo_session/` **Responsibilities**: - Create isolated demo sessions - Manage session lifecycle (create, extend, destroy) - Clone base demo data to virtual tenants - Track session metrics and activity **Key Endpoints**: ``` GET /api/demo/accounts # Get public demo account info POST /api/demo/session/create # Create new demo session POST /api/demo/session/extend # Extend session expiration POST /api/demo/session/destroy # Destroy session GET /api/demo/session/{id} # Get session info GET /api/demo/stats # Get usage statistics ``` ### 2. Demo Data Seeding **Location**: `scripts/demo/` **Scripts**: - `seed_demo_users.py` - Creates demo user accounts - `seed_demo_tenants.py` - Creates base demo tenants (templates) - `seed_demo_inventory.py` - Populates inventory with Spanish data (25 ingredients per template) - `clone_demo_tenant.py` - Clones data from base template to virtual tenant (runs as K8s Job) **Demo Accounts**: #### Individual Bakery (Panadería San Pablo) ``` Email: demo.individual@panaderiasanpablo.com Password: DemoSanPablo2024! Business Model: Producción Local Features: Production, Recipes, Inventory, Forecasting, POS, Sales ``` #### Central Baker Satellite (Panadería La Espiga) ``` Email: demo.central@panaderialaespiga.com Password: DemoLaEspiga2024! Business Model: Obrador Central + Punto de Venta Features: Suppliers, Inventory, Orders, POS, Sales, Forecasting ``` ### 3. Gateway Middleware **Location**: `gateway/app/middleware/demo_middleware.py` **Responsibilities**: - Intercept requests with demo session IDs - Inject virtual tenant ID - Enforce operation restrictions - Track session activity **Allowed Operations**: ```python # Read - All allowed GET, HEAD, OPTIONS: * # Limited Write - Realistic testing POST: /api/pos/sales, /api/orders, /api/inventory/adjustments PUT: /api/pos/sales/*, /api/orders/* # Blocked DELETE: All (read-only for destructive operations) ``` ### 4. Redis Cache Layer **Purpose**: Store frequently accessed demo session data **Data Cached**: - Session metadata - Inventory summaries - POS session data - Recent sales **TTL**: 30 minutes (auto-cleanup) ### 5. Kubernetes Resources **Databases**: - `demo-session-db` - Tracks session records **Services**: - `demo-session-service` - Main demo service (2 replicas) **Jobs** (Initialization): - `demo-seed-users` - Creates demo users - `demo-seed-tenants` - Creates demo tenant templates - `demo-seed-inventory` - Populates inventory data (25 ingredients per tenant) **Dynamic Jobs** (Runtime): - `demo-clone-{virtual_tenant_id}` - Created per session to clone data from template **CronJob** (Maintenance): - `demo-session-cleanup` - Runs hourly to cleanup expired sessions **RBAC**: - `demo-session-sa` - ServiceAccount for demo-session-service - `demo-session-job-creator` - Role allowing job creation and pod management - `demo-seed-role` - Role for seed jobs to access databases ## Data Flow ### Session Creation ``` 1. User clicks "Probar Demo" on website ↓ 2. Frontend calls POST /api/demo/session/create { "demo_account_type": "individual_bakery" } ↓ 3. Demo Session Service: - Generates unique session_id: "demo_abc123..." - Creates virtual_tenant_id: UUID - Stores session in database - Returns session_token (JWT) ↓ 4. Kubernetes Job Cloning (background): - Demo service triggers K8s Job with clone script - Job container uses CLONE_JOB_IMAGE (inventory-service image) - Clones inventory data from base template tenant - Uses ORM models for safe data copying - Job runs with IfNotPresent pull policy (works in dev & prod) ↓ 5. Frontend receives: { "session_id": "demo_abc123...", "virtual_tenant_id": "uuid-here", "expires_at": "2025-10-02T12:30:00Z", "session_token": "eyJ..." } ↓ 6. Frontend stores session_token in cookie/localStorage All subsequent requests include: Header: X-Demo-Session-Id: demo_abc123... ``` ### Request Handling ``` 1. Request arrives at Gateway ↓ 2. Demo Middleware checks: - Is X-Demo-Session-Id present? - Is session still active? - Is operation allowed? ↓ 3. If valid: - Injects X-Tenant-Id: {virtual_tenant_id} - Routes to appropriate service ↓ 4. Service processes request: - Reads/writes data for virtual tenant - No knowledge of demo vs. real tenant ↓ 5. Response returned to user ``` ### Session Cleanup ``` Every hour (CronJob): 1. Demo Cleanup Service queries: SELECT * FROM demo_sessions WHERE status = 'active' AND expires_at < NOW() ↓ 2. For each expired session: - Mark as 'expired' - Delete all virtual tenant data - Delete Redis keys - Update statistics ↓ 3. Weekly cleanup: DELETE FROM demo_sessions WHERE status = 'destroyed' AND destroyed_at < NOW() - INTERVAL '7 days' ``` ## Database Schema ### demo_sessions Table ```sql CREATE TABLE demo_sessions ( id UUID PRIMARY KEY, session_id VARCHAR(100) UNIQUE NOT NULL, -- Ownership user_id UUID, ip_address VARCHAR(45), user_agent VARCHAR(500), -- Demo linking base_demo_tenant_id UUID NOT NULL, virtual_tenant_id UUID NOT NULL, demo_account_type VARCHAR(50) NOT NULL, -- Lifecycle status VARCHAR(20) NOT NULL, -- active, expired, destroyed created_at TIMESTAMP WITH TIME ZONE NOT NULL, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, last_activity_at TIMESTAMP WITH TIME ZONE, destroyed_at TIMESTAMP WITH TIME ZONE, -- Metrics request_count INTEGER DEFAULT 0, data_cloned BOOLEAN DEFAULT FALSE, redis_populated BOOLEAN DEFAULT FALSE, -- Metadata metadata JSONB ); CREATE INDEX idx_session_id ON demo_sessions(session_id); CREATE INDEX idx_virtual_tenant ON demo_sessions(virtual_tenant_id); CREATE INDEX idx_status ON demo_sessions(status); CREATE INDEX idx_expires_at ON demo_sessions(expires_at); ``` ### tenants Table (Updated) ```sql ALTER TABLE tenants ADD COLUMN is_demo BOOLEAN DEFAULT FALSE; ALTER TABLE tenants ADD COLUMN is_demo_template BOOLEAN DEFAULT FALSE; ALTER TABLE tenants ADD COLUMN base_demo_tenant_id UUID; ALTER TABLE tenants ADD COLUMN demo_session_id VARCHAR(100); ALTER TABLE tenants ADD COLUMN demo_expires_at TIMESTAMP WITH TIME ZONE; CREATE INDEX idx_is_demo ON tenants(is_demo); CREATE INDEX idx_demo_session ON tenants(demo_session_id); ``` ## Deployment ### Initial Deployment ```bash # 1. Deploy infrastructure (databases, redis, rabbitmq) kubectl apply -k infrastructure/kubernetes/overlays/prod # 2. Run migrations # (Automatically handled by migration jobs) # 3. Seed demo data # (Automatically handled by demo-seed-* jobs) # 4. Verify demo system kubectl get jobs -n bakery-ia | grep demo-seed kubectl logs -f job/demo-seed-users -n bakery-ia kubectl logs -f job/demo-seed-tenants -n bakery-ia kubectl logs -f job/demo-seed-inventory -n bakery-ia # 5. Test demo session creation curl -X POST http://your-domain/api/demo/session/create \ -H "Content-Type: application/json" \ -d '{"demo_account_type": "individual_bakery"}' ``` ### Using Tilt (Local Development) ```bash # Start Tilt tilt up # Demo resources in Tilt UI: # - databases: demo-session-db # - migrations: demo-session-migration # - services: demo-session-service # - demo-init: demo-seed-users, demo-seed-tenants, demo-seed-inventory # - config: patch-demo-session-env (sets CLONE_JOB_IMAGE dynamically) # Tilt automatically: # 1. Gets inventory-service image tag (e.g., tilt-abc123) # 2. Patches demo-session-service with CLONE_JOB_IMAGE env var # 3. Clone jobs use this image with IfNotPresent pull policy ``` ## Monitoring ### Key Metrics ```python # Session Statistics GET /api/demo/stats { "total_sessions": 1250, "active_sessions": 45, "expired_sessions": 980, "destroyed_sessions": 225, "avg_duration_minutes": 18.5, "total_requests": 125000 } ``` ### Health Checks ```bash # Demo Session Service curl http://demo-session-service:8000/health # Check active sessions kubectl exec -it deployment/demo-session-service -- \ python -c "from app.services import *; print(get_active_sessions())" ``` ### Logs ```bash # Demo session service logs kubectl logs -f deployment/demo-session-service -n bakery-ia # Demo seed job logs kubectl logs job/demo-seed-inventory -n bakery-ia # Cleanup cron job logs kubectl logs -l app=demo-cleanup -n bakery-ia --tail=100 ``` ## Scaling Considerations ### Current Limits - **Concurrent Sessions**: ~200 (2 replicas × ~100 sessions each) - **Redis Memory**: ~1-2 GB (10 MB per session × 200) - **PostgreSQL**: ~5-10 GB (30 MB per virtual tenant × 200) - **Session Duration**: 30 minutes (configurable) - **Extensions**: Maximum 3 per session ### Scaling Up ```yaml # Scale demo-session-service kubectl scale deployment/demo-session-service --replicas=4 -n bakery-ia # Increase Redis memory (if needed) # Edit redis deployment, increase memory limits # Adjust session settings # Edit demo-session configmap: DEMO_SESSION_DURATION_MINUTES: 45 # Increase session time DEMO_SESSION_MAX_EXTENSIONS: 5 # Allow more extensions ``` ## Security ### Public Demo Credentials Demo credentials are **intentionally public** for prospect access: - Published on marketing website - Included in demo documentation - Safe because sessions are isolated and ephemeral ### Restrictions 1. **No Destructive Operations**: DELETE blocked 2. **Limited Modifications**: Only realistic testing operations 3. **No Sensitive Data Access**: Cannot change passwords, billing, etc. 4. **Automatic Expiration**: Sessions auto-destroy after 30 minutes 5. **Rate Limiting**: Standard gateway rate limits apply 6. **No AI Training**: Forecast API blocked for demo accounts (no trained models) 7. **Scheduler Prevention**: Procurement scheduler filters out demo tenants ### Data Privacy - No real customer data in demo tenants - Session data automatically deleted - Anonymized analytics only ## Troubleshooting ### Session Creation Fails ```bash # Check demo-session-service health kubectl get pods -l app=demo-session-service -n bakery-ia # Check logs kubectl logs deployment/demo-session-service -n bakery-ia --tail=50 # Verify base demo tenants exist kubectl exec -it deployment/tenant-service -- \ psql $TENANT_DATABASE_URL -c \ "SELECT id, name, subdomain FROM tenants WHERE is_demo_template = true;" ``` ### Sessions Not Cleaning Up ```bash # Check cleanup cronjob kubectl get cronjobs -n bakery-ia kubectl get jobs -l app=demo-cleanup -n bakery-ia # Manually trigger cleanup kubectl create job --from=cronjob/demo-session-cleanup manual-cleanup-$(date +%s) -n bakery-ia # Check for orphaned sessions kubectl exec -it deployment/demo-session-service -- \ psql $DEMO_SESSION_DATABASE_URL -c \ "SELECT status, COUNT(*) FROM demo_sessions GROUP BY status;" ``` ### Redis Connection Issues ```bash # Test Redis connectivity kubectl exec -it deployment/demo-session-service -- \ python -c "import redis; r=redis.Redis(host='redis-service'); print(r.ping())" # Check Redis memory usage kubectl exec -it deployment/redis -- redis-cli INFO memory ``` ## Technical Implementation Details ### Data Cloning Architecture **Choice: Kubernetes Job-based Cloning** (selected over service-based endpoints) **Why K8s Jobs**: - Database-level operations (faster than API calls) - Scalable (one job per session, isolated execution) - No service-specific clone endpoints needed - Works in both dev (Tilt) and production **How it Works**: 1. Demo-session-service creates K8s Job via K8s API 2. Job uses `CLONE_JOB_IMAGE` environment variable (configured image) 3. In **Dev (Tilt)**: `patch-demo-session-env` sets dynamic Tilt image tag 4. In **Production**: Deployment manifest has stable release tag 5. Job runs `clone_demo_tenant.py` with `imagePullPolicy: IfNotPresent` 6. Script uses ORM models to clone data safely **Environment-based Image Configuration**: ```yaml # Demo-session deployment env: - name: CLONE_JOB_IMAGE value: "bakery/inventory-service:latest" # Overridden by Tilt in dev # Tilt automatically patches this to match actual inventory-service tag # e.g., bakery/inventory-service:tilt-abc123 ``` ### AI Model Restrictions **Fake Models in Database**: - Demo tenants have AI model records in database - No actual model files (.pkl, .h5) stored - Forecast API blocked at gateway level for demo accounts - Returns user-friendly error message **Scheduler Prevention**: - Procurement scheduler filters `is_demo = true` tenants - Prevents automated procurement runs on demo data - Manual procurement still allowed for realistic testing ## Future Enhancements 1. **Analytics Dashboard**: Track demo → paid conversion rates 2. **Guided Tours**: In-app tutorials for demo users 3. **Custom Demo Scenarios**: Let prospects choose specific features 4. **Demo Recordings**: Capture anonymized session recordings 5. **Multi-Region**: Deploy demo infrastructure in EU, US, LATAM 6. **Sales & Orders Cloning**: Extend clone script to copy sales and orders data ## References - [Demo Session Service API](services/demo_session/README.md) - [Demo Data Seeding](scripts/demo/README.md) - [Gateway Middleware](gateway/app/middleware/README.md) - [Kubernetes Manifests](infrastructure/kubernetes/base/components/demo-session/)