docs/sync-and-op-log/diagrams/07-supersync-vs-file-based.md
Last Updated: January 2026 Status: Implemented
This document compares the two sync provider architectures: SuperSync (server-based) and File-Based (WebDAV/Dropbox/LocalFile).
graph TB
subgraph Title[" "]
direction LR
T1["<b>SUPERSYNC</b>
Server-Based"]
T2["<b>FILE-BASED</b>
File-Based"]
end
subgraph SS["SuperSync Architecture"]
direction TB
SS_Client["CLIENT"]
SS_Upload["Upload: POST /ops
━━━━━━━━━━━━━━━
Send ops array
Server assigns seq"]
SS_Download["Download: GET /ops
━━━━━━━━━━━━━━━
Query since lastSeq
Returns only new ops"]
SS_Server["SERVER
━━━━━━━━━━━━━━━
Validates sequence
Detects gaps
Returns 409 on conflict"]
SS_DB[("PostgreSQL
━━━━━━━━━━━━━━━
operations table
All ops forever
Server-assigned seq")]
SS_Client --> SS_Upload
SS_Client --> SS_Download
SS_Upload --> SS_Server
SS_Download --> SS_Server
SS_Server --> SS_DB
end
subgraph FB["File-Based Architecture"]
direction TB
FB_Client["CLIENT"]
FB_Upload["Upload: uploadFile()
━━━━━━━━━━━━━━━
Download first
Merge + increment ver
Upload entire file"]
FB_Download["Download: downloadFile()
━━━━━━━━━━━━━━━
Get entire file
Filter ops locally
Detect version changes"]
FB_Provider["FILE PROVIDER
━━━━━━━━━━━━━━━
WebDAV/Dropbox/Local
Simple file operations
No server logic"]
FB_File[("sync-data.json
━━━━━━━━━━━━━━━
Full state snapshot
Last 200 ops
Client-managed ver")]
FB_Client --> FB_Upload
FB_Client --> FB_Download
FB_Upload --> FB_Provider
FB_Download --> FB_Provider
FB_Provider --> FB_File
end
style SS fill:#e3f2fd,stroke:#1565c0,stroke-width:3px
style FB fill:#fff3e0,stroke:#e65100,stroke-width:3px
style SS_DB fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
style FB_File fill:#ffe0b2,stroke:#e65100,stroke-width:2px
style Title fill:none,stroke:none
graph LR
subgraph Concept["KEY DIFFERENCE"]
direction TB
C1["Where is the
<b>source of truth</b>?"]
C2["Who manages
<b>sequence numbers</b>?"]
C3["How are
<b>conflicts detected</b>?"]
C4["What gets
<b>transferred</b>?"]
C5["How do
<b>late joiners</b> sync?"]
end
subgraph SSAnswer["SuperSync"]
direction TB
A1["Server's PostgreSQL
database"]
A2["Server assigns
serverSeq on insert"]
A3["Server returns 409
with missing ops"]
A4["Only the ops
that changed"]
A5["Replay all ops
from server"]
end
subgraph FBAnswer["File-Based"]
direction TB
B1["The sync file
(sync-data.json)"]
B2["Client increments
syncVersion locally"]
B3["Client detects
version mismatch"]
B4["Entire file
(state + ops)"]
B5["Get state snapshot
from file"]
end
C1 --> A1
C1 --> B1
C2 --> A2
C2 --> B2
C3 --> A3
C3 --> B3
C4 --> A4
C4 --> B4
C5 --> A5
C5 --> B5
style Concept fill:#f5f5f5,stroke:#333,stroke-width:2px
style SSAnswer fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style FBAnswer fill:#fff3e0,stroke:#e65100,stroke-width:2px
| Aspect | SuperSync | File-Based | Winner |
|---|---|---|---|
| Bandwidth Efficiency | Only transfers changed ops | Transfers entire file each sync | SuperSync |
| Setup Complexity | Requires account + server | Use existing cloud storage | File-Based |
| Offline Duration | Unlimited (server stores all) | Limited (only 200 ops retained) | SuperSync |
| Self-Hosting | Need to run server | Just need file storage | File-Based |
| Late Joiner Speed | Slow (replay all ops) | Fast (load snapshot) | File-Based |
| Conflict Handling | Server-authoritative | Client-side piggybacking | Tie |
| Real-time Sync | Yes (polling/webhooks) | No (periodic sync) | SuperSync |
| Data Recovery | Full op history available | Limited to snapshot + 200 ops | SuperSync |
graph TB
subgraph Tradeoffs["TRADE-OFFS AT A GLANCE"]
direction TB
subgraph Bandwidth["Bandwidth"]
SS_BW["SuperSync: ✅ LOW
Only delta ops transferred"]
FB_BW["File-Based: ⚠️ HIGH
Full file each time"]
end
subgraph Setup["Setup Effort"]
SS_Setup["SuperSync: ⚠️ HIGH
Account + server needed"]
FB_Setup["File-Based: ✅ LOW
Use existing storage"]
end
subgraph History["Operation History"]
SS_Hist["SuperSync: ✅ FULL
All ops stored forever"]
FB_Hist["File-Based: ⚠️ LIMITED
Only last 200 ops"]
end
subgraph LateJoin["Late Joiner Experience"]
SS_Late["SuperSync: ⚠️ SLOW
Must replay all ops"]
FB_Late["File-Based: ✅ FAST
Just load snapshot"]
end
subgraph Complexity["Client Complexity"]
SS_Comp["SuperSync: ✅ SIMPLE
Server handles sequences"]
FB_Comp["File-Based: ⚠️ COMPLEX
Client manages versions"]
end
end
style SS_BW fill:#c8e6c9,stroke:#2e7d32
style FB_BW fill:#ffecb3,stroke:#ffa000
style SS_Setup fill:#ffecb3,stroke:#ffa000
style FB_Setup fill:#c8e6c9,stroke:#2e7d32
style SS_Hist fill:#c8e6c9,stroke:#2e7d32
style FB_Hist fill:#ffecb3,stroke:#ffa000
style SS_Late fill:#ffecb3,stroke:#ffa000
style FB_Late fill:#c8e6c9,stroke:#2e7d32
style SS_Comp fill:#c8e6c9,stroke:#2e7d32
style FB_Comp fill:#ffecb3,stroke:#ffa000
sequenceDiagram
participant A as Client A
participant B as Client B
participant SS as SuperSync Server
participant File as sync-data.json
Note over A,File: ═══ SUPERSYNC: Server Detects Gap ═══
rect rgb(227, 242, 253)
A->>SS: POST /ops [op1, op2]
SS->>SS: Assign seq 10, 11
SS-->>A: OK {seqs: [10, 11]}
B->>SS: POST /ops [op3] (lastKnown: 9)
SS->>SS: Gap! Client missing 10, 11
SS-->>B: 409 Conflict {missing: [op1, op2]}
B->>B: Process missing ops first
B->>SS: POST /ops [op3] (lastKnown: 11)
SS-->>B: OK {seqs: [12]}
end
Note over A,File: ═══ FILE-BASED: Piggybacking ═══
rect rgb(255, 243, 224)
A->>File: Download (v=5)
A->>A: Merge ops, set v=6
A->>File: Upload (v=6)
B->>File: Download (v=5, expects v=5)
Note over B: Version changed! (now v=6)
B->>B: Find A's ops in file (piggybacked)
B->>B: Merge A's ops + own ops
B->>B: Set v=7
B->>File: Upload (v=7)
B->>B: Process piggybacked ops locally
end
flowchart TB
subgraph SuperSync["SuperSync Storage"]
direction TB
PG["PostgreSQL Database"]
subgraph Tables["Tables"]
OpsTable["operations
━━━━━━━━━━━━━━━
id, client_id, seq
action_type, payload
vector_clock, timestamp"]
ClientsTable["clients
━━━━━━━━━━━━━━━
client_id, last_seq
created_at"]
end
PG --> Tables
end
subgraph FileBased["File-Based Storage"]
direction TB
SyncFile["sync-data.json"]
subgraph Contents["Contents"]
Meta["Metadata
━━━━━━━━━━━━━━━
version, syncVersion
lastModified, checksum"]
State["State Snapshot
━━━━━━━━━━━━━━━
Full AppDataComplete"]
Archive["Archive Data
━━━━━━━━━━━━━━━
archiveYoung, archiveOld"]
RecentOps["Recent Ops (200)
━━━━━━━━━━━━━━━
CompactOperation[]"]
end
SyncFile --> Contents
end
style SuperSync fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style FileBased fill:#fff3e0,stroke:#e65100,stroke-width:2px
sequenceDiagram
participant Client
participant SS as SuperSync Server
participant FB as File Provider
rect rgb(227, 242, 253)
Note over Client,SS: SuperSync Download
Client->>SS: GET /ops?since={lastSeq}
SS->>SS: Query ops WHERE seq > lastSeq
SS-->>Client: {ops: [...], lastSeq: N}
Note over Client: Only receives new ops
Bandwidth efficient
end
rect rgb(255, 243, 224)
Note over Client,FB: File-Based Download
Client->>FB: downloadFile("sync-data.json")
FB-->>Client: {state, recentOps, syncVersion}
Client->>Client: Filter ops by lastProcessedSeq
Note over Client: Downloads full file
Filters locally
end
sequenceDiagram
participant Client
participant SS as SuperSync Server
participant FB as File Provider
rect rgb(227, 242, 253)
Note over Client,SS: SuperSync Upload
Client->>SS: POST /ops {ops: [...], lastKnownSeq}
SS->>SS: Validate sequence continuity
alt Gap detected
SS-->>Client: 409 Conflict + missing ops
else No gap
SS->>SS: Insert ops, assign seq numbers
SS-->>Client: 200 OK {assignedSeqs}
end
end
rect rgb(255, 243, 224)
Note over Client,FB: File-Based Upload
Client->>FB: downloadFile (get current state)
FB-->>Client: {syncVersion: N, recentOps}
Client->>Client: Merge local ops + file ops
Client->>Client: Find piggybacked ops
Client->>Client: Set syncVersion = N+1
Client->>FB: uploadFile(merged data)
FB-->>Client: Success
Note over Client: Returns piggybacked ops
for immediate processing
end
flowchart TB
subgraph SuperSync["SuperSync Conflict Handling"]
SS1["Client uploads ops"]
SS2{"Server checks
sequence gap?"}
SS3["Gap: Return 409
+ missing ops"]
SS4["No gap: Accept ops"]
SS5["Client downloads
missing ops"]
SS6["LWW resolution
on client"]
SS1 --> SS2
SS2 -->|Yes| SS3
SS2 -->|No| SS4
SS3 --> SS5
SS5 --> SS6
end
subgraph FileBased["File-Based Conflict Handling"]
FB1["Client downloads file"]
FB2{"syncVersion
changed?"}
FB3["Version match:
Clean upload"]
FB4["Version changed:
Piggybacking"]
FB5["Merge all ops"]
FB6["Upload merged file"]
FB7["Return piggybacked ops"]
FB8["LWW resolution
on client"]
FB1 --> FB2
FB2 -->|No| FB3
FB2 -->|Yes| FB4
FB4 --> FB5
FB5 --> FB6
FB6 --> FB7
FB7 --> FB8
end
style SuperSync fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style FileBased fill:#fff3e0,stroke:#e65100,stroke-width:2px
flowchart TD
Start["Choose Sync Provider"] --> Q1{Need real-time
multi-device sync?}
Q1 -->|Yes| Q2{Have SuperSync
account?}
Q1 -->|No| FileBased
Q2 -->|Yes| SuperSync
Q2 -->|No| Q3{Have cloud
storage?}
Q3 -->|WebDAV/Dropbox| FileBased
Q3 -->|No| LocalFile
SuperSync["SuperSync
━━━━━━━━━━━━━━━
• Real-time sync
• Efficient bandwidth
• Server-managed gaps
• Best for active teams"]
FileBased["File-Based Sync
━━━━━━━━━━━━━━━
• Uses existing storage
• No additional account
• Self-hosted option
• Good for individuals"]
LocalFile["Local File
━━━━━━━━━━━━━━━
• Manual sync
• Full control
• Backup purposes"]
style SuperSync fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
style FileBased fill:#fff3e0,stroke:#e65100,stroke-width:2px
style LocalFile fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
Both providers implement OperationSyncCapable interface and use:
| Component | Purpose |
|---|---|
OperationLogSyncService | Orchestrates sync timing and triggers |
ConflictResolutionService | LWW resolution for concurrent edits |
VectorClockService | Causality tracking for all operations |
OperationApplierService | Applies remote ops to NgRx state |
ArchiveOperationHandler | Handles archive side effects |
| SuperSync | File-Based |
|---|---|
SuperSyncProvider | FileBasedSyncAdapter |
| REST API client | File provider abstraction |
| Server-side sequence management | Client-side syncVersion tracking |
| Gap detection via HTTP 409 | Piggybacking on version mismatch |
| File | Purpose |
|---|---|
src/app/op-log/sync-providers/super-sync/ | SuperSync provider implementation |
src/app/op-log/sync-providers/file-based/ | File-based adapter and types |
src/app/op-log/sync/operation-log-sync.service.ts | Shared sync orchestration |
src/app/op-log/sync/conflict-resolution.service.ts | LWW conflict resolution |