{"path":"research/crdt-collaborative-graphs.md","content":"# Real-Time Collaborative Editing of Graph Structures\n\n**Research date**: March 27, 2026\n**Context**: Architecture research for Deliberus — collaborative argumentation platform\n**Status**: EXPLORATORY — findings inform design, not decisions\n\n---\n\n## 1. CRDTs for Graphs: Yjs, Automerge, Diamond Types, Loro\n\n### Do any support graph structures natively?\n\n**Short answer: No.** None of the major CRDT libraries provide a first-class directed graph data type. All require composition from primitives.\n\n**Yjs** provides: `Y.Text`, `Y.Array`, `Y.Map`, `Y.XmlFragment`. Under the hood, all compound types are represented as lists. The [Yjs community has discussed graph data structures](https://discuss.yjs.dev/t/graph-data-structures/745) and the consensus pattern is:\n\n- `Y.Map` of nodes (keyed by node ID, values are `Y.Map` of properties)\n- `Y.Map` of edges (keyed by edge ID, values contain source/target node IDs + properties)\n\nThis is an adjacency-list-as-maps pattern, not a native graph CRDT. The key challenge: when one user deletes a node while another adds an edge to it, the edge becomes a dangling reference. Yjs has no built-in mechanism to enforce referential integrity across maps.\n\n**Automerge** provides JSON-like documents (maps, lists, text, counters). Same adjacency-list-via-maps approach applies. Automerge's Rust core with WASM bindings gives better performance than pure-JS alternatives. No native graph type.\n\n**Diamond Types** is focused exclusively on text/list CRDTs. Currently only supports plain text editing, with JSON-style data types under development. Not suitable for graph structures today.\n\n**Loro** is the most interesting newcomer. Built in Rust with JS/WASM bindings, it supports: List, Map, Text, Tree (movable tree CRDT based on Kleppmann et al.), MovableList, Counter. The **Tree type** is notable — it handles hierarchical/tree structures with move operations, but trees are not graphs (no cycles, single parent per node). For DAG-structured argument maps, Loro's Tree is insufficient but its primitives (Map + List) can compose a graph the same way as Yjs.\n\n**GUN** is the only CRDT library that natively models graphs. It's a decentralized, offline-first graph database using WebRTC networking. Used by the decentralized Internet Archive. However, GUN's API is unconventional, documentation is sparse, and the project's maintenance trajectory is uncertain. Its graph CRDT uses a custom conflict resolution algorithm (HAM — Hypothetical Amnesia Machine) that may not align well with structured argumentation semantics.\n\n### Challenges of CRDT-based graph editing\n\n1. **Dangling references**: The fundamental problem. CRDTs guarantee convergence within a data type, but cross-reference integrity between separate CRDT documents (nodes map vs edges map) is not enforced. Solutions: application-level validation, tombstoning deleted nodes (mark deleted but retain for edge resolution), or compound operations.\n\n2. **Cycle detection**: For DAG-structured argument graphs, concurrent edge additions could create cycles. CRDTs have no mechanism to prevent this — cycle detection must happen as a post-merge validation step on the server or client.\n\n3. **Semantic conflicts**: Two users adding contradictory arguments to the same claim isn't a data conflict — both additions should survive. But two users editing the *same* argument's text is a genuine conflict. The conflict resolution strategy must differ by operation type.\n\n4. **Tombstone accumulation**: Deleted nodes/edges must be retained as tombstones for CRDT convergence. In a long-lived argumentation graph, this can accumulate significant memory overhead over months/years.\n\n5. **Ordering**: Arguments under a claim often have meaningful order (strongest first, chronological, etc.). CRDT lists handle ordering well, but the ordering must be maintained per-parent-node, adding complexity.\n\nSources:\n- [Yjs GitHub](https://github.com/yjs/yjs)\n- [Yjs Graph Data Structures Discussion](https://discuss.yjs.dev/t/graph-data-structures/745)\n- [Yjs Deletion with Multiple Parents](https://discuss.yjs.dev/t/deletion-of-things-with-multiple-parents-on-conflicts/685)\n- [Automerge](https://automerge.org/)\n- [Diamond Types](https://github.com/josephg/diamond-types)\n- [Loro](https://loro.dev/)\n- [Loro Tree Documentation](https://www.loro.dev/docs/tutorial/tree)\n- [Loro Movable Tree CRDT](https://github.com/loro-dev/movable-tree)\n- [GUN Graph Database](https://github.com/amark/gun)\n- [CRDT Implementations List](https://crdt.tech/implementations)\n\n---\n\n## 2. How Existing Collaborative Tools Handle Graph/Diagram Editing\n\n### Figma\n\n**Architecture**: Client/server over WebSockets. Each open document gets a dedicated server process (\"multiplayer service\") that is authoritative. Not pure CRDT — custom conflict resolution inspired by CRDT literature.\n\n**Conflict resolution**: Last-writer-wins (LWW) per property per object. Two users changing different properties on the same object don't conflict. Two users changing the same property on the same object: last value received by server wins. This is equivalent to a LWW-Register CRDT but with server-defined ordering.\n\n**Document model**: Tree of objects (like HTML DOM) — root → pages → object hierarchy. Each object has an ID and property map. Changes are property-level diffs, not full object replacements.\n\n**Performance**: 95% of edits saved within 600ms. Uses WASM for rendering. Highly optimized diff format — old file format was replaced because it wasn't efficient enough for small message sizes.\n\n**Key lesson for Deliberus**: Figma discovered that \"seemingly simple action on one object actually affected certain other objects too, so users editing two seemingly unrelated objects would cause unintentional conflicts.\" This is directly relevant to argument graphs where editing a claim might affect its relationship edges.\n\n### tldraw\n\n**Architecture**: Each canvas runs on a Cloudflare Durable Object (dedicated mini-server), handling up to 50 simultaneous collaborators. Uses \"TLSync\" protocol — CRDT-like approach with separate layers for confirmed server data and pending local edits.\n\n**Persistence**: Automatic to Durable Object SQLite storage. Every change saved immediately. Media files go to R2.\n\n**Key lesson**: Durable Objects pattern is interesting for Deliberus — each \"debate\" or \"deliberation\" could be its own isolated sync instance, avoiding cross-debate interference.\n\n### Excalidraw\n\n**Architecture**: Pseudo-P2P model. Central server (Socket.IO) relays end-to-end encrypted messages but does no coordination. No central state authority.\n\n**Conflict resolution**: Custom reconciliation algorithm using `versionNonce` field on elements. When same element edited concurrently, lower `versionNonce` wins (deterministic tiebreaker). Not using CRDTs or OT — considers same-element conflicts \"rare\" and acceptable to resolve with arbitrary tiebreaking.\n\n**Key lesson**: For argument graphs, \"rare concurrent same-element edits\" may not hold — popular claims will attract simultaneous responses. Excalidraw's approach is too simplistic for Deliberus.\n\n### Miro\n\nTechnical architecture details are not publicly documented. Uses WebSocket-based communication with real-time cursor tracking. No published details on conflict resolution algorithm.\n\nSources:\n- [How Figma's Multiplayer Technology Works](https://www.figma.com/blog/how-figmas-multiplayer-technology-works/)\n- [Making Multiplayer More Reliable (Figma)](https://www.figma.com/blog/making-multiplayer-more-reliable/)\n- [tldraw Sync Documentation](https://tldraw.dev/docs/sync)\n- [tldraw Sync Cloudflare](https://github.com/tldraw/tldraw-sync-cloudflare)\n- [Excalidraw P2P Collaboration](https://blog.excalidraw.com/building-excalidraw-p2p-collaboration-feature/)\n- [Excalidraw CRDT RFC Discussion](https://github.com/excalidraw/excalidraw/issues/3537)\n\n---\n\n## 3. Yjs + Graph Database Sync Architecture\n\n### Can Yjs sync with a persistent graph database?\n\nYes, but it requires a custom sync layer. No off-the-shelf Yjs → FalkorDB/Neo4j adapter exists.\n\n### Architecture patterns\n\n**Pattern A: Yjs as source of truth, graph DB as read model (recommended for Deliberus)**\n\n```\nClients ←→ y-websocket/y-redis ←→ Yjs Doc (authoritative)\n                                        ↓ (event-driven)\n                                   Sync Worker\n                                        ↓\n                                   FalkorDB (query/analytics)\n```\n\n- Yjs document is the authoritative state for real-time editing\n- Yjs `observeDeep` events trigger a sync worker that translates changes into Cypher mutations\n- FalkorDB serves as the query/analytics layer (complex graph traversals, argument scoring, search)\n- Sync is **event-driven, unidirectional** (Yjs → FalkorDB)\n- FalkorDB writes that don't come from user edits (e.g., AI-generated analysis, moderation) go through a separate path that also updates the Yjs doc\n\n**Pattern B: Graph DB as source of truth, Yjs as collaboration layer**\n\n```\nClients ←→ Yjs Doc (ephemeral collaboration state)\n                ↓ (debounced writes)\n            API Server\n                ↓\n            FalkorDB (authoritative)\n                ↓ (change feed)\n            WebSocket push to clients\n```\n\n- FalkorDB is authoritative\n- Yjs handles real-time merge of concurrent edits, then periodically flushes to DB\n- More complex: requires bidirectional sync, conflict between DB state and Yjs state\n\n**Pattern C: Hybrid (Figma-style server authority)**\n\n```\nClients ←→ WebSocket ←→ Collaboration Server (authoritative)\n                              ↓\n                         FalkorDB (persistence)\n```\n\n- Server process per debate, holds authoritative state in memory\n- Applies LWW or custom merge logic\n- Persists to FalkorDB on every change\n- No Yjs needed — custom sync protocol\n\n### Yjs persistence ecosystem\n\n- **y-websocket**: Basic server with optional LevelDB persistence and HTTP callbacks\n- **y-redis**: Scalable backend using Redis pub/sub. No in-memory state after initial sync. Separate worker persists to S3/Postgres. **This is the production-grade option.**\n- **Liveblocks**: Managed Yjs hosting — handles scaling, persistence, and conflict resolution as a service. Charges per monthly active user.\n- **y-indexeddb**: Client-side persistence for offline support\n\n### Sync architecture for Deliberus recommendation\n\nPattern A with y-redis is the most pragmatic:\n1. y-redis handles WebSocket connections and pub/sub\n2. Yjs doc per debate/deliberation maps nodes and edges as `Y.Map` collections\n3. A custom sync worker listens to Yjs update events and maintains FalkorDB in sync\n4. FalkorDB serves complex queries (argument scoring, graph analytics, semantic search via Graphiti)\n5. AI-generated insights write to both FalkorDB and Yjs doc via server-side Yjs mutations\n\nSources:\n- [y-websocket Documentation](https://docs.yjs.dev/ecosystem/connection-provider/y-websocket)\n- [y-redis Documentation](https://docs.yjs.dev/ecosystem/database-provider/y-redis)\n- [y-redis GitHub](https://github.com/yjs/y-redis)\n- [Liveblocks Yjs](https://liveblocks.io/docs/platform/sync-datastore/liveblocks-yjs)\n- [Yjs Database Sync (Liveblocks)](https://liveblocks.io/docs/guides/how-to-synchronize-your-liveblocks-yjs-document-data-to-a-planetscale-mysql-database)\n\n---\n\n## 4. Conflict Resolution for Argument Graphs\n\n### The core question: When two users simultaneously add arguments to the same claim, how should the system resolve?\n\n**Answer: \"Both additions win\" is absolutely the right default for argument graphs.**\n\nThis is fundamentally different from document editing where concurrent edits to the same paragraph need complex merging. In argumentation:\n\n- **Adding a pro-argument**: Always additive. Two users adding different supporting arguments is not a conflict — both should appear.\n- **Adding a con-argument**: Same — both should appear.\n- **Adding an argument and deleting a different one**: Add-wins semantics (CRDT OR-Set behavior) is correct. The deletion should only affect the specific argument being deleted, not block the addition.\n- **Editing the same argument's text**: This IS a genuine conflict. Options: LWW (simple but lossy), character-level CRDT merge (complex but preserves both edits), or fork-and-review (create two versions for human resolution).\n- **Moving/reordering arguments**: CRDT list ordering handles this, but concurrent reorders under the same parent need a deterministic tiebreaker.\n- **Voting/scoring the same argument**: Commutative counters (G-Counter, PN-Counter CRDTs) handle concurrent votes naturally — votes are additive operations that commute.\n\n### CRDT merge semantics vs Operational Transform\n\nFor argument graphs, **CRDTs are clearly superior to OT**:\n\n| Aspect | CRDT | OT |\n|--------|------|-----|\n| Adding arguments | Natural (set-add is commutative) | Requires transform functions |\n| Offline editing | Built-in (merge on reconnect) | Requires central server |\n| Server complexity | Simpler (no transform logic) | Complex (O(n^2) transforms) |\n| Consistency | Eventual, guaranteed | Immediate, but fragile |\n| Graph-specific | No native support, but composable | No native support, harder to compose |\n\n### Recommended conflict resolution strategy for Deliberus\n\n| Operation | Strategy | Rationale |\n|-----------|----------|-----------|\n| Add node (claim/argument) | Add-wins (OR-Set) | Arguments are additive; never lose user contributions |\n| Add edge (support/attack) | Add-wins, validate no cycles | Relationships are additive; cycle detection post-merge |\n| Delete node | Tombstone + cascade soft-delete edges | Preserve for undo; mark as deleted, not removed |\n| Edit node text | Character-level CRDT (Y.Text) | Preserves both users' edits at character granularity |\n| Edit node metadata | LWW per-field | Simple, acceptable for metadata (tags, labels) |\n| Vote/score | PN-Counter | Votes naturally commute |\n| Reorder children | CRDT list ordering | Deterministic merge of concurrent reorders |\n\nSources:\n- [CRDT Theory](https://crdt.tech/)\n- [CRDT Wikipedia](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)\n- [CRDTs vs OT Practical Guide](https://hackernoon.com/crdts-vs-operational-transformation-a-practical-guide-to-real-time-collaboration)\n- [Redis CRDT Deep Dive](https://redis.io/blog/diving-into-crdts/)\n- [The CRDT Dictionary](https://www.iankduncan.com/engineering/2025-11-27-crdt-dictionary/)\n\n---\n\n## 5. Collaborative Argument Mapping Tools: Concurrency Handling\n\n### Kialo\n\nKialo supports simultaneous editing by multiple users — students can \"contribute instantly, simultaneously, and from anywhere.\" The platform uses a tree structure (thesis → supporting/opposing claims → sub-claims) rather than a general graph. **No public documentation on concurrency mechanism.** Given the tree structure and claim-level granularity (users edit individual claims, not shared text), conflicts are likely rare by design — each claim is an atomic unit owned by its author. Concurrent additions to the same parent claim are straightforward (both appear).\n\n### DebateGraph\n\nDebateGraph (debategraph.org) is a collaborative argument mapping platform focused on visualization. Limited technical documentation available. Appears to use traditional server-side state management rather than CRDTs.\n\n### OVA3 (ARG-tech)\n\nOnline Visualisation of Argument (OVA3) from ARG-tech explicitly supports \"collaborative editing\" and \"online collaborative working, allowing remote teams to work together on argument analysis in real time.\" Supports argumentation schemes (Walton-style). No published details on the sync mechanism.\n\n### AppleTree\n\nAcademic system with graph-based workspace for argumentation and real-time learning analytics (contribution counts, social network analysis, argumentation structure analysis). Research-oriented, not production software.\n\n### Athens Research (discontinued)\n\nWas a YC W21 collaborative knowledge graph built with ClojureScript and DataScript (Datalog-based). Used Roam-like block-level editing. **No longer maintained.** The failure of Athens is instructive — collaborative knowledge graphs are hard to sustain as products.\n\n### Key insight for Deliberus\n\nNo existing argumentation platform has published a sophisticated concurrency solution. Most avoid the problem through design:\n- **Claim-level atomicity**: Each claim/argument is an atomic unit. Users add/remove whole claims, not edit shared text collaboratively.\n- **Optimistic UI with server reconciliation**: Show the addition immediately, let the server order things.\n- **Moderation as conflict resolution**: Human moderators handle contradictory additions, not algorithms.\n\nDeliberus has an opportunity to be the first argumentation platform with true CRDT-based real-time collaboration — but should consider whether the complexity is justified for the MVP.\n\nSources:\n- [Kialo Wikipedia](https://en.wikipedia.org/wiki/Kialo)\n- [ARG-tech Software](https://www.arg-tech.org/index.php/category/software/)\n- [Argunet](http://www.argunet.org)\n- [Athens Research (archived)](https://github.com/athensresearch/athens)\n- [Kialo Edu](https://hundred.org/en/innovations/6-kialo-edu)\n\n---\n\n## 6. WebSocket + Graph Updates: Architecture Patterns\n\n### Pushing graph changes to connected clients\n\n**Pattern 1: Full-state broadcast (simplest, doesn't scale)**\n- On any change, serialize entire visible graph and send to all clients\n- Only viable for very small graphs (<100 nodes)\n\n**Pattern 2: Operation-based broadcast (recommended)**\n- Send individual operations (addNode, removeNode, addEdge, updateProperty)\n- Clients apply operations to local state\n- Server maintains operation log for late-joining clients\n- Similar to event sourcing\n\n**Pattern 3: CRDT sync (most robust)**\n- Yjs/Automerge handle sync protocol automatically\n- Clients exchange state vectors and missing updates\n- Works across reconnections without special handling\n\n**Pattern 4: GraphQL Subscriptions**\n- Clients subscribe to specific queries\n- Server evaluates subscriptions against changes, pushes matching updates\n- Natural fit for partial graph subscriptions\n- Production tooling: Apollo Server + Redis PubSub for horizontal scaling\n\n### Partial graph subscriptions (viewing a subgraph)\n\nThis is critical for argument graphs — users typically view a subtree or neighborhood, not the entire graph.\n\n**Approach 1: Topic-based pub/sub**\n- Each node ID is a \"topic\"\n- Client subscribes to visible node IDs + their immediate neighbors\n- When client pans/navigates, update subscriptions\n- Efficient but requires subscription management logic\n\n**Approach 2: Viewport-based subscriptions**\n- Client reports current viewport (visible node IDs)\n- Server filters updates to only those affecting visible nodes\n- Simpler client logic, more server-side filtering work\n\n**Approach 3: Yjs subdocuments**\n- Yjs supports lazy-loading subdocuments\n- Each debate branch could be a subdocument, loaded on demand\n- Natural fit for argument trees where users drill into branches\n- Challenge: edges crossing subdocument boundaries\n\n**Recommended for Deliberus**: Yjs subdocuments for branch-level isolation + topic-based subscriptions for cross-branch edges. Each major claim and its immediate arguments form a subdocument. Cross-branch relationships (analogies, duplicates) use a separate shared edge document.\n\nSources:\n- [GraphQL Subscriptions](https://graphql.org/learn/subscriptions/)\n- [Apollo GraphQL Subscriptions](https://www.apollographql.com/docs/react/data/subscriptions)\n- [Yjs Subdocuments](https://docs.yjs.dev)\n- [Mastering Scalable GraphQL Subscriptions](https://dev.to/vaib/mastering-scalable-graphql-subscriptions-advanced-patterns-for-real-time-applications-j0n)\n- [Building Real-Time WebSocket App with SvelteKit](https://www.inngest.com/blog/building-a-realtime-websocket-app-using-sveltekit)\n\n---\n\n## 7. Performance at Scale\n\n### CRDT performance benchmarks\n\n**Yjs**: High-performance, binary-encoded CRDT. Handles large documents with long edit histories efficiently. Memory-efficient garbage collection for tombstones. The [crdt-benchmarks](https://github.com/dmonad/crdt-benchmarks) repository by Yjs creator Kevin Jahns provides comparative data.\n\n**IM-CRDT** (academic benchmark): Latency increases only 6% (249ms → 264ms) when scaling from 1 to 20 concurrent updates. Promising for moderate concurrency.\n\n**Tombstone-based CRDTs** (e.g., WOOT): Significant memory growth and performance degradation as deletions accumulate. Not suitable for long-lived documents without periodic compaction.\n\n### Estimated performance for 100+ concurrent users on 10K-node argument graph\n\n| Metric | Estimate | Notes |\n|--------|----------|-------|\n| Latency (local edit → render) | <50ms | Local-first, no network wait |\n| Latency (edit → other users see it) | 100-600ms | WebSocket round-trip + server processing |\n| Memory per client | 5-50 MB | Depends on loaded subgraph size |\n| Memory per server (y-redis) | Minimal | No in-memory Yjs state after initial sync |\n| Bandwidth per edit operation | 50-500 bytes | CRDT operation encoding |\n| Bandwidth sustained (100 users, active editing) | 50-500 KB/s total | Most users read, few edit simultaneously |\n| Initial sync (10K nodes) | 1-5 seconds | Binary Yjs encoding, compressed |\n\n### Scaling strategies\n\n1. **Subdocument loading**: Don't load entire 10K-node graph. Load visible subtree (50-200 nodes) and lazy-load on navigation.\n2. **y-redis for horizontal scaling**: Stateless WebSocket servers behind load balancer. Redis pub/sub for cross-server sync.\n3. **Debate isolation**: Each debate is a separate Yjs document. No cross-debate sync overhead.\n4. **Read replicas**: FalkorDB for read-heavy operations (search, analytics). Yjs only for active editing sessions.\n5. **Awareness throttling**: User cursor/presence updates at 1-2 Hz max, not on every mouse move.\n\n### Real-world precedents\n\n- **League of Legends** chat: Riak CRDT handling 7.5 million concurrent users, 11K messages/second\n- **Figma**: 95% of edits saved within 600ms, supporting large professional teams\n- **tldraw**: Up to 50 simultaneous collaborators per Durable Object instance\n\nSources:\n- [CRDT Benchmarks](https://github.com/dmonad/crdt-benchmarks)\n- [Quantifying CRDT Performance (INRIA)](https://inria.hal.science/hal-04337761v1/document)\n- [Are CRDTs Suitable for Shared Editing? (Kevin Jahns)](https://blog.kevinjahns.de/are-crdts-suitable-for-shared-editing)\n- [CRDT Implementation Guide (Velt)](https://velt.dev/blog/crdt-implementation-guide-conflict-free-apps)\n\n---\n\n## 8. Offline-First Argument Graphs\n\n### Can argument graphs work offline?\n\n**Yes, and CRDTs make this natural.** The entire point of CRDTs is that independent edits can merge without coordination.\n\n### How it works\n\n1. **Client stores Yjs document locally** via y-indexeddb (IndexedDB in browser) or filesystem on mobile\n2. **User edits offline**: Adds arguments, votes, edits text. All operations stored in local Yjs doc.\n3. **Reconnection**: Yjs sync protocol exchanges state vectors, identifies missing operations, merges automatically\n4. **Conflict resolution**: Per the strategies in Section 4 — additions merge cleanly, text edits merge at character level, metadata uses LWW\n\n### Challenges specific to argument graphs\n\n1. **Divergent argument trees**: Two users offline for hours could build significantly different argument structures under the same claim. When they reconnect, both structures merge — but the combined tree might be confusing or contain near-duplicate arguments. **Mitigation**: Post-merge duplicate detection (NLI/semantic similarity) + notification to users.\n\n2. **Stale voting**: A user votes on an argument offline. Meanwhile, that argument is edited substantially or deleted online. When they reconnect, their vote applies to the current (changed) version. **Mitigation**: Tombstone detection + vote re-confirmation prompts.\n\n3. **Offline moderation gap**: If a harmful argument is added offline, moderation only kicks in on reconnect. **Mitigation**: Server-side validation on sync, with automatic quarantine of flagged content.\n\n### Mobile considerations\n\n- Yjs binary encoding is compact — a 1000-node argument graph is roughly 100-500 KB\n- Automerge's Rust core with mobile bindings may be more efficient than Yjs's WASM for native mobile apps\n- React Native / Capacitor can use Yjs via WASM; native iOS/Android may prefer Automerge's Swift/Kotlin bindings\n\nSources:\n- [Building Offline-First Editors with CRDTs](https://dev.to/hexshift/building-offline-first-collaborative-editors-with-crdts-and-indexeddb-no-backend-needed-4p7l)\n- [Automerge + Convex (Local-First)](https://stack.convex.dev/automerge-and-convex)\n- [Why Cinapse Moved Away from CRDTs](https://www.powersync.com/blog/why-cinapse-moved-away-from-crdts-for-sync)\n- [Offline P2P Editing with Yjs (ProseMirror)](https://discuss.prosemirror.net/t/offline-peer-to-peer-collaborative-editing-using-yjs/2488)\n\n---\n\n## 9. Existing Implementations\n\n### Collaborative knowledge graph editors (open source)\n\n| Project | Status | Tech | Collaboration | Notes |\n|---------|--------|------|---------------|-------|\n| Athens Research | **Dead** (was YC W21) | ClojureScript, DataScript | Real-time multiplayer | Knowledge graph; no longer maintained |\n| Relay (Obsidian) | **Active** | Yjs | Real-time CRDT via Yjs | Plugin for Obsidian vaults; file-based, not graph-native |\n| WhyHow KG Studio | **Active** (MIT) | Python, NoSQL | Collaborative graph building | Non-technical users can view/contribute; not real-time editing |\n| Graphiti (Zep) | **Active** | Python, FalkorDB | Single-writer (AI agents) | Temporal knowledge graph; no multi-user editing |\n\n### How production collaborative editors work\n\n**Notion**: Hybrid approach — CRDT for block structure, OT for text within blocks. Server-authoritative. Blocks are the unit of collaboration (similar to claims in an argument graph).\n\n**Roam Research**: Built on DataScript (Datalog). Graph-based data model where every block is a node. Has multiplayer mode but it's limited — not designed for team workflows. No published sync architecture.\n\n**Obsidian Relay**: Uses Yjs CRDTs. Local-first — server acts as a relay, not authority. Edits tracked locally, server echoes updates to collaborators. Works offline, merges on reconnect. **Most architecturally relevant to Deliberus** because it demonstrates Yjs-based collaboration on structured (linked) content.\n\n**Peerdraft (Obsidian)**: Alternative Obsidian collaboration plugin. Uses peer-to-peer sync.\n\n### Key takeaway\n\nNo open-source project combines: (1) graph-native data model, (2) real-time CRDT collaboration, (3) argumentation semantics. This is Deliberus's differentiating opportunity — and its primary technical risk.\n\nSources:\n- [Athens Research GitHub](https://github.com/athensresearch/athens)\n- [Relay for Obsidian](https://relay.md/)\n- [Relay GitHub](https://github.com/No-Instructions/Relay)\n- [WhyHow Knowledge Graph Studio](https://medium.com/enterprise-rag/open-sourcing-the-whyhow-knowledge-graph-studio-powered-by-nosql-edce283fb341)\n- [Graphiti GitHub](https://github.com/getzep/graphiti)\n- [Roam Research Data Structure Deep Dive](https://www.zsolt.blog/2021/01/Roam-Data-Structure-Query.html)\n- [Notion Architecture (Architectures for Central Server Collaboration)](https://mattweidner.com/2024/06/04/server-architectures.html)\n\n---\n\n## 10. Recommended Architecture for Deliberus\n\nGiven: FalkorDB backend, SvelteKit frontend, Sigma.js for graph visualization.\n\n### Proposed architecture: Yjs + y-redis + FalkorDB sync\n\n```\n┌─────────────────────────────────────────────────────────┐\n│                    SvelteKit Frontend                    │\n│  ┌──────────┐  ┌──────────┐  ┌────────────────────────┐ │\n│  │ Sigma.js │  │ Yjs Doc  │  │ Awareness (cursors,    │ │\n│  │ (render) │←→│ (local)  │  │  presence, selections) │ │\n│  └──────────┘  └────┬─────┘  └────────────────────────┘ │\n│                     │ WebSocket                          │\n└─────────────────────┼───────────────────────────────────┘\n                      │\n┌─────────────────────┼───────────────────────────────────┐\n│              y-redis Server Layer                        │\n│  ┌──────────────────┼──────────────────────────────┐    │\n│  │  WebSocket Server │  (stateless, horizontally    │    │\n│  │  (n instances)    │   scalable)                  │    │\n│  └──────────────────┼──────────────────────────────┘    │\n│                     │                                    │\n│  ┌──────────────────┼──────────────────────────────┐    │\n│  │         Redis (pub/sub + temp storage)           │    │\n│  └──────────────────┼──────────────────────────────┘    │\n│                     │                                    │\n│  ┌──────────────────┼──────────────────────────────┐    │\n│  │         Sync Worker (y-redis worker)             │    │\n│  │         • Persists Yjs state to S3/disk          │    │\n│  │         • Triggers FalkorDB sync                 │    │\n│  └──────────────────┼──────────────────────────────┘    │\n└─────────────────────┼───────────────────────────────────┘\n                      │\n┌─────────────────────┼───────────────────────────────────┐\n│              FalkorDB Sync Layer                         │\n│  ┌──────────────────┼──────────────────────────────┐    │\n│  │   Graph Sync Service                             │    │\n│  │   • Listens to Yjs update events                 │    │\n│  │   • Translates to Cypher mutations               │    │\n│  │   • Maintains FalkorDB as query/analytics layer  │    │\n│  └──────────────────┼──────────────────────────────┘    │\n│                     │                                    │\n│  ┌──────────────────┼──────────────────────────────┐    │\n│  │   FalkorDB (on Darwin, darwin.home:6380)          │    │\n│  │   • Graph queries (traversals, scoring)          │    │\n│  │   • AI analysis (claim extraction, NLI)          │    │\n│  │   • Graphiti integration (episodic memory)       │    │\n│  └─────────────────────────────────────────────────┘    │\n└─────────────────────────────────────────────────────────┘\n```\n\n### Yjs document structure for argument graphs\n\n```javascript\n// Per-debate Yjs document\nconst ydoc = new Y.Doc()\n\n// Nodes: claims, arguments, evidence\nconst nodes = ydoc.getMap('nodes')\n// Each node: Y.Map { id, type, text: Y.Text, author, created, metadata: Y.Map }\n\n// Edges: support, attack, analogy, duplicate\nconst edges = ydoc.getMap('edges')\n// Each edge: Y.Map { id, type, source, target, author, created, weight }\n\n// Ordering: children order per parent node\nconst childOrder = ydoc.getMap('childOrder')\n// Each entry: Y.Array of child node IDs\n\n// Votes/scores\nconst votes = ydoc.getMap('votes')\n// Each entry: Y.Map { nodeId: Y.Map { userId: vote } }\n```\n\n### Why this architecture\n\n1. **Yjs handles the hard part** — real-time sync, offline support, conflict resolution, binary encoding\n2. **y-redis scales horizontally** — stateless servers, Redis pub/sub, no in-memory document state\n3. **FalkorDB serves queries** — complex graph traversals, argument scoring, semantic search, AI analysis\n4. **Separation of concerns** — editing layer (Yjs) vs. analytics layer (FalkorDB) vs. presentation layer (Sigma.js)\n5. **SvelteKit + Sigma.js fit** — Sigma.js handles WebGL graph rendering of thousands of nodes; SvelteKit provides reactive UI around it; Yjs integrates via simple store bindings\n\n### MVP simplification\n\nFor the MVP, skip y-redis and use y-websocket with LevelDB persistence:\n\n```\nClient (SvelteKit + Yjs + Sigma.js)\n  ↕ WebSocket\ny-websocket server (single instance, LevelDB persistence)\n  → FalkorDB sync (event-driven, debounced)\n```\n\nThis handles 10-20 concurrent users on a single server. Migrate to y-redis when scaling demands it.\n\n### Technology decisions still needed\n\n- **Yjs vs Loro**: Loro is newer, faster, has native tree support — but less ecosystem (no y-redis equivalent yet). Yjs is battle-tested with mature tooling. **Recommendation**: Start with Yjs, evaluate Loro when it matures.\n- **Subdocument granularity**: One Yjs doc per debate? Per major claim? Per branch? Affects initial sync time and memory. Needs prototyping.\n- **AI-generated content**: How do AI-produced claim extractions, NLI judgments, and argument analysis enter the collaborative doc? Server-side Yjs mutations via the sync worker, or separate read-only overlay?\n- **Moderation flow**: Real-time moderation on the Yjs layer (immediate, before persistence) or post-hoc on the FalkorDB layer (delayed, but simpler)?\n\nSources:\n- [Sigma.js](https://www.sigmajs.org/)\n- [Sigma.js with Svelte](https://dev.to/deotyma/how-to-use-sigmajs-with-sveltejs-166p)\n- [y-redis README](https://github.com/yjs/y-redis/blob/master/README.md)\n- [y-websocket](https://github.com/yjs/y-websocket)\n- [Yjs vis-network Example](https://discuss.yjs.dev/t/example-of-yjs-used-to-share-an-editable-network-graph-with-vis-network/105)\n- [Yjs Diagram Collaboration (Synergy Codes)](https://www.synergycodes.com/yjs)\n- [SvelteKit WebSocket App](https://www.inngest.com/blog/building-a-realtime-websocket-app-using-sveltekit)\n"}