Skip to content

Offline-First Conversation Persistence

AICO's conversation persistence strategy ensures instant message loading and seamless offline functionality through local-first storage with intelligent background synchronization.

Problem Statement

Currently, the frontend starts with a blank slate on each launch, requiring users to wait for backend message retrieval. This creates a poor UX compared to modern messaging apps (WhatsApp, Telegram) which display cached conversations instantly.

Architecture Principles

1. Cache-First Loading

  • Instant Display: Messages load from local cache in <200ms
  • Background Sync: Fresh data fetched asynchronously without blocking UI
  • Optimistic Updates: Sent messages appear immediately, sync in background

2. Offline-First Design

  • Full Read Access: View all cached conversations without network
  • Graceful Degradation: Clear offline indicators, no error states
  • Automatic Recovery: Seamless sync when connection restored

3. Smart Synchronization

  • Lazy Sync: Only sync active conversations
  • Conflict Resolution: Backend as source of truth (last-write-wins)
  • Efficient Updates: Delta sync for changed messages only

Implementation Overview

Frontend (Flutter)

Local Storage: Drift ORM with SQLite - conversations table: Conversation metadata - messages table: Full message history - sync_metadata table: Sync state tracking - Retention: 90 days (configurable)

Repository Pattern: Enhanced MessageRepositoryImpl

getMessages(conversationId) {
  1. Load from cache  instant display
  2. Return cached data immediately
  3. Sync with backend in background
  4. Update cache with fresh data
}

Startup Flow: 1. ConversationProvider.build() → Load last active conversation from cache 2. Display messages instantly (<200ms) 3. Background sync updates cache 4. UI reflects changes smoothly

Backend (Python)

Message Retrieval: Enhanced GET /conversation/messages - Query working memory (LMDB) for recent messages (24h retention) - Fallback to semantic memory for older messages - Pagination support: page, page_size, before_message_id - Returns: messages[], total_count, has_more

Storage Architecture: - Working Memory (LMDB): Fast access to recent conversations (24h TTL) - Semantic Memory (ChromaDB): Long-term storage with vector search - Automatic Cleanup: Messages older than retention period

Performance Targets

Metric Target Rationale
Time to first message <200ms Industry standard: <500ms
Cache hit rate >95% Most sessions load cached data
Sync latency <2s Non-blocking background operation
Storage per conversation ~50KB Efficient for 90-day retention

UX Enhancements

Loading States

  • Skeleton Screen: Show message placeholders during initial load (not blank screen)
  • Sync Indicator: Subtle icon when syncing in background
  • Offline Banner: Clear "Viewing cached messages" when offline

Smooth Transitions

  • Scroll Position: Restore last scroll position on app restart
  • Draft Persistence: Save unsent messages across sessions
  • Unread Counts: Maintain locally, sync with backend

Migration Strategy

Existing Users: 1. First launch: One-time sync from backend → "Loading conversation history..." 2. Subsequent launches: Instant display from cache 3. No data loss: All messages preserved in backend (24h working memory + long-term semantic memory)

Rollout Phases: 1. Backend API completion (Week 1) 2. Frontend local storage + basic sync (Week 2) 3. UX polish + performance optimization (Week 3) 4. Beta testing + monitoring (Week 4) 5. Production release (Week 5)

Technical Decisions

Decision Rationale
Drift ORM Type-safe, performant, excellent Flutter integration
90-day retention Balances storage vs. utility (industry standard)
Cache-first strategy Instant UX, matches WhatsApp/Telegram patterns
Background sync Non-blocking, preserves responsiveness
Optimistic UI Feels instant, handles failures gracefully

Risk Mitigation

  • Database migration failures → Comprehensive testing + rollback plan
  • Sync conflicts → Last-write-wins with backend as source of truth
  • Storage bloat → Automatic cleanup of messages >90 days
  • Performance degradation → Pagination + lazy loading for large conversations
  • Network failures → Retry logic with exponential backoff

Architectural Impact Analysis

Frontend Impact

New Components: - Local database layer (Drift ORM + SQLite) - Local data source (ConversationLocalDataSource) - Enhanced repository with cache-first logic - Database migration system

Modified Components: - MessageRepositoryImpl: Add cache-first loading + background sync - ConversationNotifier: Add startup conversation loading - HomeScreen: Add skeleton loading states

Architecture Alignment: ✅ Fully Aligned - Maintains thin client design - local DB is just a cache layer - No heavy processing added to frontend - Follows existing repository pattern - Preserves message-driven architecture (backend still source of truth)

Impact Level: Medium - Adds persistence layer but doesn't change core architecture

Backend Impact

New Components: - Message retrieval logic in working memory store - Pagination support in API endpoint

Modified Components: - GET /conversation/messages: Complete TODO implementation - WorkingMemoryStore: Add get_messages() method with pagination - Conversation router: Return actual message data instead of empty list

Architecture Alignment: ✅ Fully Aligned - Leverages existing working memory (LMDB) + semantic memory (ChromaDB) - Follows existing API Gateway pattern - Maintains message bus architecture - No changes to core conversation engine

Impact Level: Low - Completes existing API, minimal new code

Security & Privacy Alignment

Principle Compliance:

Principle Implementation Status
Local-First Processing All data cached locally, backend optional after initial sync ✅ Aligned
Privacy by Design No new data collection, just local caching of existing messages ✅ Aligned
Zero-Effort Security Automatic encryption via Drift + SQLite, transparent to user ✅ Aligned
Data Encryption SQLite database encrypted using platform secure storage ✅ Aligned
User Control User owns all cached data, can clear cache anytime ✅ Aligned

Security Considerations: - Frontend local DB must use encrypted storage (Flutter secure storage + Drift encryption) - Cache retention policy (90 days) respects privacy by not hoarding old data - Sync only when user authenticated - no anonymous data leakage - Backend already encrypts working memory (LMDB) - no changes needed

Security Impact: None - Maintains existing security posture, adds frontend encryption

Developer Guidelines Alignment

Principle Compliance:

Guideline Implementation Status
Simplicity First Cache-first pattern is straightforward, well-understood ✅ Aligned
DRY Repository pattern reused, single source of truth (backend) ✅ Aligned
KISS No overengineering - standard SQLite + background sync ✅ Aligned
Modularity Clean separation: local datasource, repository, provider ✅ Aligned
Resource Awareness 90-day retention + auto-cleanup prevents storage bloat ✅ Aligned
Privacy & Security Local-first, encrypted, user-controlled ✅ Aligned

Risk Assessment

Risk Probability Impact Mitigation
Database migration failures Medium High Comprehensive testing, rollback plan, beta testing
Cache-backend sync conflicts Low Medium Last-write-wins, backend authoritative
Performance degradation Low Medium Pagination, lazy loading, performance testing
Storage bloat Low Low 90-day auto-cleanup, configurable retention
Security vulnerabilities Low High Encrypted storage, security audit, penetration testing

References

  • Industry patterns: WhatsApp, Telegram, Signal (offline-first messaging)
  • Flutter best practices: Drift ORM, Riverpod state management
  • Backend storage: LMDB (working memory), ChromaDB (semantic memory)
  • AICO Architecture: Architecture Overview
  • AICO Security: Security Overview
  • Developer Guidelines: Guidelines