This document describes the implementation of per-account thread isolation and comprehensive user session management in VT. The solution ensures that threads are isolated per user account while maintaining the local-only storage approach using IndexedDB.
The original requirements were:
- Gated Access Management: Ensure all subscription/feature access is cleared on user logout
- Sidebar Button: Show appropriate subscription management options based on user status
- Thread Isolation: Implement per-account thread management with the current local-only (Dexie/IndexedDB) implementation
Implementation: Enhanced the existing logout flow in packages/common/hooks/use-logout.ts
Key Changes:
- Added
clearAllThreads()call to logout process - Maintains existing gated feature clearing (API keys, MCP config, subscription cache)
- Ensures complete session cleanup on logout
Code Location: packages/common/hooks/use-logout.ts
Status: Already correctly implemented
Implementation: Sidebar logic in packages/common/components/side-bar.tsx correctly shows:
- "Manage Subscription" for VT+ subscribers
- "Upgrade to Plus" for non-subscribers
Code Location: packages/common/components/side-bar.tsx
Implementation: User-specific database namespacing with automatic switching
Approach: Modified the IndexedDB database naming strategy to include user identification:
// Database naming pattern:
// Authenticated users: "threads-{userId}"
// Anonymous users: "threads-anonymous"
const getDatabaseName = (userId: string | null): string => {
return userId ? `threads-${userId}` : 'threads-anonymous';
};Code Location: packages/common/store/chat.store.ts
Implementation: Created authentication-aware hook that monitors user state changes and switches thread databases automatically.
Key Components:
-
Thread Authentication Hook (
packages/common/hooks/use-thread-auth.ts):- Monitors user session changes
- Triggers database switches when user login/logout occurs
- Provides logging for debugging
-
Database Management Functions (
packages/common/store/chat.store.ts):initializeUserDatabase(userId): Sets up user-specific databaseswitchUserDatabase(userId): Switches active database and loads user's threads- Graceful error handling and fallback states
-
Global Integration (
packages/common/context/root.tsx):- Integrated
useThreadAuth()hook into root provider - Ensures database switching runs globally across the application
- Integrated
// Core database switching logic
switchUserDatabase: (async (userId: string | null) => {
try {
console.log(`[ThreadDB] Switching to database for user: ${userId || 'anonymous'}`);
// Initialize the new user-specific database
initializeUserDatabase(userId);
// Load data from the new database
const newData = await loadInitialData();
// Update the store with data from the new user's database
set({
threads: newData.threads,
threadItems: [],
currentThreadId: newData.currentThreadId,
// ... other state updates
});
console.log(
`[ThreadDB] Successfully switched to user database with ${newData.threads.length} threads`,
);
} catch (error) {
console.error('[ThreadDB] Error switching user database:', error);
// Fallback to clean state on error
}
});// Hook that monitors authentication changes
export const useThreadAuth = () => {
const { data: session } = useSession();
const switchUserDatabase = useChatStore(state => state.switchUserDatabase);
const previousUserIdRef = useRef<string | null>(null);
useEffect(() => {
const currentUserId = session?.user?.id || null;
const previousUserId = previousUserIdRef.current;
// Only switch database if user actually changed
if (currentUserId !== previousUserId) {
switchUserDatabase(currentUserId);
previousUserIdRef.current = currentUserId;
}
}, [session?.user?.id, switchUserDatabase]);
};- Anonymous User: Uses
threads-anonymousdatabase - User Login: Automatically switches to
threads-{userId}database - User Logout: Clears all threads and switches back to
threads-anonymous - User Switch: Seamlessly switches between user-specific databases
- Complete Isolation: Each user sees only their own threads
- No Cross-Contamination: No access to other users' thread data
- Logout Security: All thread data cleared on logout
- Anonymous Support: Anonymous users get their own isolated space
- Device-Specific: Threads remain local to the device/browser
- No Cross-Device Sync: User's threads are not synced across devices
- Local Storage Dependency: Relies on IndexedDB availability
-
packages/common/hooks/use-logout.ts- Added
clearAllThreads()call to logout flow
- Added
-
packages/common/store/chat.store.ts- Implemented user-specific database namespacing
- Added
switchUserDatabase()andinitializeUserDatabase()functions - Modified database initialization logic
-
packages/common/hooks/use-thread-auth.ts(New)- Created authentication monitoring hook
- Handles automatic database switching
-
packages/common/hooks/index.ts- Exported new
useThreadAuthhook
- Exported new
-
packages/common/context/root.tsx- Integrated
useThreadAuth()hook globally
- Integrated
- ✅ Build Test: Successful compilation with no TypeScript errors
- ✅ Type Safety: All types properly maintained
- ✅ Integration: Hook properly integrated into global context
- ✅ Error Handling: Graceful fallbacks implemented
- ✅ Logging: Debug logging added for troubleshooting
- Privacy: Complete thread isolation between user accounts
- Security: Thread data cleared on logout
- Performance: Local-only storage remains fast
- Seamless UX: Automatic switching without user intervention
- Robust: Graceful error handling and fallback states
- Maintainable: Clean separation of concerns
- Cross-Device Sync: Could implement server-side thread storage for sync
- Migration Path: Current approach allows easy migration to server-side storage
- Backup/Export: Could add thread export functionality for user data portability
- Analytics: Database switching events provide insights into user behavior
The implemented solution successfully addresses all requirements:
- ✅ Gated access cleared on logout
- ✅ Sidebar button shows correct subscription options
- ✅ Per-account thread isolation implemented with local storage
The approach maintains the performance benefits of local-only storage while providing complete user isolation and a seamless authentication experience.