# Final Implementation Requirements & Constraints ## Complete Specification - Ready for Implementation **Status:** Complete specification, ready to begin **Critical Issues:** 4 major + original gaps **Implementation Time:** 7-10 days --- ## Summary of All Requirements This document consolidates: 1. Original tenancy gaps (from audits) 2. Free trial signup simplification 3. Four critical new issues discovered 4. Current database state context --- ## CRITICAL ISSUE A: Plan Allocation & Credits Must Be Strict ### Problem - Inconsistent plan fallback logic in old code - Some accounts created with 0 credits despite plan having credits - Enterprise plan being auto-assigned (should never happen) - Multiple fallback paths causing confusion ### Strict Rules (NO EXCEPTIONS) #### Rule A1: Free Trial Signup ```python # /signup route ALWAYS assigns: plan_slug = "free-trial" # First choice if not exists: plan_slug = "free" # ONLY fallback # NEVER assign: starter, growth, scale, enterprise automatically ``` #### Rule A2: Credit Seeding (MANDATORY) ```python # On account creation, ALWAYS: account.credits = plan.get_effective_credits_per_month() account.status = 'trial' # For free-trial/free plans # Log transaction: CreditTransaction.create( account=account, transaction_type='subscription', amount=credits, description='Initial credits from {plan.name}', metadata={'registration': True, 'plan_slug': plan.slug} ) ``` #### Rule A3: Enterprise Plan Protection ```python # Enterprise plan (slug='enterprise') must NEVER be auto-assigned # Only Developer/Admin can manually assign enterprise # Check in serializer: if plan.slug == 'enterprise' and not user.is_developer(): raise ValidationError("Enterprise plan requires manual assignment") ``` #### Rule A4: Paid Plan Assignment ```python # Paid plans (starter, growth, scale) can ONLY be assigned: # 1. From /account/upgrade endpoint (inside app) # 2. After payment confirmation # NEVER during initial /signup ``` ### Implementation Location - **File:** [`backend/igny8_core/auth/serializers.py:276`](backend/igny8_core/auth/serializers.py:276) - **Changes:** Already applied, but needs enterprise protection added --- ## CRITICAL ISSUE B: Subscription Date Accuracy ### Problem - Trial accounts have missing or incorrect period dates - Bank transfer activation doesn't set proper subscription periods - No clear rule for date calculation ### Strict Rules (ZERO AMBIGUITY) #### Rule B1: Free Trial Signup ```python from django.utils import timezone from datetime import timedelta # Constants TRIAL_DAYS = 14 # or 30, must be defined # On registration: now = timezone.now() subscription = Subscription.objects.create( account=account, status='trialing', payment_method='trial', # or None current_period_start=now, current_period_end=now + timedelta(days=TRIAL_DAYS), cancel_at_period_end=False ) account.status = 'trial' ``` #### Rule B2: Bank Transfer Activation ```python # When admin confirms payment: now = timezone.now() # For monthly plan: if plan.billing_cycle == 'monthly': period_end = now + timedelta(days=30) elif plan.billing_cycle == 'annual': period_end = now + timedelta(days=365) subscription.payment_method = 'bank_transfer' subscription.external_payment_id = payment_ref subscription.status = 'active' subscription.current_period_start = now subscription.current_period_end = period_end subscription.save() account.status = 'active' account.credits = plan.get_effective_credits_per_month() account.save() ``` #### Rule B3: Subscription Renewal ```python # On renewal (manual or webhook): previous_end = subscription.current_period_end # Set new period (NO GAP, NO OVERLAP) subscription.current_period_start = previous_end if plan.billing_cycle == 'monthly': subscription.current_period_end = previous_end + timedelta(days=30) elif plan.billing_cycle == 'annual': subscription.current_period_end = previous_end + timedelta(days=365) # Reset credits account.credits = plan.get_effective_credits_per_month() account.save() subscription.save() ``` ### Implementation Location - **File:** `backend/igny8_core/business/billing/views.py` (bank transfer endpoint) - **File:** [`backend/igny8_core/auth/serializers.py:276`](backend/igny8_core/auth/serializers.py:276) (registration) --- ## CRITICAL ISSUE C: Superuser Session Contamination ### Problem **CRITICAL SECURITY ISSUE:** - New regular users sometimes logged in as superuser - Frontend picks up admin/developer session from same browser - Catastrophic for tenancy isolation ### Root Cause **Session auth + JWT auth coexistence:** - Admin logs into Django admin → Session cookie created - Regular user visits frontend → Browser sends session cookie - Backend authenticates as admin instead of JWT user - Frontend suddenly has superuser access ### Strict Fix (MANDATORY) #### Fix C1: Disable Session Auth for API Routes **File:** [`backend/igny8_core/api/authentication.py`](backend/igny8_core/api/authentication.py) ```python # ViewSets should ONLY use: authentication_classes = [JWTAuthentication] # NO CSRFExemptSessionAuthentication # Exception: Admin panel can use session # But /api/* routes must be JWT-only ``` #### Fix C2: Middleware Superuser Detection **File:** [`backend/igny8_core/auth/middleware.py:25`](backend/igny8_core/auth/middleware.py:25) Add after account validation: ```python def process_request(self, request): # ... existing code ... # CRITICAL: Detect superuser on non-admin routes if not request.path.startswith('/admin/'): if hasattr(request, 'user') and request.user and request.user.is_superuser: # Non-admin route but superuser authenticated # This should ONLY happen for JWT with developer role auth_header = request.META.get('HTTP_AUTHORIZATION', '') if not auth_header.startswith('Bearer '): # Superuser via session, not JWT - BLOCK IT from django.contrib.auth import logout logout(request) return JsonResponse({ 'success': False, 'error': 'Session authentication not allowed for API routes. Please use JWT.' }, status=403) ``` #### Fix C3: Frontend Explicit Logout on Register **File:** [`frontend/src/store/authStore.ts:120`](frontend/src/store/authStore.ts:120) Before registration: ```typescript register: async (registerData) => { // Clear any existing sessions first try { await fetch(`${API_BASE_URL}/v1/auth/logout/`, { method: 'POST', credentials: 'include' // Clear session cookies }); } catch (e) { // Ignore errors, just ensure clean state } set({ loading: true }); // ... rest of registration ... } ``` #### Fix C4: Frontend Clear All Auth on Logout ```typescript logout: () => { // Clear cookies document.cookie.split(";").forEach(c => { document.cookie = c.trim().split("=")[0] + "=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/"; }); // Clear localStorage localStorage.clear(); // Clear state set({ user: null, token: null, refreshToken: null, isAuthenticated: false, loading: false }); }, ``` ### Implementation Priority 🔥 **CRITICAL** - Fix before any production deployment --- ## CRITICAL ISSUE D: Docker Build Cache Causing Router Errors ### Problem **Symptoms:** - `useLocation() may be used only in the context of a component` - `useNavigate` similar errors - Errors appear in: Planner, Writer, Sites modules and subpages - **Resolved by removing containers and rebuilding WITHOUT code change** ### Root Cause ✅ **Not a code issue - Docker build cache issue** - Stale node_modules cached in Docker layers - Stale build artifacts from previous versions - React Router hydration mismatch between cached and new code ### Strict Fix #### Fix D1: Frontend Dockerfile - No Build Cache **File:** `frontend/Dockerfile.dev` Ensure these lines: ```dockerfile # Copy package files COPY package*.json ./ # Clean install (no cache) RUN npm ci --only=production=false # Remove any cached builds RUN rm -rf dist/ .vite/ node_modules/.vite/ # Copy source COPY . . ``` #### Fix D2: Docker Compose - No Volume Cache for node_modules **File:** [`docker-compose.app.yml:77`](docker-compose.app.yml:77) Current: ```yaml volumes: - /data/app/igny8/frontend:/app:rw ``` Change to: ```yaml volumes: - /data/app/igny8/frontend:/app:rw # Exclude node_modules from volume mount to prevent cache issues - /app/node_modules ``` #### Fix D3: Build Script - Force Clean Build **File:** `frontend/rebuild.sh` (create this) ```bash #!/bin/bash # Force clean frontend rebuild echo "Removing old containers..." docker rm -f igny8_frontend igny8_marketing_dev igny8_sites echo "Removing old images..." docker rmi -f igny8-frontend-dev:latest igny8-marketing-dev:latest igny8-sites-dev:latest echo "Rebuilding without cache..." cd /data/app/igny8/frontend docker build --no-cache -t igny8-frontend-dev:latest -f Dockerfile.dev . docker build --no-cache -t igny8-marketing-dev:latest -f Dockerfile.marketing.dev . cd /data/app/igny8/sites docker build --no-cache -t igny8-sites-dev:latest -f Dockerfile.dev . echo "Restarting containers..." cd /data/app/igny8 docker compose -f docker-compose.app.yml up -d igny8_frontend igny8_marketing_dev igny8_sites echo "Done! Frontend rebuilt fresh." ``` #### Fix D4: Deployment Best Practice ```bash # After git push, ALWAYS do: docker compose -f docker-compose.app.yml down docker compose -f docker-compose.app.yml build --no-cache docker compose -f docker-compose.app.yml up -d # This ensures no stale cache ``` ### Why This Fixes Router Errors - Fresh node_modules every build - No stale React Router components - No hydration mismatches - Clean build artifacts --- ## Updated Implementation Plan with All Issues ### Phase 0: Pre-Implementation Checklist ✅ - [x] Analyze database state - [x] Document all relationships - [x] Identify all gaps - [x] Create free trial code changes - [x] Document all 4 critical issues ### Phase 1: Free Trial Signup (Day 1) **Actions:** 1. ✅ Update RegisterSerializer (already done) 2. ✅ Update SignUpForm (already done) 3. ⏳ Create free-trial plan: `docker exec igny8_backend python manage.py create_free_trial_plan` 4. ✅ Add enterprise plan protection 5. ✅ Create Subscription with correct trial dates 6. Test signup flow **Critical Constraints:** - ✅ Must assign free-trial or free ONLY - ✅ Must seed credits from plan - ✅ Must create Subscription with trial dates - ✅ Must log CreditTransaction ### Phase 2: Superuser Session Fix (Day 1 - CRITICAL) **Actions:** 1. Remove CSRFExemptSessionAuthentication from API ViewSets 2. Add middleware superuser detection 3. Add frontend logout before register 4. Add frontend cookie clearing on logout 5. Test: Regular user cannot access superuser session **Critical Constraints:** - 🔥 API routes must be JWT-only - 🔥 Superuser on API route without JWT = logout - 🔥 Registration clears old sessions first ### Phase 3: Docker Build Cache Fix (Day 1 - CRITICAL) **Actions:** 1. Update frontend Dockerfile to use `npm ci` 2. Add node_modules volume exclusion 3. Create rebuild.sh script 4. Document deployment procedure 5. Test: Router errors don't occur after rebuild **Critical Constraints:** - 🔥 Always use `--no-cache` for frontend builds - 🔥 Exclude node_modules from volume mounts - 🔥 Clean rebuild after every git deployment ### Phase 4: Payment Method Fields (Day 2) **Actions:** 1. Create migration 0007 2. Add payment_method to Account
3. Add payment_method, external_payment_id to Subscription 4. Make stripe_subscription_id nullable 5. Add 'pending_payment' status 6. Run migration ### Phase 5: Subscription Date Accuracy (Day 2-3) **Actions:** 1. Update RegisterSerializer to create Subscription with trial dates 2. Update bank transfer endpoint with strict date rules 3. Add renewal logic with correct date transitions 4. Test all date transitions **Critical Constraints:** - ✅ Trial: current_period_end = now + TRIAL_DAYS - ✅ Activation: current_period_end = now + billing_cycle - ✅ Renewal: current_period_start = previous_end (NO GAP) ### Phase 6: Account Validation Helper (Day 3) **Actions:** 1. Create validate_account_and_plan() in auth/utils.py 2. Update middleware to use helper 3. Update API key authentication to use helper 4. Test validation blocks suspended/cancelled accounts ### Phase 7: Throttling Fix (Day 4) **Actions:** 1. Remove blanket authenticated bypass 2. Add get_cache_key() for per-account throttling 3. Test throttling enforces limits per account ### Phase 8: Bank Transfer Endpoint (Day 4-5) **Actions:** 1. Create BillingViewSet 2. Add confirm_bank_transfer endpoint 3. Add URL routes 4. Test payment confirmation flow ### Phase 9: Comprehensive Tests (Day 6) **Actions:** 1. Test free trial signup 2. Test credit seeding 3. Test subscription dates 4. Test superuser isolation 5. Test API key validation 6. Test throttling 7. Test bank transfer ### Phase 10: Documentation & Verification (Day 7) **Actions:** 1. Update all documentation 2. Run full system test 3. Verify all flows 4. Deploy to production --- ## Critical Constraints Summary ### A. Plan & Credits (STRICT) ``` ✅ free-trial → free (fallback) → ERROR (nothing else) ✅ Credits always seeded on registration ✅ CreditTransaction always logged ❌ Never auto-assign enterprise ❌ Never allow 0 credits after registration ``` ### B. Subscription Dates (PRECISE) ``` ✅ Trial: start=now, end=now+14days ✅ Activation: start=now, end=now+billing_cycle ✅ Renewal: start=previous_end, end=start+billing_cycle ❌ No gaps between periods ❌ No overlapping periods ``` ### C. Superuser Isolation (SECURITY) ``` ✅ API routes: JWT auth ONLY ✅ Superuser on /api/* without JWT → logout + error ✅ Registration clears existing sessions ✅ Logout clears all cookies and localStorage ❌ Never allow session auth for API ❌ Never allow superuser contamination ``` ### D. Docker Build (STABILITY) ``` ✅ Use npm ci (not npm install) ✅ Exclude node_modules from volume mounts ✅ Always build with --no-cache after git push ✅ Removing containers + rebuild fixes router errors ❌ Don't cache build artifacts between deployments ``` --- ## Verification Matrix ### Test 1: Free Trial Signup ```bash # Prerequisites: free-trial plan exists, code deployed # Action: Visit /signup, fill form, submit # Expected: # - Account created with status='trial' # - Credits = 2000 (or plan.included_credits) # - Subscription created with trial dates # - CreditTransaction logged # - Redirect to /sites # - User can immediately use app # Database check: docker exec igny8_backend python manage.py shell -c " from igny8_core.auth.models import User; u = User.objects.latest('id'); assert u.account.status == 'trial'; assert u.account.credits > 0; assert u.account.plan.slug in ['free-trial', 'free']; print('✅ Free trial signup working') " ``` ### Test 2: Superuser Isolation ```bash # Prerequisites: Regular user account, admin logged into /admin # Action: Login as regular user in frontend # Expected: # - User sees only their account # - User does NOT have superuser privileges # - API calls use JWT, not session # Test: # Inspect frontend network tab # All API calls must have: Authorization: Bearer # No sessionid cookies sent to /api/* ``` ### Test 3: Docker Build Stability ```bash # Action: Deploy code, rebuild containers cd /data/app/igny8 docker compose -f docker-compose.app.yml down docker build --no-cache -t igny8-frontend-dev:latest -f frontend/Dockerfile.dev frontend/ docker compose -f docker-compose.app.yml up -d # Expected: # - No useLocation errors # - No useNavigate errors # - Planner, Writer, Sites pages load correctly # - Router context available everywhere ``` ### Test 4: Subscription Dates ```bash # Action: Confirm bank transfer for trial account curl -X POST /api/v1/billing/confirm-bank-transfer/ \ -H "Authorization: Bearer " \ -d '{ "account_id": 123, "external_payment_id": "BT-001", "amount": "29.99", "payer_name": "Test User" }' # Expected: # - subscription.status = 'active' # - subscription.current_period_start = now # - subscription.current_period_end = now + 30 days # - account.status = 'active' # - account.credits = plan monthly credits ``` --- ## Implementation Order (Revised) ### Day 1 (CRITICAL) 1. ✅ Free trial signup (code changes done, need to create plan) 2. 🔥 Superuser session fix (MUST FIX) 3. 🔥 Docker build cache fix (MUST FIX) ### Day 2 4. Payment method fields migration 5. Subscription date accuracy updates ### Day 3 6. Account validation helper 7. API key authentication fix ### Day 4 8. Throttling fix 9. Bank transfer endpoint ### Day 5-6 10. Comprehensive tests ### Day 7 11. Documentation 12. Deployment 13. Verification --- ## Rollback Plan (If Any Issue Occurs) ### Database Rollback ```bash docker exec igny8_backend python manage.py migrate igny8_core_auth 0006_soft_delete_and_retention ``` ### Code Rollback ```bash git revert docker compose -f docker-compose.app.yml down docker compose -f docker-compose.app.yml up -d ``` ### Emergency Disable Feature Flags Add to settings.py: ```python # Emergency feature flags TENANCY_ENABLE_FREE_TRIAL = False # Fall back to old signup TENANCY_VALIDATE_API_KEY = False # Disable validation temporarily TENANCY_STRICT_JWT_ONLY = False # Allow session auth temporarily ``` --- ## Success Criteria (ALL must pass) - ✅ Signup creates account with correct credits - ✅ Subscription has accurate start/end dates - ✅ Regular users NEVER get superuser access - ✅ Router errors don't appear after container rebuild - ✅ API key validates account status - ✅ Throttling enforces per-account limits - ✅ Bank transfer confirmation works - ✅ All tests passing (>80% coverage) - ✅ Zero authentication bypasses - ✅ Zero credit seeding failures --- ## Files Reference ### Analysis Documents (This Folder) 1. **CURRENT-STATE-CONTEXT.md** - Database state from Docker query 2. **IMPLEMENTATION-SUMMARY.md** - Context gathering summary 3. **FINAL-IMPLEMENTATION-REQUIREMENTS.md** (this file) - Complete spec 4. **FINAL-IMPLEMENTATION-PLAN-COMPLETE.md** - Detailed phase guide 5. **FREE-TRIAL-SIGNUP-FIX.md** - Signup flow specifics 6. **COMPLETE-IMPLEMENTATION-PLAN.md** - Original gap analysis 7. **Final_Flow_Tenancy.md** - Target flow specifications 8. **Tenancy_Audit_Report.md** - Audit findings 9. **audit_fixes.md** - Previous recommendations 10. **tenancy-implementation-plan.md** - Original plan ### Code Changes Made (Review Before Deploy) 1. `backend/igny8_core/auth/serializers.py` - Free trial registration 2. `frontend/src/components/auth/SignUpForm.tsx` - Simplified signup 3. `backend/igny8_core/auth/management/commands/create_free_trial_plan.py` - Plan creation ### Code Changes Needed (Not Yet Made) 1. Middleware - Superuser detection 2. Authentication - Remove session auth from API 3. Frontend authStore - Clear sessions before register 4. Dockerfile - No-cache build 5. docker-compose.app.yml - Exclude node_modules volume 6. All Phase 4-10 changes from FINAL-IMPLEMENTATION-PLAN-COMPLETE.md --- ## Hand-off Instructions **To implement this system:** 1. **Review code changes** in serializer and frontend 2. **Start with Day 1 critical fixes:** - Create free-trial plan - Fix superuser session contamination - Fix Docker build caching 3. **Then proceed** through Phase 4-10 4. **Use** `FINAL-IMPLEMENTATION-PLAN-COMPLETE.md` as step-by-step guide 5. **Reference** `CURRENT-STATE-CONTEXT.md` for what exists in DB **All specifications are complete, accurate, and ready for implementation.**