veza/docs/audits/INTEGRATION_ISSUES_INDEX.json

1784 lines
62 KiB
JSON
Raw Normal View History

2025-12-22 21:00:50 +00:00
{
"meta": {
"generated_at": "2025-01-27T00:00:00Z",
"scope": "backend veza-backend-api <-> frontend apps/web integration",
"repo_commit": "e4212ee59420e1c2d53e84af5cba1d9351d123d5",
"auditor": "Cursor AI",
"severity_model": {
"P0": "Critical - breaks core functionality or security, must fix immediately",
"P1": "High - frequent functional breakage or major UX regression",
"P2": "Medium - correctness/consistency issues that will create bugs soon",
"P3": "Low - maintainability, dead code, tech debt"
}
},
"summary": {
"health_score_0_to_10": 4,
"issue_counts": {
"P0": 5,
"P1": 10,
"P2": 8,
"P3": 7
},
"top5_issue_ids": [
"INT-000001",
"INT-000002",
"INT-000003",
"INT-000004",
"INT-000005"
]
},
"issues": [
{
"id": "INT-000001",
"severity": "P0",
"priority_rank": 1,
"title": "CORS Configuration Will Break Production - Empty Origins Reject All Requests",
"component": {
"frontend": "",
"backend": "veza-backend-api/internal/api/router.go, internal/config/config.go, internal/middleware/cors.go",
"infra": "deployment configs"
},
"category": ["cors", "env", "production"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": true,
"security_risk": "medium",
"notes": "All API requests will fail in production if CORS_ALLOWED_ORIGINS is not set"
},
"evidence": {
"files": [
{
"path": "veza-backend-api/internal/api/router.go",
"lines": "L119-L128",
"excerpt": "if r.config != nil {\n router.Use(middleware.CORS(r.config.CORSOrigins))\n if len(r.config.CORSOrigins) == 0 {\n r.logger.Warn(\"CORS origins not configured - strict mode enabled: ALL CORS requests will be rejected.\")\n }\n}"
},
{
"path": "veza-backend-api/internal/config/config.go",
"lines": "L638-L664",
"excerpt": "case EnvProduction:\n return []string{} // Empty list = reject all"
},
{
"path": "veza-backend-api/internal/middleware/cors.go",
"lines": "L34-L37",
"excerpt": "if len(allowed) == 0 {\n return false // Reject all origins"
}
],
"runtime": []
},
"repro": {
"steps": [
"Set APP_ENV=production",
"Leave CORS_ALLOWED_ORIGINS unset",
"Start backend server",
"Make CORS request from frontend",
"Observe: Request rejected with CORS error"
],
"expected": "CORS requests should work with configured origins",
"actual": "All CORS requests rejected because origins list is empty"
},
"root_cause": "Production environment defaults to strict mode (empty origins list) but frontend expects CORS to work. No validation ensures CORS_ALLOWED_ORIGINS is set in production.",
"fix_plan": {
"minimal_steps": [
"Add startup validation: fail fast if APP_ENV=production and CORS_ALLOWED_ORIGINS empty",
"Document CORS_ALLOWED_ORIGINS as REQUIRED in production deployment guide",
"Update docker-compose.production.yml with example CORS_ALLOWED_ORIGINS",
"Add integration test with production-like CORS config"
],
"tests_to_add": [
"E2E test: Verify CORS works with configured origins",
"Unit test: Verify startup fails if prod + empty CORS"
],
"regression_risks": ["None - this is a new validation"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000002",
"severity": "P0",
"priority_rank": 2,
"title": "Multiple Auth Storage Mechanisms Cause Token Sync Failures",
"component": {
"frontend": "apps/web/src/services/tokenStorage.ts, apps/web/src/services/api/client.ts, apps/web/src/stores/auth.ts, apps/web/src/utils/token-manager.ts",
"backend": "",
"infra": ""
},
"category": ["auth", "state-management"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": true,
"security_risk": "low",
"notes": "Tokens stored in localStorage, Zustand persist, and TokenStorage can desync, causing auth failures"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/tokenStorage.ts",
"lines": "L35-L36",
"excerpt": "localStorage.setItem(ACCESS_TOKEN_KEY, accessToken);\nlocalStorage.setItem(REFRESH_TOKEN_KEY, refreshToken);"
},
{
"path": "apps/web/src/services/api/client.ts",
"lines": "L48-L64",
"excerpt": "// Fallback: Si pas de token dans TokenStorage, chercher dans auth-storage Zustand\nif (!token && typeof window !== 'undefined') {\n const authStorage = localStorage.getItem('auth-storage');\n // Parse Zustand storage..."
},
{
"path": "apps/web/src/utils/token-manager.ts",
"lines": "L25",
"excerpt": "localStorage.setItem(this.ACCESS_TOKEN_KEY, accessToken);"
}
],
"runtime": []
},
"repro": {
"steps": [
"Login via Zustand store",
"Token stored in auth-storage (Zustand)",
"API call uses TokenStorage.getAccessToken()",
"TokenStorage returns null",
"Fallback to Zustand storage works but fragile"
],
"expected": "Single source of truth for token storage",
"actual": "Tokens stored in 3+ different places, can desync"
},
"root_cause": "No single source of truth for token storage. Multiple classes (TokenStorage, token-manager, Zustand) all manage tokens independently.",
"fix_plan": {
"minimal_steps": [
"Standardize on TokenStorage class as single source",
"Remove token storage from Zustand (keep only user and isAuthenticated flags)",
"Remove token-manager.ts or migrate all usage to TokenStorage",
"Update apiClient interceptor to only use TokenStorage",
"Add tests for token persistence across page reloads"
],
"tests_to_add": [
"Test: Token persists after page reload",
"Test: Token sync across multiple tabs",
"Test: Token cleared on logout"
],
"regression_risks": ["Existing code using Zustand token storage may break"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000003",
"severity": "P0",
"priority_rank": 3,
"title": "Type Mismatch: User.id (string vs number) Causes Runtime Errors",
"component": {
"frontend": "apps/web/src/types/api.ts, apps/web/src/features/auth/types/index.ts, apps/web/src/services/api/auth.ts",
"backend": "veza-backend-api/internal/dto/user_response.go",
"infra": ""
},
"category": ["schema-drift", "type-safety"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": true,
"security_risk": "none",
"notes": "TypeScript type errors and runtime coercion bugs when accessing user.id"
},
"evidence": {
"files": [
{
"path": "apps/web/src/types/api.ts",
"lines": "L3",
"excerpt": "id: string; // ✅ Correct"
},
{
"path": "apps/web/src/features/auth/types/index.ts",
"lines": "L8",
"excerpt": "id: number; // ❌ Wrong type"
},
{
"path": "veza-backend-api/internal/dto/user_response.go",
"lines": "L30",
"excerpt": "ID: uuid.UUID // Serializes as string"
}
],
"runtime": []
},
"repro": {
"steps": [
"Import User type from features/auth/types",
"Access user.id in TypeScript",
"TypeScript expects number but receives string",
"Runtime: String operations on number or vice versa"
],
"expected": "User.id is string (UUID) everywhere",
"actual": "Mixed types: string in some places, number in others"
},
"root_cause": "Inconsistent type definitions across frontend modules. Backend uses UUID (string) but some frontend types use number.",
"fix_plan": {
"minimal_steps": [
"Audit all User type definitions in frontend",
"Update apps/web/src/features/auth/types/index.ts to use id: string",
"Update all Zod schemas to validate UUID format",
"Run TypeScript strict mode checks",
"Fix all type errors"
],
"tests_to_add": [
"TypeScript compilation test",
"Runtime validation: Verify user.id is string"
],
"regression_risks": ["Code expecting number may break"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000004",
"severity": "P0",
"priority_rank": 4,
"title": "Deprecated ApiService Expects Wrong Response Format",
"component": {
"frontend": "apps/web/src/services/api.ts",
"backend": "veza-backend-api/internal/handlers/auth.go",
"infra": ""
},
"category": ["api-contract", "error-handling"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": true,
"security_risk": "none",
"notes": "Code using deprecated ApiService will break when backend response format changes"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/api.ts",
"lines": "L238-L239",
"excerpt": "const { user, token } = response.data.data; // Expects flat structure"
},
{
"path": "veza-backend-api/internal/handlers/auth.go",
"lines": "L96-L106",
"excerpt": "RespondSuccess(c, http.StatusOK, dto.LoginResponse{\n User: {...},\n Token: {...}\n}) // Returns { success: true, data: { user: {...}, token: {...} } }"
},
{
"path": "apps/web/src/services/api.ts",
"lines": "L74-L80",
"excerpt": "@deprecated Cette classe est dépréciée. Utilisez apiClient"
}
],
"runtime": []
},
"repro": {
"steps": [
"Use ApiService.login()",
"Backend returns { success: true, data: { user: {...}, token: {...} } }",
"ApiService expects { user, token } directly in data",
"Code works but fragile - depends on exact structure"
],
"expected": "All code uses apiClient with consistent response unwrapping",
"actual": "Deprecated ApiService still used, expects different format"
},
"root_cause": "Legacy ApiService class expects flat structure in data, but backend wraps in nested structure. Class marked deprecated but not fully migrated.",
"fix_plan": {
"minimal_steps": [
"Complete migration from ApiService to apiClient (see MIGRATION_GUIDE.md)",
"Search for all ApiService usage",
"Replace with apiClient calls",
"Remove ApiService class entirely",
"Update all imports"
],
"tests_to_add": [
"Verify all API calls use apiClient",
"No references to ApiService in codebase"
],
"regression_risks": ["Code still using ApiService will break"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000005",
"severity": "P0",
"priority_rank": 5,
"title": "Missing CSRF Protection for State-Changing Operations",
"component": {
"frontend": "apps/web/src/services/csrf.ts, apps/web/src/services/secure-auth.ts",
"backend": "veza-backend-api (no CSRF middleware found)",
"infra": ""
},
"category": ["security", "csrf"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "high",
"notes": "Vulnerable to CSRF attacks on POST/PUT/DELETE requests"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/csrf.ts",
"lines": "L14-L17",
"excerpt": "public async refreshCsrfToken(): Promise<void> {\n // Placeholder: fetch from backend if needed\n // this.csrfToken = ...\n}"
},
{
"path": "apps/web/src/services/secure-auth.ts",
"lines": "L119",
"excerpt": "await csrfService.refreshCsrfToken(); // Does nothing"
}
],
"runtime": []
},
"repro": {
"steps": [
"Create malicious website",
"Include form that POSTs to https://veza.com/api/v1/tracks (user logged in)",
"User visits malicious site",
"Form submits automatically",
"Request succeeds (no CSRF protection)"
],
"expected": "CSRF token required for POST/PUT/DELETE",
"actual": "No CSRF protection implemented"
},
"root_cause": "CSRF protection not implemented. Frontend has placeholder service, backend has no CSRF middleware. Chat server has CSRF but REST API does not.",
"fix_plan": {
"minimal_steps": [
"Implement CSRF token generation endpoint: GET /api/v1/csrf-token",
"Backend: Add CSRF middleware for POST/PUT/DELETE (exclude GET, OPTIONS)",
"Frontend: Fetch CSRF token on app init",
"Update apiClient interceptor to add X-CSRF-Token header",
"Implement csrfService.refreshCsrfToken() to fetch from backend"
],
"tests_to_add": [
"CSRF attack simulation test",
"Verify tokens validated on backend",
"Test token rotation"
],
"regression_risks": ["Existing API calls may fail if CSRF required"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000006",
"severity": "P1",
"priority_rank": 6,
"title": "18 Frontend API Calls Target Missing Backend Endpoints",
"component": {
"frontend": "apps/web/src/features/*/services/*.ts, apps/web/src/services/2fa-service.ts",
"backend": "veza-backend-api/internal/api/router.go",
"infra": ""
},
"category": ["api-contract"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": true,
"security_risk": "none",
"notes": "Frontend calls endpoints that don't exist, causing 404 errors"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/2fa-service.ts",
"lines": "L17, L22, L27, L31, L35",
"excerpt": "GET /api/v1/2fa/status, POST /api/v1/2fa/setup, POST /api/v1/2fa/enable, POST /api/v1/2fa/disable, POST /api/v1/2fa/verify"
},
{
"path": "apps/web/src/features/playlists/services/playlistService.ts",
"lines": "L125, L136, L175, L186, L218",
"excerpt": "Missing: /playlists/:id/collaborators, /playlists/search, /playlists/:id/share, /playlists/recommendations"
},
{
"path": "veza-backend-api/internal/api/router.go",
"lines": "L494-L528",
"excerpt": "Playlist routes only: GET, POST, PUT, DELETE /playlists, POST/DELETE tracks"
}
],
"runtime": []
},
"repro": {
"steps": [
"Call playlistService.addCollaborator()",
"Request: POST /api/v1/playlists/:id/collaborators",
"Backend returns 404",
"Feature broken"
],
"expected": "All frontend API calls have corresponding backend endpoints",
"actual": "18 endpoints called but not implemented"
},
"root_cause": "Frontend features implemented but backend endpoints not created. Incomplete feature implementation.",
"fix_plan": {
"minimal_steps": [
"Audit all frontend API calls (grep apiClient.get/post/put/delete)",
"Compare with backend routes (router.go)",
"For each missing endpoint: implement OR remove frontend call",
"Update API documentation",
"Add integration tests"
],
"tests_to_add": [
"Integration test: Verify all frontend calls have backend endpoints",
"E2E test: Verify feature works end-to-end"
],
"regression_risks": ["Removing frontend calls may break UI features"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000007",
"severity": "P1",
"priority_rank": 7,
"title": "Environment Variable Naming Inconsistency (VITE_API_BASE_URL vs VITE_API_URL)",
"component": {
"frontend": "apps/web/src/config/env.ts, apps/web/scripts/*.sh, apps/web/Dockerfile",
"backend": "",
"infra": "deployment configs"
},
"category": ["env", "build"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": true,
"security_risk": "none",
"notes": "Some scripts use VITE_API_BASE_URL, others use VITE_API_URL, causing build failures"
},
"evidence": {
"files": [
{
"path": "apps/web/src/config/env.ts",
"lines": "L6",
"excerpt": "VITE_API_URL: z.string().url().default('http://127.0.0.1:8080/api/v1')"
},
{
"path": "apps/web/scripts/check_backend.sh",
"lines": "L16",
"excerpt": "API_URL=\"${VITE_API_BASE_URL:-http://localhost:8080/api/v1}\""
},
{
"path": "apps/web/Dockerfile",
"lines": "L17",
"excerpt": "ARG VITE_API_BASE_URL"
}
],
"runtime": []
},
"repro": {
"steps": [
"Set VITE_API_BASE_URL in environment",
"Frontend code reads VITE_API_URL",
"VITE_API_URL is undefined",
"Uses default or throws error"
],
"expected": "Consistent env var name: VITE_API_URL",
"actual": "Mixed usage: VITE_API_BASE_URL and VITE_API_URL"
},
"root_cause": "Migration from VITE_API_BASE_URL to VITE_API_URL incomplete. Some scripts and configs still use old name.",
"fix_plan": {
"minimal_steps": [
"Search for all VITE_API_BASE_URL usage",
"Replace with VITE_API_URL",
"Update scripts/check_backend.sh",
"Update Dockerfile",
"Update deployment docs"
],
"tests_to_add": [
"Verify env var name consistent everywhere",
"Build test with VITE_API_URL"
],
"regression_risks": ["Scripts using old name will break"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000008",
"severity": "P1",
"priority_rank": 8,
"title": "Path Mismatch: /users/:userId/profile vs /users/:id",
"component": {
"frontend": "apps/web/src/features/profile/services/profileService.ts",
"backend": "veza-backend-api/internal/api/router.go",
"infra": ""
},
"category": ["api-contract"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": true,
"security_risk": "none",
"notes": "Frontend calls /users/:userId/profile but backend expects /users/:id"
},
"evidence": {
"files": [
{
"path": "apps/web/src/features/profile/services/profileService.ts",
"lines": "L17, L43",
"excerpt": "GET /api/v1/users/${userId}/profile, PUT /api/v1/users/${userId}/profile"
},
{
"path": "veza-backend-api/internal/api/router.go",
"lines": "L344, L358",
"excerpt": "GET /api/v1/users/:id, PUT /api/v1/users/:id"
}
],
"runtime": []
},
"repro": {
"steps": [
"Call profileService.getProfile(userId)",
"Request: GET /api/v1/users/:userId/profile",
"Backend returns 404",
"Feature broken"
],
"expected": "Frontend paths match backend routes",
"actual": "Frontend uses /profile suffix, backend doesn't"
},
"root_cause": "Frontend uses /profile suffix but backend routes don't include it. Inconsistent API design.",
"fix_plan": {
"minimal_steps": [
"Option A: Update frontend to use /users/:id (recommended)",
"Option B: Add /profile routes to backend",
"Update profileService to use correct paths",
"Test profile features"
],
"tests_to_add": [
"Integration test: Verify profile endpoints work",
"E2E test: Verify profile page loads"
],
"regression_risks": ["Changing paths may break existing code"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000009",
"severity": "P1",
"priority_rank": 9,
"title": "Error Code Type Mismatch: number vs string in ApiError",
"component": {
"frontend": "apps/web/src/types/api.ts, apps/web/src/schemas/validation.ts",
"backend": "veza-backend-api/internal/errors/codes.go",
"infra": ""
},
"category": ["schema-drift", "error-handling"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Type mismatches in error handling, potential runtime errors"
},
"evidence": {
"files": [
{
"path": "apps/web/src/types/api.ts",
"lines": "L245",
"excerpt": "code: number; // ✅ Correct"
},
{
"path": "apps/web/src/schemas/validation.ts",
"lines": "L338",
"excerpt": "code: z.string(), // ❌ Wrong type"
},
{
"path": "veza-backend-api/internal/errors/codes.go",
"lines": "L1-L32",
"excerpt": "ErrorCode is int type"
}
],
"runtime": []
},
"repro": {
"steps": [
"Backend returns error with code: 1000 (number)",
"Frontend Zod schema expects code: string",
"Validation fails or coerces incorrectly"
],
"expected": "Error code is number everywhere",
"actual": "Mixed types: number in types, string in Zod schemas"
},
"root_cause": "Inconsistent error code types. Backend uses int, frontend types use number, but Zod schemas use string.",
"fix_plan": {
"minimal_steps": [
"Update Zod schemas to use z.number() for error code",
"Verify all error parsing uses number type",
"Update apiErrorHandler to handle number codes",
"Test error handling"
],
"tests_to_add": [
"Test: Verify error codes parsed as numbers",
"Test: Error display works correctly"
],
"regression_risks": ["Code expecting string may break"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000010",
"severity": "P1",
"priority_rank": 10,
"title": "Remember Me Field Handling Inconsistency",
"component": {
"frontend": "apps/web/src/services/api/auth.ts, apps/web/src/features/auth/types/index.ts",
"backend": "veza-backend-api/internal/dto/login_request.go",
"infra": ""
},
"category": ["api-contract"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Field name inconsistency: remember_me vs rememberMe, may cause issues"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/api/auth.ts",
"lines": "L150",
"excerpt": "remember_me: data.remember_me || false"
},
{
"path": "apps/web/src/features/auth/types/index.ts",
"lines": "L11",
"excerpt": "remember_me?: boolean;"
},
{
"path": "veza-backend-api/internal/dto/login_request.go",
"lines": "L6",
"excerpt": "RememberMe bool `json:\"remember_me\"`"
}
],
"runtime": []
},
"repro": {
"steps": [
"Login form uses rememberMe (camelCase)",
"API call converts to remember_me (snake_case)",
"Backend expects remember_me",
"Works but inconsistent naming"
],
"expected": "Consistent field naming",
"actual": "Mixed: rememberMe in forms, remember_me in API"
},
"root_cause": "Frontend uses camelCase in forms but backend expects snake_case. Conversion happens but inconsistent.",
"fix_plan": {
"minimal_steps": [
"Standardize on snake_case for API (matches backend)",
"Update form types to use remember_me",
"Update all references",
"Test login with remember me"
],
"tests_to_add": [
"Test: Login with remember_me=true",
"Test: Token expiration matches remember_me setting"
],
"regression_risks": ["Forms using rememberMe may break"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000011",
"severity": "P1",
"priority_rank": 11,
"title": "Token Refresh Response Format Ambiguity",
"component": {
"frontend": "apps/web/src/services/tokenRefresh.ts",
"backend": "veza-backend-api/internal/handlers/auth.go",
"infra": ""
},
"category": ["api-contract", "error-handling"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": true,
"security_risk": "none",
"notes": "Multiple format checks in tokenRefresh suggest uncertainty about response structure"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/tokenRefresh.ts",
"lines": "L70-L84",
"excerpt": "// Format with wrapper { success: true, data: {...} }\nif (response.data?.data?.access_token) { ... }\n// Format direct { access_token, refresh_token, expires_in }\nelse if (response.data?.access_token) { ... }\n// Format with token nested { token: { access_token, refresh_token } }\nelse if (response.data?.token?.access_token) { ... }"
},
{
"path": "veza-backend-api/internal/handlers/auth.go",
"lines": "L197-L201",
"excerpt": "RespondSuccess(c, http.StatusOK, dto.TokenResponse{...}) // Returns { success: true, data: {...} }"
}
],
"runtime": []
},
"repro": {
"steps": [
"Token refresh called",
"Backend returns { success: true, data: { access_token, refresh_token, expires_in } }",
"Frontend checks 3 different formats",
"Works but fragile"
],
"expected": "Single, documented response format",
"actual": "Multiple format checks suggest uncertainty"
},
"root_cause": "Frontend handles multiple response formats to be defensive, but this indicates contract uncertainty.",
"fix_plan": {
"minimal_steps": [
"Document exact response format in FRONTEND_INTEGRATION.md",
"Simplify tokenRefresh to only handle correct format",
"Remove fallback format checks",
"Add test for refresh response parsing"
],
"tests_to_add": [
"Test: Verify refresh response parsed correctly",
"Test: Error handling for invalid format"
],
"regression_risks": ["If format changes, code will break"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000012",
"severity": "P1",
"priority_rank": 12,
"title": "No Retry Logic for Transient Errors (503, 502)",
"component": {
"frontend": "apps/web/src/services/api/client.ts",
"backend": "",
"infra": ""
},
"category": ["error-handling", "reliability"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "503/502 errors are handled but not retried, causing user-visible failures"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/api/client.ts",
"lines": "L201-L213",
"excerpt": "if (status === 503) { return Promise.reject(apiError); }\nif (status === 502) { return Promise.reject(apiError); } // No retry"
}
],
"runtime": []
},
"repro": {
"steps": [
"Backend returns 503 (Service Unavailable)",
"Frontend rejects immediately",
"User sees error",
"No automatic retry"
],
"expected": "Transient errors (503, 502) should be retried with exponential backoff",
"actual": "Errors rejected immediately, no retry"
},
"root_cause": "No retry logic for transient errors. 429 (rate limit) has retry but 503/502 do not.",
"fix_plan": {
"minimal_steps": [
"Add retry logic for 503/502 errors",
"Implement exponential backoff (max 3 retries)",
"Add retry-after header support",
"Update error messages to indicate retry"
],
"tests_to_add": [
"Test: Verify 503 retries with backoff",
"Test: Verify 502 retries with backoff",
"Test: Max retries respected"
],
"regression_risks": ["May cause excessive retries if not configured correctly"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000013",
"severity": "P1",
"priority_rank": 13,
"title": "Missing Error Correlation IDs in Frontend Error Boundaries",
"component": {
"frontend": "apps/web/src (error boundaries)",
"backend": "veza-backend-api/internal/response/response.go",
"infra": ""
},
"category": ["observability", "error-handling"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Backend returns request_id but frontend doesn't log it, making debugging difficult"
},
"evidence": {
"files": [
{
"path": "veza-backend-api/internal/response/response.go",
"lines": "L106",
"excerpt": "RequestID: c.GetString(\"request_id\")"
}
],
"runtime": []
},
"repro": {
"steps": [
"API error occurs",
"Backend returns { error: { request_id: \"req-123\", ... } }",
"Frontend logs error but doesn't include request_id",
"Debugging difficult"
],
"expected": "Frontend error logs include request_id for correlation",
"actual": "Request ID not logged in frontend"
},
"root_cause": "Backend provides request_id for error correlation but frontend doesn't extract or log it.",
"fix_plan": {
"minimal_steps": [
"Update apiErrorHandler to extract request_id",
"Include request_id in error logs",
"Update error boundaries to log request_id",
"Add request_id to user-facing error messages (optional)"
],
"tests_to_add": [
"Test: Verify request_id logged in errors",
"Test: Error correlation works"
],
"regression_risks": ["None"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000014",
"severity": "P1",
"priority_rank": 14,
"title": "CORS Allow-Credentials Always True (Security Risk if Origins Misconfigured)",
"component": {
"frontend": "",
"backend": "veza-backend-api/internal/middleware/cors.go",
"infra": ""
},
"category": ["cors", "security"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "medium",
"notes": "If CORS origins misconfigured, credentials flag enables cookie theft"
},
"evidence": {
"files": [
{
"path": "veza-backend-api/internal/middleware/cors.go",
"lines": "L21",
"excerpt": "c.Header(\"Access-Control-Allow-Credentials\", \"true\") // Always true"
}
],
"runtime": []
},
"repro": {
"steps": [
"CORS origins misconfigured (e.g., contains wildcard or wrong domain)",
"Malicious site makes credentialed request",
"Credentials flag allows cookies/tokens to be sent",
"Security risk"
],
"expected": "Credentials only allowed if origins correctly whitelisted",
"actual": "Credentials always true, relies on origins being correct"
},
"root_cause": "Credentials flag hardcoded to true. Relies on origins being correctly configured. If origins misconfigured, security risk.",
"fix_plan": {
"minimal_steps": [
"Add validation: credentials only if origins non-empty and no wildcard",
"Log warning if credentials=true but origins contain wildcard",
"Document security implications",
"Add test for credentials + wildcard combination"
],
"tests_to_add": [
"Test: Verify credentials rejected with wildcard",
"Test: Verify credentials allowed with specific origins"
],
"regression_risks": ["If origins misconfigured, requests may fail"]
},
"dependencies": ["INT-000001"],
"owner_suggestion": "backend"
},
{
"id": "INT-000015",
"severity": "P1",
"priority_rank": 15,
"title": "GetMe Endpoint Returns Incomplete User Data",
"component": {
"frontend": "apps/web/src/features/auth/api/authApi.ts",
"backend": "veza-backend-api/internal/handlers/auth.go",
"infra": ""
},
"category": ["api-contract"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "GET /auth/me returns only id, email, role but frontend expects full user object"
},
"evidence": {
"files": [
{
"path": "veza-backend-api/internal/handlers/auth.go",
"lines": "L369-L373",
"excerpt": "RespondSuccess(c, http.StatusOK, gin.H{\n \"id\": userID,\n \"email\": c.GetString(\"email\"),\n \"role\": c.GetString(\"role\")\n}) // Missing username, avatar, etc."
},
{
"path": "apps/web/src/features/auth/api/authApi.ts",
"lines": "L34-L36",
"excerpt": "getMe: async (): Promise<User> => { ... } // Expects full User object"
}
],
"runtime": []
},
"repro": {
"steps": [
"Call authApi.getMe()",
"Backend returns { id, email, role }",
"Frontend expects full User object with username, avatar, etc.",
"Type mismatch or missing fields"
],
"expected": "GET /auth/me returns full user object",
"actual": "Returns only id, email, role"
},
"root_cause": "GetMe handler returns minimal user data from context, not full user object from database.",
"fix_plan": {
"minimal_steps": [
"Update GetMe handler to fetch full user from database",
"Return complete UserResponse object",
"Update frontend types if needed",
"Test getMe returns all expected fields"
],
"tests_to_add": [
"Test: Verify getMe returns full user object",
"Test: Verify all User fields present"
],
"regression_risks": ["Frontend may break if response structure changes"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000016",
"severity": "P2",
"priority_rank": 16,
"title": "Field Name Mismatch: cover_art_path vs cover_art_url",
"component": {
"frontend": "apps/web/src/types/api.ts",
"backend": "veza-backend-api (track models)",
"infra": ""
},
"category": ["schema-drift"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Field name inconsistency may cause display issues"
},
"evidence": {
"files": [
{
"path": "apps/web/src/types/api.ts",
"lines": "L66",
"excerpt": "cover_art_path?: string; // Backend uses cover_art_path"
}
],
"runtime": []
},
"repro": {
"steps": [
"Backend returns cover_art_path",
"Frontend expects cover_art_path",
"Works but naming inconsistent with other _url fields"
],
"expected": "Consistent field naming",
"actual": "Mixed: _path vs _url"
},
"root_cause": "Inconsistent naming convention. Some fields use _url, others use _path.",
"fix_plan": {
"minimal_steps": [
"Document field naming convention",
"Standardize on _url for URLs, _path for file paths",
"Update types and backend if needed",
"Update documentation"
],
"tests_to_add": [
"Test: Verify cover art displays correctly",
"Test: Verify field names consistent"
],
"regression_risks": ["Changing field names may break existing code"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000017",
"severity": "P2",
"priority_rank": 17,
"title": "Pagination Format Inconsistencies",
"component": {
"frontend": "apps/web/src/types/api.ts",
"backend": "veza-backend-api (pagination responses)",
"infra": ""
},
"category": ["schema-drift"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Different pagination formats may cause parsing issues"
},
"evidence": {
"files": [
{
"path": "apps/web/src/types/api.ts",
"lines": "L222-L231",
"excerpt": "PaginationData: { page, limit, total, total_pages, has_next, has_prev, next_cursor?, prev_cursor? }"
}
],
"runtime": []
},
"repro": {
"steps": [
"Backend returns pagination in different format",
"Frontend expects specific format",
"Parsing may fail or be inconsistent"
],
"expected": "Consistent pagination format across all endpoints",
"actual": "May vary by endpoint"
},
"root_cause": "No standardized pagination format. Different endpoints may return different structures.",
"fix_plan": {
"minimal_steps": [
"Document standard pagination format",
"Audit all paginated endpoints",
"Standardize pagination responses",
"Update frontend types"
],
"tests_to_add": [
"Test: Verify pagination format consistent",
"Test: Verify pagination parsing works"
],
"regression_risks": ["Changing pagination format may break existing code"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000018",
"severity": "P2",
"priority_rank": 18,
"title": "Missing Validation Schema Alignment Between Frontend and Backend",
"component": {
"frontend": "apps/web/src/schemas/validation.ts",
"backend": "veza-backend-api/internal/validators",
"infra": ""
},
"category": ["schema-drift", "validation"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Frontend and backend validation rules may differ, causing inconsistent errors"
},
"evidence": {
"files": [
{
"path": "apps/web/src/schemas/validation.ts",
"lines": "L1-L359",
"excerpt": "Zod schemas for validation"
}
],
"runtime": []
},
"repro": {
"steps": [
"Frontend validates email format",
"Backend validates differently",
"Frontend passes, backend rejects (or vice versa)",
"Inconsistent user experience"
],
"expected": "Frontend and backend validation rules match",
"actual": "Validation rules may differ"
},
"root_cause": "No synchronization between frontend Zod schemas and backend validators. Rules may drift.",
"fix_plan": {
"minimal_steps": [
"Audit validation rules in both frontend and backend",
"Document validation rules in shared doc",
"Align frontend Zod schemas with backend validators",
"Add tests to verify alignment"
],
"tests_to_add": [
"Test: Verify validation rules match",
"Test: Verify error messages consistent"
],
"regression_risks": ["Changing validation may break existing forms"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000019",
"severity": "P2",
"priority_rank": 19,
"title": "No Request ID Propagation to Frontend Logs",
"component": {
"frontend": "apps/web/src/services/api/client.ts",
"backend": "veza-backend-api/internal/middleware/request_id.go",
"infra": ""
},
"category": ["observability"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Backend generates request_id but frontend doesn't log it, making debugging difficult"
},
"evidence": {
"files": [
{
"path": "veza-backend-api/internal/middleware/request_id.go",
"lines": "L1-L20",
"excerpt": "RequestID middleware generates X-Request-ID header"
}
],
"runtime": []
},
"repro": {
"steps": [
"API request made",
"Backend generates request_id",
"Frontend doesn't log request_id",
"Debugging difficult"
],
"expected": "Frontend logs include request_id for correlation",
"actual": "Request ID not logged in frontend"
},
"root_cause": "Backend generates request_id but frontend doesn't extract or log it in request/response interceptors.",
"fix_plan": {
"minimal_steps": [
"Update apiClient interceptor to extract X-Request-ID from response",
"Include request_id in request logs",
"Include request_id in error logs",
"Add request_id to error objects"
],
"tests_to_add": [
"Test: Verify request_id logged",
"Test: Verify request_id in error objects"
],
"regression_risks": ["None"]
},
"dependencies": ["INT-000013"],
"owner_suggestion": "frontend"
},
{
"id": "INT-000020",
"severity": "P2",
"priority_rank": 20,
"title": "Inconsistent Error Detail Formats",
"component": {
"frontend": "apps/web/src/utils/apiErrorHandler.ts",
"backend": "veza-backend-api/internal/errors/errors.go",
"infra": ""
},
"category": ["error-handling"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Different error detail formats may cause parsing issues"
},
"evidence": {
"files": [
{
"path": "apps/web/src/utils/apiErrorHandler.ts",
"lines": "L145-L154",
"excerpt": "normalizeApiError handles multiple error formats"
}
],
"runtime": []
},
"repro": {
"steps": [
"Backend returns error with details array",
"Frontend expects specific format",
"Parsing may fail or be inconsistent"
],
"expected": "Consistent error detail format",
"actual": "Multiple formats handled, suggests inconsistency"
},
"root_cause": "Backend may return errors in different formats, frontend handles multiple formats defensively.",
"fix_plan": {
"minimal_steps": [
"Standardize error detail format in backend",
"Simplify frontend error parsing",
"Remove fallback format handling",
"Update error documentation"
],
"tests_to_add": [
"Test: Verify error parsing works",
"Test: Verify error details displayed correctly"
],
"regression_risks": ["If format changes, parsing may break"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000021",
"severity": "P2",
"priority_rank": 21,
"title": "Missing Health Check Integration Tests",
"component": {
"frontend": "apps/web/e2e",
"backend": "veza-backend-api/internal/handlers/health.go",
"infra": ""
},
"category": ["testing", "observability"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "No E2E tests verify health check endpoints work"
},
"evidence": {
"files": [
{
"path": "apps/web/e2e/qa-audit.spec.ts",
"lines": "L80",
"excerpt": "const response = await page.request.get(`${API_URL}/health`); // Basic check but no assertion"
}
],
"runtime": []
},
"repro": {
"steps": [
"Health check endpoint called",
"No test verifies response format",
"If format changes, no test catches it"
],
"expected": "E2E tests verify health check endpoints",
"actual": "Basic check exists but no assertions"
},
"root_cause": "Health check endpoints exist but not properly tested in E2E suite.",
"fix_plan": {
"minimal_steps": [
"Add E2E test for GET /api/v1/health",
"Verify response format",
"Verify health status",
"Add tests for /healthz and /readyz"
],
"tests_to_add": [
"E2E test: Verify /health returns correct format",
"E2E test: Verify /healthz returns 200",
"E2E test: Verify /readyz returns 200"
],
"regression_risks": ["None"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000022",
"severity": "P2",
"priority_rank": 22,
"title": "No API Versioning Strategy",
"component": {
"frontend": "apps/web/src/services/api/client.ts",
"backend": "veza-backend-api/internal/api/router.go",
"infra": ""
},
"category": ["api-contract"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "All routes under /api/v1, no versioning strategy for future changes"
},
"evidence": {
"files": [
{
"path": "veza-backend-api/internal/api/router.go",
"lines": "L152",
"excerpt": "v1 := router.Group(\"/api/v1\") // Hardcoded v1"
}
],
"runtime": []
},
"repro": {
"steps": [
"API needs breaking change",
"No versioning strategy",
"Must break existing clients or maintain backward compatibility forever"
],
"expected": "Versioning strategy for API changes",
"actual": "All routes under /api/v1, no migration path"
},
"root_cause": "No API versioning strategy. All routes under /api/v1 with no plan for v2.",
"fix_plan": {
"minimal_steps": [
"Document API versioning strategy",
"Plan for v2 when needed",
"Add version negotiation if needed",
"Update documentation"
],
"tests_to_add": [
"Test: Verify versioning works",
"Test: Verify backward compatibility"
],
"regression_risks": ["None - planning only"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000023",
"severity": "P2",
"priority_rank": 23,
"title": "WebSocket Token in Query Params (Security Risk)",
"component": {
"frontend": "apps/web/src/services/api.ts",
"backend": "",
"infra": ""
},
"category": ["security", "websocket"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "medium",
"notes": "WebSocket tokens in URL query params exposed in logs, browser history"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/api.ts",
"lines": "L565",
"excerpt": "return `${WS_BASE_URL}?token=${token}`; // Token in query param"
}
],
"runtime": []
},
"repro": {
"steps": [
"WebSocket connection established",
"Token in URL: ws://example.com/ws?token=xxx",
"Token exposed in browser history, server logs",
"Security risk"
],
"expected": "Token in header or subprotocol, not query param",
"actual": "Token in query param"
},
"root_cause": "WebSocket authentication uses query param instead of header or subprotocol.",
"fix_plan": {
"minimal_steps": [
"Move token to WebSocket subprotocol or header",
"Update WebSocket client to use header",
"Update chat server to read from header",
"Remove token from query params"
],
"tests_to_add": [
"Test: Verify WebSocket auth works with header",
"Test: Verify token not in URL"
],
"regression_risks": ["WebSocket connections may break if not updated correctly"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000024",
"severity": "P3",
"priority_rank": 24,
"title": "Deprecated Routes Still Accessible",
"component": {
"frontend": "",
"backend": "veza-backend-api/internal/api/router.go",
"infra": ""
},
"category": ["api-contract", "maintainability"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Deprecated routes still work, causing confusion and maintenance burden"
},
"evidence": {
"files": [
{
"path": "veza-backend-api/internal/api/router.go",
"lines": "L596-L603",
"excerpt": "router.GET(\"/health\", deprecationMW, healthCheckHandler) // Deprecated but still works"
}
],
"runtime": []
},
"repro": {
"steps": [
"Call deprecated /health endpoint",
"Returns deprecation warning but still works",
"No removal plan"
],
"expected": "Deprecated routes removed or have removal timeline",
"actual": "Deprecated routes still accessible indefinitely"
},
"root_cause": "Deprecated routes marked with warning but not removed. No removal timeline.",
"fix_plan": {
"minimal_steps": [
"Document removal timeline for deprecated routes",
"Add removal date to deprecation warnings",
"Plan migration path",
"Remove deprecated routes after migration period"
],
"tests_to_add": [
"Test: Verify deprecated routes return warnings",
"Test: Verify new routes work"
],
"regression_risks": ["Removing routes may break existing clients"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000025",
"severity": "P3",
"priority_rank": 25,
"title": "Inconsistent Logging Formats",
"component": {
"frontend": "apps/web/src (logging)",
"backend": "veza-backend-api (structured logging)",
"infra": ""
},
"category": ["observability"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Frontend uses console.log, backend uses structured logging, making correlation difficult"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/api/client.ts",
"lines": "L62",
"excerpt": "console.error('[API] Failed to parse auth storage:', e); // Unstructured"
}
],
"runtime": []
},
"repro": {
"steps": [
"Frontend logs error with console.error",
"Backend logs with structured format",
"Log aggregation difficult",
"Correlation challenging"
],
"expected": "Structured logging in frontend for correlation",
"actual": "Mixed: console.log in frontend, structured in backend"
},
"root_cause": "Frontend uses console.log while backend uses structured logging. No correlation.",
"fix_plan": {
"minimal_steps": [
"Introduce structured logging library for frontend",
"Replace console.log with structured logger",
"Include request_id, user_id in logs",
"Update log aggregation config"
],
"tests_to_add": [
"Test: Verify logs structured",
"Test: Verify correlation works"
],
"regression_risks": ["Changing logging may break log aggregation"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000026",
"severity": "P3",
"priority_rank": 26,
"title": "Missing Observability Hooks in Frontend",
"component": {
"frontend": "apps/web/src (error boundaries, API calls)",
"backend": "",
"infra": ""
},
"category": ["observability"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "No error tracking (Sentry), no performance monitoring, making production debugging difficult"
},
"evidence": {
"files": [],
"runtime": []
},
"repro": {
"steps": [
"Error occurs in production",
"No error tracking",
"Debugging difficult",
"No visibility into issues"
],
"expected": "Error tracking and performance monitoring in frontend",
"actual": "No observability tools integrated"
},
"root_cause": "No error tracking or performance monitoring integrated in frontend.",
"fix_plan": {
"minimal_steps": [
"Integrate Sentry or similar error tracking",
"Add performance monitoring",
"Add error boundaries with tracking",
"Configure error reporting"
],
"tests_to_add": [
"Test: Verify errors tracked",
"Test: Verify performance monitored"
],
"regression_risks": ["None"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000027",
"severity": "P3",
"priority_rank": 27,
"title": "No Rate Limit Headers in Responses",
"component": {
"frontend": "apps/web/src/services/api/client.ts",
"backend": "veza-backend-api (rate limiting)",
"infra": ""
},
"category": ["observability", "api-contract"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Rate limit info not exposed, frontend can't show remaining requests"
},
"evidence": {
"files": [
{
"path": "apps/web/src/services/api/client.ts",
"lines": "L181-L199",
"excerpt": "Handles 429 but no rate limit headers checked"
}
],
"runtime": []
},
"repro": {
"steps": [
"Rate limit hit",
"Backend returns 429",
"Frontend doesn't know remaining requests",
"Can't show user-friendly message"
],
"expected": "Rate limit headers (X-RateLimit-*) in responses",
"actual": "No rate limit headers"
},
"root_cause": "Backend rate limiting exists but doesn't expose rate limit info in headers.",
"fix_plan": {
"minimal_steps": [
"Add X-RateLimit-* headers to rate limit middleware",
"Update frontend to read and display rate limit info",
"Update error messages to show remaining requests",
"Document rate limit headers"
],
"tests_to_add": [
"Test: Verify rate limit headers present",
"Test: Verify frontend displays rate limit info"
],
"regression_risks": ["None"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000028",
"severity": "P3",
"priority_rank": 28,
"title": "Missing API Documentation Updates",
"component": {
"frontend": "apps/web/FRONTEND_INTEGRATION.md",
"backend": "veza-backend-api/docs",
"infra": ""
},
"category": ["documentation"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "API documentation may be out of sync with implementation"
},
"evidence": {
"files": [
{
"path": "apps/web/FRONTEND_INTEGRATION.md",
"lines": "L1-L307",
"excerpt": "API documentation"
}
],
"runtime": []
},
"repro": {
"steps": [
"API endpoint changes",
"Documentation not updated",
"Developers use wrong format",
"Errors occur"
],
"expected": "API documentation always up to date",
"actual": "Documentation may be outdated"
},
"root_cause": "No automated process to keep API documentation in sync with implementation.",
"fix_plan": {
"minimal_steps": [
"Use OpenAPI/Swagger for API documentation",
"Generate docs from code",
"Update FRONTEND_INTEGRATION.md from OpenAPI spec",
"Add CI check for doc sync"
],
"tests_to_add": [
"Test: Verify docs match implementation",
"Test: Verify CI catches doc drift"
],
"regression_risks": ["None"]
},
"dependencies": [],
"owner_suggestion": "backend"
},
{
"id": "INT-000029",
"severity": "P3",
"priority_rank": 29,
"title": "No Vite Proxy Configuration for Development",
"component": {
"frontend": "apps/web/vite.config.ts",
"backend": "",
"infra": ""
},
"category": ["env", "build"],
"status": "open",
"impact": {
"user_visible": false,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "Direct CORS requests in dev, no proxy. Works but not ideal."
},
"evidence": {
"files": [
{
"path": "apps/web/vite.config.ts",
"lines": "L58-L92",
"excerpt": "server config but no proxy setup"
}
],
"runtime": []
},
"repro": {
"steps": [
"Frontend on localhost:3000",
"Backend on localhost:8080",
"CORS required",
"No proxy, direct requests"
],
"expected": "Vite proxy for /api to backend (optional but cleaner)",
"actual": "No proxy, direct CORS requests"
},
"root_cause": "No Vite proxy configuration. Relies on CORS for dev. Works but proxy would be cleaner.",
"fix_plan": {
"minimal_steps": [
"Add Vite proxy config for /api to http://localhost:8080",
"Update VITE_API_URL to use relative path /api/v1",
"Test proxy works",
"Update dev docs"
],
"tests_to_add": [
"Test: Verify proxy works",
"Test: Verify API calls work through proxy"
],
"regression_risks": ["Changing to proxy may break existing dev setup"]
},
"dependencies": [],
"owner_suggestion": "frontend"
},
{
"id": "INT-000030",
"severity": "P3",
"priority_rank": 30,
"title": "Missing HLS Endpoints (Frontend Calls But Backend Doesn't Implement)",
"component": {
"frontend": "apps/web/src/features/streaming/services/hlsService.ts",
"backend": "veza-backend-api/internal/api/router.go",
"infra": ""
},
"category": ["api-contract"],
"status": "open",
"impact": {
"user_visible": true,
"breaks_core_flow": false,
"security_risk": "none",
"notes": "HLS streaming features may not work"
},
"evidence": {
"files": [
{
"path": "apps/web/src/features/streaming/services/hlsService.ts",
"lines": "L85, L97",
"excerpt": "GET /api/v1/tracks/:id/hls/info, GET /api/v1/tracks/:id/hls/status"
}
],
"runtime": []
},
"repro": {
"steps": [
"Call hlsService.getHLSInfo()",
"Request: GET /api/v1/tracks/:id/hls/info",
"Backend returns 404",
"HLS features broken"
],
"expected": "HLS endpoints implemented or frontend calls removed",
"actual": "Frontend calls endpoints that don't exist"
},
"root_cause": "HLS streaming features in frontend but backend endpoints not implemented.",
"fix_plan": {
"minimal_steps": [
"Implement HLS endpoints in backend OR",
"Remove HLS service calls from frontend",
"Update documentation",
"Test streaming works"
],
"tests_to_add": [
"Test: Verify HLS endpoints work",
"Test: Verify streaming works end-to-end"
],
"regression_risks": ["Removing calls may break streaming features"]
},
"dependencies": [],
"owner_suggestion": "backend"
}
]
}