Local-First Architecture
AFFiNE is built on local-first principles, ensuring your data is always available, even without internet connectivity. Your workspace lives primarily on your device, with cloud sync as an enhancement—not a requirement.What is Local-First?
Local-first software prioritizes:Offline Work
Full functionality without internet connection
Speed
Instant response—no server round-trips for reads
Ownership
Your data lives on your device first
Privacy
No forced cloud storage or tracking
Sync
Cloud backup and multi-device sync when online
Reliability
Works even if servers go down
Architecture Overview
AFFiNE’s local-first stack:Core Technologies
Y.js (Yjs) CRDT
Conflict-free Replicated Data Type—the foundation of AFFiNE’s sync:How Y.js Works
How Y.js Works
CRDT Properties:
- Commutative: Order of operations doesn’t matter
- Associative: Grouping of operations doesn’t matter
- Idempotent: Applying same operation twice = applying once
- No merge conflicts ever
- Offline editing with guaranteed sync
- Peer-to-peer sync (no server required)
- Time-travel and undo/redo
- Efficient binary encoding
Y.Doc- Document containerY.Text- Collaborative textY.Array- Ordered listY.Map- Key-value storeY.XmlFragment- Rich text with formatting
IndexedDB Storage
Browser-based local database:- Persistent: Data survives browser restarts
- Large capacity: Gigabytes of storage
- Async API: Non-blocking operations
- Indexed queries: Fast lookups
- Transactional: ACID guarantees
- Documents
- Workspace
- Assets
- Cache
- All page content (Y.js updates)
- Document metadata (title, created date)
- Edit history (for undo/redo)
- Unsent updates (when offline)
WebSocket Sync Protocol
Real-time bidirectional communication:- sync-025: Legacy single-update broadcasts
- sync-026: Optimized batch updates (current)
The server automatically detects client version (≥0.25.0) and uses the appropriate sync protocol.
Sync Flow
Initial Load
When opening a workspace:Online Editing
When making changes while online:Offline Editing
When internet disconnects:Conflict Resolution
How Conflicts Are Prevented
Y.js uses operation-based CRDTs to eliminate conflicts:Conflict Resolution Examples
Conflict Resolution Examples
Scenario 1: Concurrent Text EditsInitial:
"Hello World"- User A (offline): Inserts “Beautiful ” at position 6
→
"Hello Beautiful World" - User B (offline): Inserts “Amazing ” at position 6
→
"Hello Amazing World"
- Y.js merges based on operation metadata (user ID, timestamp)
- Result:
"Hello Beautiful Amazing World"(deterministic order) - No “conflict markers” or manual resolution needed
- User A: Deletes paragraph
- User B: Edits same paragraph
- Y.js preserves deletion
- Edit operations marked as applying to deleted block
- Paragraph stays deleted (deletion takes precedence)
- Edit history preserved for potential recovery
- User A: Changes color to “red”
- User B: Changes color to “blue”
- Last-write-wins based on timestamp
- Both updates have timestamps
- Later timestamp wins
- Deterministic across all clients
State Vectors
Efficient sync using state vectors:- Minimal bandwidth (only send what’s needed)
- Fast catch-up for offline clients
- No full document re-download
Storage Architecture
Client-Side (IndexedDB)
- Old updates merged into snapshots
- Deleted docs removed after grace period
- Unused blobs cleaned up
- Cache expiration policies
Server-Side (PostgreSQL)
- Periodic merging of updates into snapshots
- Reduces query time for initial loads
- Old updates archived or deleted
- Configurable retention policies
Snapshot Creation Process (storage/doc.ts)
Snapshot Creation Process (storage/doc.ts)
- Fast initial page loads
- Reduced database size
- History preservation
- Better query performance
Multi-Device Sync
Work seamlessly across devices:Device A → Device B
- Eventual consistency: All devices converge to same state
- Causal ordering: Edits appear in logical order
- No data loss: All updates preserved and merged
Cross-Device Scenarios
- Online Sync
- Offline → Online
- Both Offline
Both devices online:
- Changes sync in real-time (50-200ms latency)
- Live cursor positions visible
- Instant collaborative editing
Performance Optimizations
Client-Side
Lazy Loading
Only load visible documents, not entire workspace
Virtual Scrolling
Render only visible blocks in long documents
Debounced Sync
Batch rapid edits before sending to server
Binary Protocol
Y.js uses efficient binary encoding (not JSON)
Incremental Updates
Only sync changes, not full document
Service Workers
Background sync when app is closed
Server-Side
- Update compression: Merge multiple updates before broadcast
- Connection pooling: Reuse database connections
- Redis caching: Hot documents cached in memory
- Horizontal scaling: Multiple servers share load via Redis
- Rate limiting: Prevent abuse and overload
Data Export and Portability
Your data is never locked in:Export Options
- Markdown: Plain text export
- HTML: Web-ready format
- PDF: Print-ready documents
- JSON: Structured data export
- Y.js binary: Full-fidelity backup
Self-Hosting
Run your own AFFiNE server:- Full control over data
- Custom backup strategies
- On-premise deployment
- Privacy compliance (GDPR, HIPAA)
Privacy and Security
Local-First = Privacy-First
Client-Side Encryption
(Coming Soon) End-to-end encryption for cloud sync
Zero-Knowledge
Server can’t read your content (with E2EE enabled)
Offline Work
No telemetry or tracking when offline
Self-Hosted
Keep all data on your infrastructure
Data Location
AFFiNE Cloud:- Data stored in your chosen region
- GDPR-compliant EU servers available
- SOC 2 Type II certified
- You control data location
- No data leaves your network
- Audit logs for compliance
Troubleshooting
Sync Issues
“Not syncing” indicator:- Check internet connection
- Verify WebSocket connection (browser dev tools)
- Check server status
- Clear IndexedDB and re-sync (last resort)
- Large document may take time to load
- Check network speed
- Disable browser extensions
- Try different network (VPN issues)
Storage Issues
“Quota exceeded” error:- Browser storage limit reached (~1-2GB on mobile)
- Delete unused workspaces
- Clear cache in settings
- Use desktop app (no quota limits)
- Rare but possible with browser crashes
- Server has backup (cloud users)
- Export and re-import workspace
- Contact support for recovery
Best Practices
Optimizing Local-First Workflow
Optimizing Local-First Workflow
- Regular sync: Connect to internet periodically to sync changes
- Multiple devices: Ensure each device syncs before switching
- Backup: Export important workspaces regularly
- Storage management: Clean up unused documents
- Update app: Keep AFFiNE updated for latest sync improvements
- Monitor sync: Watch for “Synced” indicator before closing
Future Enhancements
Peer-to-Peer Sync
Direct device-to-device sync without server
E2E Encryption
Zero-knowledge cloud sync
Selective Sync
Choose which docs to sync locally
Compression
Smaller storage footprint