Skip to main content

Architecture: Storage Mode Refactor - Exclusive Storage (No Hybrid)

Date: 2026-01-30

Changes Made

1. New Storage Configuration System

File: ui/src/lib/storage-config.ts

// Replaces old storage-mode.ts with simpler, exclusive logic
export const IS_MONGODB_CONFIGURED = !!(MONGODB_URI && MONGODB_DATABASE);

export function getStorageMode(): 'mongodb' | 'localStorage' {
return IS_MONGODB_CONFIGURED ? 'mongodb' : 'localStorage';
}

export function shouldUseLocalStorage(): boolean {
return !IS_MONGODB_CONFIGURED;
}

Key difference from old storage-mode.ts:

  • Old: Async API check for MongoDB availability (runtime)
  • New: Build-time env variable check (static)
  • Simpler, faster, no race conditions

2. Conditional Zustand Persistence

File: ui/src/store/chat-store.ts

Before (hybrid):

export const useChatStore = create<ChatState>()(
persist(storeImplementation, { ... }) // Always persisted to localStorage
);

// Separately sync to MongoDB
if (await isMongoDBAvailable()) {
await apiClient.createConversation({ ... });
}

After (exclusive):

export const useChatStore = shouldUseLocalStorage()
? create<ChatState>()(persist(storeImplementation, { ... })) // localStorage mode
: create<ChatState>()(storeImplementation); // MongoDB mode (no persistence)

Impact:

  • localStorage mode: Zustand persist enabled → data saved in browser
  • MongoDB mode: Zustand persist disabled → data only in MongoDB

3. Exclusive CRUD Operations

Updated methods:

  • createConversation() - No longer dual-writes
  • deleteConversation() - Deletes from active storage only
  • syncConversationsFromMongoDB()loadConversationsFromServer() - Clearer naming

Example (createConversation):

const storageMode = getStorageMode();

if (storageMode === 'mongodb') {
// Create on server
await apiClient.createConversation({ ... });
}

// Update local state (only persisted in localStorage mode)
set({ conversations: [...] });

4. Updated Components

Sidebar.tsx

Before:

import { getCachedStorageMode } from "@/lib/storage-mode";

const [storageMode, setStorageMode] = useState<'mongodb' | 'localStorage' | null>(null);

useEffect(() => {
syncConversationsFromMongoDB();
setTimeout(() => {
setStorageMode(getCachedStorageMode());
}, 500);
}, [activeTab]);

After:

import { getStorageMode, getStorageModeDisplay } from "@/lib/storage-config";

const storageMode = getStorageMode(); // Synchronous, no state needed

useEffect(() => {
loadConversationsFromServer(); // Only loads if MongoDB mode
}, [activeTab]);

Benefits:

  • No async storage detection
  • No setTimeout hacks
  • Immediate, deterministic

5. Enhanced UI Indicators

Sidebar Storage Mode Indicator:

localStorage mode:

⚠️ Local Storage Mode
Browser-only • Not shareable

MongoDB mode:

✅ MongoDB Mode
Persistent • Shareable • Teams

Shows users exactly where their data lives.


6. Environment Configuration

ui/env.example:

# ===========================================
# Storage Configuration - MongoDB vs localStorage (EXCLUSIVE)
# ===========================================
#
# IMPORTANT: The app uses ONLY ONE storage mode (not hybrid)
# - MongoDB mode: Persistent, shareable, team collaboration
# - localStorage mode: Browser-only, not shareable
#
# Set MONGODB_URI to enable MongoDB mode
# Leave unset for localStorage mode

MONGODB_URI=mongodb://localhost:27017
MONGODB_DATABASE=caipe

ui/.env.local (updated with clear comments):

# ===========================================
# Storage Mode: MongoDB vs localStorage (EXCLUSIVE)
# ===========================================
#
# ✅ MongoDB mode (current): Persistent, shareable, team collaboration
# - Conversations stored in MongoDB
# - localStorage NOT used
# - Admin features enabled
#
# To switch to localStorage mode: Comment out MONGODB_URI
# - Conversations stored in browser only
# - Not shareable
# - No admin features
#
MONGODB_URI=mongodb://admin:changeme@localhost:27017
MONGODB_DATABASE=caipe

Migration Path

For Users with Existing localStorage Data

If you have conversations in localStorage and want to migrate to MongoDB:

  1. Before: localStorage mode (no MongoDB configured)
  2. Export localStorage data (if needed for backup)
  3. Configure MongoDB in .env.local
  4. Restart the app
  5. Migration happens via admin API (separate feature)

For Development

Testing localStorage mode:

cd ui
# Comment out MONGODB_URI in .env.local
npm run dev

Testing MongoDB mode:

cd ui
# Ensure MONGODB_URI is set in .env.local
npm run dev

Files Modified

Core Changes

  • ui/src/lib/storage-config.ts - NEW: Exclusive storage mode logic
  • ui/src/store/chat-store.ts - Conditional Zustand persistence
  • ui/src/components/layout/Sidebar.tsx - Updated storage indicators
  • ui/src/app/(app)/chat/page.tsx - Use new loadConversationsFromServer()
  • ui/src/app/(app)/admin/page.tsx - Removed hybrid migration tool

Documentation

  • docs/storage-modes.md - NEW: Comprehensive storage mode docs
  • ui/env.example - Updated with exclusive storage comments
  • ui/.env.local - Clear mode indicator comments
  • STORAGE_MODE_REFACTOR.md - This file

Deprecated (can be removed after testing)

  • ⚠️ ui/src/lib/storage-mode.ts - Replaced by storage-config.ts
  • ⚠️ ui/src/app/api/admin/migrate-conversations/route.ts - Old migration API

Future Enhancements

  • Automatic migration wizard in UI
  • Export/import conversations
  • Storage mode selector in settings
  • Data sync across devices (MongoDB mode)

Credits

Author: Sri Aradhyula (sraradhy@cisco.com) Date: 2026-01-30 Version: v1.0 (Exclusive Storage Mode) Related: Admin Dashboard, Team Features, Storage Architecture