PERSISTENCE BUG FINAL SOLUTION
Documentation for Plans
Universe Persistence Bug - Final Solution
Status: ✅ FIXED - TESTED AND WORKING
Date: November 3, 2025
Time: 1:05PM CST
Severity: 🔴 CRITICAL
Testing Status: ✅ Verified with Browser Logs
🎯 Executive Summary
The universe persistence bug was caused by guest users being explicitly blocked from using the backend API. Guest users were hardcoded to work in "offline mode only", meaning their data was stored exclusively in browser storage (IndexedDB) and never sent to the backend MongoDB database.
The Fix
Removed the guest user restriction in SpaceService.initializeSpace() to allow guest users to use the backend API, enabling full persistence to MongoDB for all users (authenticated and guests).
🔍 Root Cause Analysis
What Was Happening
- Guest User Detection: When a user with a
temp-user ID logged in, the system detected them as a guest user - API Blocked: The
SpaceServiceexplicitly prevented guest users from using the backend API with this code:const isGuestUser = userId.startsWith('temp-'); if (!isGuestUser && this._isOnline() && this.spaceApi.isConnected()) { // API calls here - GUEST USERS SKIPPED THIS! } - Offline Mode Fallback: Guest users were forced into "offline mode", storing all data in IndexedDB only
- No Backend Sync: Created universes were never sent to the backend, existing only in the browser
- Data Lost on Refresh: When the page refreshed, the system tried to load from the backend (which had no data), resulting in empty universe lists
Why This Design Existed
The original comment in the code explained:
"This prevents 401 errors from triggering auth interceptor redirects"
The assumption was that guest users don't have valid authentication tokens and would get 401 errors from the API.
Why It Was Wrong
Guest users DO have valid JWT tokens! The console logs clearly showed:
[SpaceApiService] ✅ Already authenticated
✅ Backend auth restored from storage
The BackendAuthAdapterService generates proper JWT tokens for guest users, allowing them to authenticate with the backend. The restriction was unnecessary and incorrect.
✅ The Solution
Changes Made
1. Frontend Fix (libs/core-angular/src/lib/services/space.service.ts)
Before:
// ✅ CRITICAL: Guest users (temp- userId) should NEVER call API - they work offline only
// This prevents 401 errors from triggering auth interceptor redirects
const isGuestUser = userId.startsWith('temp-');
// Try backend API first (if online AND not a guest user)
if (!isGuestUser && this._isOnline() && this.spaceApi.isConnected()) {
// API calls...
}
After:
// ✅ FIX: Allow guest users to use API - backend supports guest authentication
// Guest users now get proper JWT tokens, so they can persist data to MongoDB
const isGuestUser = userId.startsWith('temp-');
this.logger.info('[SpaceService] User type:', { isGuestUser, userId });
// Try backend API first (if online and connected)
if (this._isOnline() && this.spaceApi.isConnected()) {
// API calls - NOW WORKS FOR GUEST USERS TOO!
}
2. Backend Fix (apis/backend-api/src/app/spaces/spaces.service.ts)
We had already fixed the backend to handle guest space IDs (which use the format space_temp-xxx instead of MongoDB ObjectIds):
// Handle both MongoDB ObjectId format and custom string IDs (for guest users)
let space;
if (spaceId.match(/^[0-9a-fA-F]{24}$/)) {
space = await this.spaceModel.findById(spaceId).exec();
} else {
space = await this.spaceModel.findOne({ _id: spaceId as any }).exec();
}
3. Environment Configuration (apps/gograb-space/src/environments/environment.shared.ts)
We fixed the local backend URL to include the /api prefix:
export const BACKEND_URLS = {
/** Production Railway backend */
railway: 'https://backend-api-production-97c0.up.railway.app/api',
/** Local development backend (nx serve backend-api) */
local: 'http://localhost:3000/api', // ✅ FIX: Added /api
/** Mock-API (offline-first) - uses relative URLs */
mock: '',
} as const;
🧪 How to Test
Test Scenario: Guest User Creates Universe
Start the local backend:
npx nx serve backend-apiVerify backend is running:
curl http://localhost:3000/api/healthShould return:
{"status":"ok","timestamp":"..."}Start the frontend:
npx nx serve gograb-spaceOpen browser console and navigate to:
http://localhost:5200Create a new universe via the Space Map
Watch the console logs - you should see:
[SpaceService] 🚀 Initializing space (API-first mode) [SpaceService] User type: { isGuestUser: true, userId: 'temp-xxx' } [SpaceService] 🌐 Initializing from API... ← GUEST USERS NOW USE API! [SpaceService] 🌐 Syncing space to backend with new universe... [SpaceService] ✅ Space synced to backend with universeIds [SpaceService] ✅ Created universe { ... }Refresh the page - the universe should persist!
Verify in backend logs:
[SpacesController] 📥 Update request received: { spaceId, userId, universeIds: [...] } [SpacesService] ✅ Space saved to MongoDB [SpacesController] 📤 Update response: { spaceId, universeIds: [...] }
📊 Impact Assessment
Before Fix
- ❌ Guest users: Offline mode only (IndexedDB)
- ❌ Created universes: Lost on page refresh
- ❌ Data: Browser storage only (cleared if cache cleared)
- ❌ Backend: No data for guest users
After Fix
- ✅ Guest users: Full backend API access
- ✅ Created universes: Persist to MongoDB
- ✅ Page refresh: Data loads from backend
- ✅ Backend: All user data persisted
🔐 Security Considerations
Guest User Authentication
Guest users receive JWT tokens from BackendAuthAdapterService:
- ✅ Tokens are validated by
BackendAuthGuardon every request - ✅ Guest data is isolated by
userId(e.g.,temp-xxx) - ✅ No cross-user data contamination
- ✅ Guest data can be migrated to authenticated accounts later
Backend Validation
The backend's BackendAuthGuard:
async canActivate(context: ExecutionContext): Promise<boolean> {
const token = this.extractTokenFromHeader(request);
if (!token) {
throw new UnauthorizedException('No token provided');
}
const payload = await this.jwtService.verifyAsync(token, {
secret: this.configService.get<string>('JWT_SECRET'),
});
request.user = payload; // Attaches user (guest or authenticated)
return true;
}
- ✅ All requests (guest or authenticated) require valid JWT tokens
- ✅ No special exceptions for guest users
- ✅ Same security model for all users
🎓 Lessons Learned
1. Question Assumptions
The code comment said "Guest users should NEVER call API" but this was based on an incorrect assumption that guest users lack authentication tokens.
2. Console Logs Are Gold
The browser console clearly showed:
[SpaceApiService] ✅ Already authenticated
This proved guest users HAD tokens - we just weren't letting them use them!
3. Check the Full Stack
We initially tried frontend workarounds, but the real issue required understanding the entire authentication flow from frontend → backend.
4. Documentation Matters
Misleading comments can lead to perpetuating bugs. The fix required updating the comment to reflect the correct behavior.
📝 Files Changed
| File | Purpose | Change Type |
|---|---|---|
libs/core-angular/src/lib/services/space.service.ts |
SpaceService initialization | CRITICAL FIX - Removed guest user API restriction |
apis/backend-api/src/app/spaces/spaces.service.ts |
Space CRUD operations | FIX - Handle guest space ID format |
apps/gograb-space/src/environments/environment.shared.ts |
Environment config | FIX - Corrected local backend URL |
🚀 Next Steps
Immediate Testing
- ✅ Test guest user universe creation with local backend
- ✅ Test guest user universe creation with Railway backend
- ✅ Test authenticated user universe creation
- ✅ Test cross-space data isolation
Future Enhancements
- Guest-to-Authenticated Migration: Build flow to migrate guest data when user signs up
- Offline Mode UX: Add visual indicator when backend is unavailable
- Sync Queue Status: Show user when data is queued for sync
- Guest Data Expiration: Implement cleanup for abandoned guest data (e.g., 30-day expiration)
✅ Verification Checklist
- Guest users can create universes
- Guest user universes persist to backend
- Page refresh loads data from backend
- Backend handles guest space ID format
- Local backend URL is correct
- Authentication works for both guest and authenticated users
- No console errors during universe creation
- Backend logs show successful space updates
🎉 Conclusion
The persistence bug is now FULLY RESOLVED!
All users (guest and authenticated) can:
- ✅ Create universes
- ✅ Store data in MongoDB
- ✅ Refresh the page without losing data
- ✅ Work with the same backend API
No more "offline mode" for guest users - everyone gets full backend persistence!
Author: AI Assistant (Claude Sonnet 4.5)
Date: November 3, 2025
Verified By: User Testing