- Published on
リアルタイム協業 & CRDT 2026 ディープダイブ — Liveblocks・PartyKit・Yjs・Automerge・Loro・ShareDB・Replicache・Fluid・Tldraw sync
- Authors

- Name
- Youngju Kim
- @fjvbn20031
プロローグ — 2026年、「一緒に編集する」とはどういうことか
2010年にGoogle Docsが一般ユーザーに「同じ文書を同時に編集する」を届けたとき、その技術はOT(Operational Transform)だった。ある人の編集操作を別の人の編集操作に合わせて「変換」する方式である。動作はしたが、サーバ中心であり、オフラインは難しく、誤った変換関数は永遠にデバッグを強いた。
2026年5月、風景は完全に逆転した。CRDT(Conflict-free Replicated Data Type) がウェブ協業の基本単位になった。Figmaのマルチプレイヤー基盤、Linearのローカルファースト同期、Notionのライブカーソル、tldrawのリアルタイムキャンバス、Excalidrawのルーム同期 — これらすべてがCRDTまたはその変種の上に成り立っている。
そしてさらに大きな変化が二つある。
- 2025年11月、VercelがLiveblocksとPartyKitを相次いで密接なパートナーとして取り込み、「Vercel for Realtime」というポジショニングを固めた。
- Ink & Switch研究所が発表した「Local-first software」のマニフェストはもはや学術的好奇心ではない。Automerge 2.x・Loro・Yrs(YjsのRustポート)が実サービスに乗った。
本稿は2026年のリアルタイム協業スタックを一気通貫で整理する。理論(CRDT vs OT、ベクタークロック、Lamport)からライブラリ比較(Yjs vs Automerge vs Loro)、SaaS比較(Liveblocks vs PartyKit vs Hocuspocus)、運用パターン(WebSocket vs WebRTC、awareness、永続化)まで。
第1章 · 同時編集の本質 — 同じ文書、別の時間線
まず問題を定義しよう。二人の人が同じ文書を同時に編集するというのは、計算機科学的には「同じ状態に二つの独立した操作が加わる」ことを意味する。
時刻 t0: "Hello" (サーバ = クライアントA = クライアントB)
時刻 t1: クライアントAが位置5に ", world" を挿入
時刻 t1: クライアントBが位置0に "Say: " を挿入
(互いに知らない)
時刻 t2: 二つの操作がサーバで出会う
-> 結果はどうあるべきか?
答えは二つある。OTは「二つの操作を互いの座標系に合うように変換する」。CRDTは「座標を最初から絶対的・一意のIDで打ち、操作をそのまま適用する」。
OTはサーバという単一の真実の源を仮定するが、CRDTはサーバ無しでもすべてのクライアントが最終的に同じ状態に収束する(これをeventual consistencyと呼ぶ)。ローカルファーストソフトウェアの前提条件は、まさにこれだ。
第2章 · CmRDT vs CvRDT — 操作か状態か
CRDTは大きく二種類に分かれる。
- CmRDT(Operation-based): 操作(operation)を伝播する。「位置5に 'x' を挿入」というメッセージを送る。メッセージサイズは小さいが、正確に一回配送(exactly-once) または因果順序(causal order)の保証が必要。
- CvRDT(State-based): 状態(state)全体または一部を伝播する。同期時にマージ関数が二つの状態を結合する。マージ関数は可換・結合・冪等(commutative, associative, idempotent) でなければならない。
YjsとAutomergeはどちらもop-basedで始まったが、2026年のLoro・Y-CRDT・Automerge 2.xはdelta-stateまたはハイブリッドモデルへ進化した。差分のみを送るが、冪等に適用できる形で。
| 種類 | メッセージサイズ | ネットワーク保証 | 代表的ライブラリ |
|---|---|---|---|
| CmRDT | 小 | 因果順序 + at-least-once | 初期 Treedoc, RGA |
| CvRDT | 大 | best-effort, 冪等 | 初期 Riak DT |
| Delta-state | 中 | 冪等 + 因果メタ | Yjs, Automerge 2.x, Loro |
第3章 · OT vs CRDT — なぜCRDTが勝ったのか
| 基準 | OT | CRDT |
|---|---|---|
| 中央サーバ要否 | 実質必要 | 不要(P2P可能) |
| オフライン編集 | 難しい | 自然 |
| 変換関数の検証 | 非常に困難(TP2問題) | 不要 |
| メモリ使用量 | 小 | 大(メタデータ蓄積) |
| テキストCRDTの成熟度 | 2000年代に検証済 | 2020年代に検証済 |
| 代表的な採用 | Google Docs(歴史的) | Figma, Linear, Notion ライブカーソル |
Google Docsは今もOTを使う(レガシー + 検証済みインフラ)。しかし2020年代以降の新規協業アプリはほぼすべてCRDTを選んだ。最大の理由はオフライン対応とP2P可能性である。
第4章 · ベクタークロックとLamport timestamp — 分散時間の二つの顔
分散システムには絶対時間が無い。そこで二つの道具が使われる。
- Lamport timestamp: すべてのイベントに単調増加する整数を付与する。二つのノードの時間を比較するにはmax + 1。全順序(total order)は作れるが、二つのイベントが同時(concurrent)かは分からない。
- Vector clock: ノード別カウンターをすべて持ち歩く。
[A:3, B:2, C:1]。二つのベクトルを比較すれば「先行/後行/同時」の三つから一つを確定できる。
Yjsは各クライアントが一意IDを持ち、ID + 単調シーケンスの組み合わせで操作を識別する(実質Lamport + client-id)。Automergeはアクター(actor) IDとシーケンス番号の組み合わせ(ベクタークロックの変種)を使う。Loroもactor + counterモデル。
第5章 · テキストCRDTの理論 — Yjs Y.Textの内部
テキストはCRDTの中で最も難しい。「位置5に 'x' を挿入」というインデックスベースの操作は同時編集で崩れる(他人が位置4に何か入れたら位置5は位置6になる)。だからCRDTは一意ID基盤の位置指定を使う。
YjsのY.Textはすべての文字に (clientID, clock) ペアを付与し、それを双方向連結リスト(double-linked list of Items) で繋ぐ。挿入は「このIDの次に入れろ」となり、削除はtombstone(削除マーク)として残る。同位置の同時挿入はclientIDで決定論的順序を作る。
// Yjs: 同じ文書を二人で編集する
import * as Y from 'yjs'
// クライアントA
const docA = new Y.Doc()
const textA = docA.getText('content')
textA.insert(0, 'Hello')
// クライアントB(オフラインで)
const docB = new Y.Doc()
const textB = docB.getText('content')
Y.applyUpdate(docB, Y.encodeStateAsUpdate(docA))
textB.insert(5, ', world')
// 同時に、クライアントAでも
textA.insert(0, 'Say: ')
// 両方の差分を相互に伝達
Y.applyUpdate(docA, Y.encodeStateAsUpdate(docB))
Y.applyUpdate(docB, Y.encodeStateAsUpdate(docA))
// 二つの文書が同じ状態へ収束する
console.log(textA.toString()) // "Say: Hello, world"
console.log(textB.toString()) // "Say: Hello, world"
このコードの核は encodeStateAsUpdate と applyUpdate である。どの順序で、何回、どの経路で適用しても結果は同じ。冪等で、可換で、結合的だ。
第6章 · Yjsの内部構造 — Itemリンクドリストと GC
Yjsの中心的データ構造は Item である。各Itemは:
id: { client: number, clock: number }— 一意識別子origin: ID | null— 「このItemはどのItemの次に挿入されたか」rightOrigin: ID | null— 同時挿入衝突解決用content: ContentString | ContentEmbed | ...— 実際の値deleted: boolean— tombstoneフラグ
メモリ爆発を防ぐためYjsはGCを行う。すべてのクライアントが見届けた削除は実際にtombstoneから消去される。また隣接する同一クライアントのItemをブロック単位で圧縮する(例: 一度に 'Hello' と打った5個のItemを一つに)。
ベンチマークではYjsは一貫して最速のテキストCRDTの一つだ。100万文字の文書でもメモリ・CPUが実用水準に収まる。
第7章 · Automerge — JSON CRDTの標準
AutomergeはYjsとほぼ同時期に始まったが、JSON文書全体をCRDTとして扱うことに焦点を当てる。テキストも対応するが(Automerge.Text → 最近はsplice ベースAPI)、強みは深くネストしたオブジェクトツリーのマージだ。
// Automerge: 同じJSON文書を二人で編集する
import { from, change, save, load, merge } from '@automerge/automerge'
// 初期文書
let doc1 = from({
title: 'Realtime collab notes',
todos: [{ done: false, text: 'Write CRDT post' }],
})
// シリアライズして他クライアントへ
const binary = save(doc1)
let doc2 = load(binary)
// 両側で同時編集
doc1 = change(doc1, (d) => {
d.todos.push({ done: false, text: 'Add references' })
d.title = 'Realtime collab notes (draft)'
})
doc2 = change(doc2, (d) => {
d.todos[0].done = true
})
// 両方向にマージ
const merged1 = merge(doc1, doc2)
const merged2 = merge(doc2, doc1)
// 二つの結果は同一
console.log(JSON.stringify(merged1) === JSON.stringify(merged2)) // true
Automerge 2.xはRustで書き直され(automerge-rs)、WASMでブラウザ・Node・Electronすべてで走る。メモリ・速度が1.x比で一桁向上した。
第8章 · Loro — 2024-2026のダークホース
Loroは中国発の新興CRDTライブラリで、2024年に1.0を達成し、2026年にはYjs・Automergeと並んで「ビッグ3」に数えられる。特徴は:
- 最初からRust + WASM
- タイムトラベル(time travel) を一級機能に — 任意時点の文書状態へ巻き戻し
- リッチテキストマークCRDT(スタイル範囲)を別データ構造で精密に処理
- ツリーCRDT(親子関係の同時移動)を真剣に扱う
ベンチマークによればLoroは特定のワークロード(特にツリー・リッチテキスト)でYjsより速い。一方Yjsはテキスト単純編集・エコシステム・教材で依然優位。
第9章 · Liveblocks — CRDT-as-a-Serviceの第一人者
Liveblocks(パリ拠点)は「マルチプレイヤー基盤をSaaSで」という命題を最初に捉えた。2026年時点の主機能:
LiveObject・LiveList・LiveMap— 独自CRDT(Yjsとは別)useStorage/useMutation— Reactフックで状態同期- Awareness/Presence — カーソル・名前・選択範囲のリアルタイム共有
- Comments・Threads — 文書上にコメントを付ける別モジュール
- Yjsアダプタ — 既存Yjsアプリ(Tiptap・BlockNote・Lexical Yjs)もLiveblocksトランスポートで同期可能
// Liveblocks: useStorageフックで共有状態を読み書き
import { useMutation, useStorage } from '@liveblocks/react/suspense'
function Todos() {
const todos = useStorage((root) => root.todos)
const addTodo = useMutation(({ storage }, text: string) => {
const list = storage.get('todos')
list.push({ id: crypto.randomUUID(), text, done: false })
}, [])
const toggle = useMutation(({ storage }, id: string) => {
const list = storage.get('todos')
const idx = list.findIndex((t) => t.id === id)
if (idx >= 0) {
const item = list.get(idx)
list.set(idx, { ...item, done: !item.done })
}
}, [])
return (
<ul>
{todos.map((t) => (
<li key={t.id} onClick={() => toggle(t.id)}>
{t.done ? 'X' : 'O'} {t.text}
</li>
))}
</ul>
)
}
Liveblocksの強みは運用の簡潔さとReactに最適化されたDX。弱点は価格(席単位・MAU単位課金)と独自CRDTへのロックイン(完全な自己ホスティングは難しい)。
第10章 · PartyKit — Cloudflare Durable Objects上の協業
PartyKit(2023年Sunil Pai創業、2024年Cloudflare買収)は別の道を行く。各協業ルームが一つのDurable Objectで、その中に任意のコード(Yjs・Automerge・独自ロジック)を走らせる。
// PartyKit: サーバコード (server.ts)
import type * as Party from 'partykit/server'
import { onConnect } from 'y-partykit'
export default class YjsServer implements Party.Server {
constructor(readonly room: Party.Room) {}
async onConnect(conn: Party.Connection) {
// y-partykitがYjs同期を自動処理
return onConnect(conn, this.room, {
persist: { mode: 'snapshot' },
})
}
}
PartyKitの強みはユーザコードをエッジで実行できる点。ルーム単位で認証・権限・永続化・ゲームロジックを自由に書ける。弱点はCloudflareロックイン(2026年現在ではむしろ強みでもある — グローバルエッジ分散が無料)。
第11章 · 自己ホスティング — Hocuspocus + Yjs
コスト・ロックインが負担ならHocuspocusが答え。Tiptapチームが作ったYjsバックエンドで、Node.jsで動き、Postgres・SQLite・Redisで永続化する。
// Hocuspocusサーバ
import { Server } from '@hocuspocus/server'
import { Database } from '@hocuspocus/extension-database'
const server = new Server({
port: 1234,
extensions: [
new Database({
fetch: async ({ documentName }) => {
// DBからYjsバイナリをロード
const row = await db.documents.findUnique({ where: { name: documentName } })
return row?.state ?? null
},
store: async ({ documentName, state }) => {
// DBへ保存
await db.documents.upsert({
where: { name: documentName },
update: { state, updatedAt: new Date() },
create: { name: documentName, state },
})
},
}),
],
async onAuthenticate({ token, documentName }) {
// JWT検証など
if (!verifyToken(token)) throw new Error('Unauthorized')
return { userId: extractUserId(token) }
},
})
server.listen()
運用を自前で握れデータ主権が確保される。代わりにスケーリング・HA・モニタリングは自前。Notion・Linear級になると結局自社インフラを書くことになる。
第12章 · ShareDB — OT陣営の生存者
ShareDB(旧derby-share)はOTベースの自己ホスティングライブラリだ。Yjs以前の時代(2015年前後)の協業アプリで広く使われ、今もOTに慣れたチームでは現役だ。
- 文書をJSONで表現し、JSON OT操作を伝播
- MongoDB・Postgresに永続化
- テキストOT(
json0・json1タイプ)は検証済 - 弱点: P2P・オフラインがCRDTほど自然ではない
2026年の新規プロジェクトならCRDT(Yjs/Automerge/Loro)を選ぶのが一般的な推奨だが、既にShareDBで順調に動いているシステムを無理に置き換える理由は無い。
第13章 · Replicache・Zero — 「ローカルファースト同期エンジン」の別解
RocicorpのReplicache(2020-)と後継のZero(2025-)はCRDTではない。mutator方式の同期 + サーバ権威 + 楽観UIというモデルだ。
- クライアントが「mutator」関数を定義する(例:
addTodo(text)) - mutatorをローカルで即実行しUIを更新
- 同じmutatorリクエストをサーバへ送り、サーバで権威的に再実行
- サーバ結果が届いたらローカル結果が上書きされる(ロールバック可能)
CRDTが「複数の真実が最終的に収束」なら、Replicacheは「サーバが最終真実、クライアントは楽観的推測」。Linearがこのモデルで有名になった。
第14章 · Fluid Framework — Microsoftのエンタープライズ解
Fluid FrameworkはMicrosoft 365(Office Online・Loop)のリアルタイム協業エンジン。CRDTではないがシーケンスベースの独自同期モデルだ。
- SharedString・SharedMap・SharedTreeなどの分散データ構造(DDS)
- Azure Fluid Relay上で動くサービス
- 外部開発者には扱いにくく(公式SDKはある)、実質Microsoft内部エンジン
2026年に外部新規アプリがFluidを採用する可能性は低いが、Office連携・Microsoft Graph上で協業を組むなら知っておく価値がある。
第15章 · ホワイトボード協業 — TldrawとExcalidraw
tldraw(@steveruizok・@orta)はホワイトボード・ダイアグラムライブラリで、マルチプレイヤーsyncエンジンを自社開発した。内部的にはYjsに似たop-basedモデルだが、キャンバスデータ構造(シェイプツリー)に最適化されている。@tldraw/sync パッケージでReact + WebSocketサーバでルーム同期し、Cloudflare Durable Objects上で動くリファレンス実装があり、シェイプ単位の最終書き込み者優先(LWW)で描画ツールの意味論に合わせている。
一方Excalidrawは手描きホワイトボードの代名詞で、協業ルーム機能は暗号化されたFirebase Realtime Database上で動く。CRDTを正式には使わず、単純なLWWとクライアント側変更マージで処理する。意外と合理的だ。ホワイトボードは短いセッションの間に少数のシェイプを同時編集する。CRDTのメタデータオーバーヘッドが不要なドメインだ。
tldrawの教訓: CRDTはデータ構造によって異なる設計が必要だ。 テキストCRDT(Yjs Y.Text)とキャンバスCRDT(tldraw)は同じ理論の上に立っていても実装ディテールは全く異なる。すべての協業アプリがフルCRDTを敷く必要はないということ。
第16章 · Figmaのマルチプレイヤーアーキテクチャ
Figmaは2016-2017年に独自マルチプレイヤーエンジンを作った。CRDTの一変種で、ノード・属性単位でLWWを適用する。核は:
- ドキュメント = ノードツリー、各ノードは属性辞書(property bag)
- 各属性に最新変更の
(timestamp, clientID)を付与 - 衝突 = より大きなtimestamp勝利(LWW)、同点はclientIDの大きい方
- ツリー構造変更(親変更、順序変更)は別ツリーCRDTロジック
FigmaのエンジニアEvan Wallaceが2019年のブログでこのアーキテクチャを公開し、その後多くの協業アプリが類似パターンを採った。tldrawも一部影響を受けた。
第17章 · Google DocsとOTの歴史
Google Docsは2010年のWritely買収から始まり、同年に同時編集を正式リリースした。エンジンはJupiter(Google内部名)OT。核となるアイディア:
- すべての編集は (revision, operation) ペア
- クライアントはサーバにopを送り、サーバは他のopと変換して返す
- TP1(Transformation Property 1):
op1 \* (op2 transformed against op1) == op2 \* (op1 transformed against op2)が成立しなければならない
OTの変換関数を正確に書くのは悪名高く難しい。Googleが十数年かけて磨いたこのコードは2020年代の新規アプリには追従コストが高すぎて、業界はCRDTへ移動した。
第18章 · Presence / Awareness — 「誰がどこで何を見ているか」
協業のもう半分はpresenceだ。誰がオンラインで、どこにカーソルがあって、何を選択していて、何色で表示されるか。
Yjsは y-protocols/awareness で標準化した。CRDT本体とは別の短命状態(ephemeral state)だ。メタデータ:
clientID(Y.Docと共有)name、color、cursor、selection等の自由JSON- ハートビートで生存表示、切断時は自動クリーンアップ
LiveblocksはawarenessをファーストクラスシチズンとしてもたらしUseOthers()・useUpdateMyPresence()で公開する。PartyKitも独自broadcastで実装可能。どちらにせよpresenceは永続化しないのが原則 — ルームが閉じれば消える。
第19章 · WebSocket vs WebRTC — トランスポートの選択
CRDT同期のトランスポートは二つに分かれる。
| 基準 | WebSocket | WebRTC (DataChannel) |
|---|---|---|
| サーバ要否 | 必要 | NAT越え用のSTUN/TURNのみ |
| レイテンシ | サーバラウンドトリップ | 直通P2P(より速い場合あり) |
| 拡張性 | サーバがファンアウト | メッシュトポロジならN^2 |
| 永続化 | 自然 | サーバミラー必要 |
| ファイアウォール | 通常通る | TURN無しだとブロック可能性 |
| 標準ライブラリ | y-websocket, y-partykit | y-webrtc |
2026年の一般推奨: WebSocketを基本にしつつ、真のP2Pが必要なドメイン(セキュリティ、オフラインメッシュ)、またはサーバ負荷を分散したい時にWebRTCを補助で使う。
第20章 · CRDTライブラリ比較
| ライブラリ | 言語 | 強み | 弱み | 代表的採用 |
|---|---|---|---|---|
| Yjs | JS/TS(+Yrs Rustポート) | 成熟・エコシステム・テキスト性能 | 本体メモリやや大 | Tiptap, BlockNote, Lexical Yjs |
| Automerge | Rust/WASM | JSONツリーマージ強い | テキスト性能はYjsより弱い | PushPin, Trail Runner |
| Loro | Rust/WASM | リッチテキスト・ツリー・タイムトラベル | エコシステム新興 | 新規アプリ |
| Yjs + Yrs | Rustポート | Rustネイティブバックエンド | Yjsとのワイヤ互換は部分的 | サーバサイドYjs |
| Diamond Types | Rust | 単一テキストCRDT性能チャンピオン | JSON非対応 | ベンチマーク用 |
| sjs (Synchronized JS) | 実験的 | TypeScript親和 | 新興 | 研究用 |
第21章 · SaaS vs 自己ホスティング比較
| 基準 | Liveblocks | PartyKit | Hocuspocus(自己) | Replicache | Fluid (MS) |
|---|---|---|---|---|---|
| CRDT種類 | 独自 + Yjsアダプタ | Yjs/Automerge自由 | Yjs | 非CRDT(楽観的) | DDS(非CRDT) |
| ホスティング | SaaS | Cloudflareエッジ | セルフ | セルフまたはRocicorp | Azure |
| 価格 | MAUベース | 使用量ベース | インフラ費のみ | 使用量 + SaaS | Azure単価 |
| 自己ホスト可能 | 部分(Edge Storageオプション) | 困難(CF依存) | 完全 | Replicacheは可能 | 困難 |
| データ主権 | 米・EUオプション | グローバルエッジ | 自由 | 自由 | Azureリージョン |
| Awarenessファースト | はい | 自作 | はい | 該当なし | 部分 |
| Comments/Threadsモジュール | はい | 無し | 無し | 無し | Loopが別途 |
第22章 · 韓国・日本の事例 — Notion、カカオ、サイボウズ、Sansan
韓国の協業市場は興味深い。Notion韓国進出(2020) 以降、Notion APIと自社ビルド協業の比率が急速に増えた(Notionは独自CRDTライクなモデルを使うが公開資料は少ない)。カカオワーク / カカオトークのメッセージはCRDTではないが、カカオワークの掲示板・メモ協業はOTベース(公開発表)で、カカオエンタープライズが「リモート協業」を伸ばした時期に自社エンジンを作った。ネイバー Line Worksは文書・表・カレンダー協業を提供し、Lineの日本市場と統合され、日本のエンタープライズで大きなシェアを持つ。トス / トスペイメンツは内部協業ツール(アーカイブ・文書)でYjs + Tiptap組み合わせを採用した事例をカンファレンスで共有した。
日本も興味深い。サイボウズ kintoneはビジネスアプリビルダーのフォーム・レコード協業で、同じレコードを複数ユーザが同時編集するときOTベースの変換を適用する。サイボウズは長いOTノウハウを持つ会社として有名だ。Sansanは名刺クラウドで、名刺データOCR結果を複数ユーザが同時に補正するワークフローでCRDT適用を発表したことがある。楽天 RMSは楽天モールの店長向け協業ツールで、内部的に独自同期エンジンを持つ。Notion日本チームは英語圏と同じエンジンだが、日本語IMEとの協業衝突(変換中の文字)を扱うパッチが日本チームPM発表で公開された。
第23章 · CRDT導入意思決定マトリクス
いつCRDTが正解で、いつそうでないか。
| 状況 | 推奨 |
|---|---|
| テキスト同時編集(エディタ) | Yjs + Tiptap/BlockNote/Lexical-Yjs |
| ホワイトボード/キャンバス | tldraw sync または Yjs Y.Map |
| フォーム・テーブル(セル単位) | Yjs Y.Map + セル別キー、または ShareDB |
| ゲーム状態(時間決定的) | CRDTよりlock-stepまたは権威サーバ |
| チャットメッセージ | CRDT不要、append-onlyログ |
| 通知・リアルタイムカウンター | CRDT G-Counter(学術的だが)または Redis |
| オフライン最優先(ローカルファースト) | Automerge, Loro, Yjs IndexedDB persistence |
| サーバ権威が重要(決済・在庫) | Replicacheまたは伝統的トランザクション |
第24章 · 永続化戦略 — IndexedDB・Postgres・バイナリスナップショット
CRDTはどこかに保存される必要がある。
- クライアント側: Yjsの
y-indexeddb、AutomergeのIndexedDBアダプタ。オフライン編集後の再接続時に自動同期。 - サーバ側(Yjsバイナリ): Yjsは
encodeStateAsUpdateで全状態をバイナリとして保存する。Postgresbyteaカラム、S3オブジェクト、Redisなどどこでも可能。 - サーバ側(デルタログ): 各アップデートをログに蓄積し、定期的にスナップショットに圧縮。大容量文書に有利。
- ハイブリッド: スナップショット + その後のデルタ。ロード時にスナップショット + デルタ累積。
永続化の落とし穴はGCだ。すべてのクライアントが見届けたtombstoneは消せるが、「すべてのクライアント」をどう判断するかが難しい。Yjsは保守的にGCを行い、運用者が定期的に明示的圧縮を呼ぶこともできる。
第25章 · セキュリティ・権限・エンドツーエンド暗号化
協業SaaSのセキュリティモデルは三つに分かれる。
- サーバが平文を見る(最も一般的): Liveblocks・Hocuspocus標準モデル。権限チェック・検索・コメントモデレーションが楽。
- エンドツーエンド暗号化(E2EE): クライアントがYjsアップデートを暗号化してサーバへ送る。サーバはバイナリブロブのみを知り、意味を知らない。Excalidrawルームが類似モデル。弱点: 検索・コメント分析・サーバ側マージが不可能。
- 部分暗号化: コンテンツはE2EE、メタデータ(ルームメンバー、presence)は平文。折衷案。
権限は通常ルーム単位トークン + 操作単位検証の組み合わせ。誰がルームに入れるか(JWT)、入ってから何ができるか(Hocuspocusの onAuthenticate + beforeHandleMessage)。
第26章 · エピローグ — 2027年を見る
CRDTはもはや学術の領域を出て運用の領域にある。2027年に起こる変化を三つ。
- モバイル・ネイティブのファーストクラス市民化 — Yjs Swiftポート、Automerge Kotlin、Loro Flutterバインディングがすべて安定し、モバイル協業アプリが爆発する。ローカルファーストが本当に可能になる。
- AIエージェントが協業参加者になる — Cursor・Claude Codeが同じ文書を同時編集するもう一人の「ユーザ」になり、awarenessが人とAIを区別する。
- 標準化の始まり — IETF・W3CレベルでCRDTワイヤフォーマット標準化の議論に入る。Yjs・Automerge・Loroのワイヤ互換性はまだ無いが、「標準テキストCRDT」の輪郭が見え始める。
最大の教訓はシンプルだ。保存ボタンが消えた世界はすでに来ている。 残るは、その上に何を建てるかだ。
References
- Shapiro, Marc et al. "Conflict-free Replicated Data Types." INRIA Research Report (2011). https://hal.inria.fr/inria-00609399v1
- Kleppmann, Martin and Beresford, Alastair R. "A Conflict-Free Replicated JSON Datatype." IEEE TPDS (2017). https://arxiv.org/abs/1608.03960
- Kleppmann, Martin et al. "Local-first software: You own your data, in spite of the cloud." Onward! 2019, Ink & Switch. https://www.inkandswitch.com/local-first/
- Kleppmann, Martin. "Designing Data-Intensive Applications." O'Reilly (2017) — レプリケーションとCRDTの章。
- Yjs documentation. https://yjs.dev および https://docs.yjs.dev
- Automerge documentation. https://automerge.org および https://github.com/automerge/automerge
- Loro documentation. https://loro.dev および https://github.com/loro-dev/loro
- Liveblocks documentation. https://liveblocks.io/docs
- PartyKit documentation. https://docs.partykit.io
- Hocuspocus documentation. https://tiptap.dev/docs/hocuspocus
- ShareDB. https://github.com/share/sharedb
- Replicache and Zero. https://replicache.dev および https://zero.rocicorp.dev
- Fluid Framework. https://fluidframework.com
- tldraw sync. https://tldraw.dev/docs/sync
- Excalidraw collaboration. https://github.com/excalidraw/excalidraw
- Evan Wallace, "How Figma's multiplayer technology works." Figma blog (2019). https://www.figma.com/blog/how-figmas-multiplayer-technology-works/
- Diamond Types — Seph Gentle. https://github.com/josephg/diamond-types
- Kleppmann, Martin et al. "Moving Elements in List CRDTs." PaPoC 2020. https://martin.kleppmann.com/papers/list-move-papoc20.pdf
- Yjs internals: Kevin Jahns, "Are CRDTs suitable for shared editing?" https://blog.kevinjahns.de/are-crdts-suitable-for-shared-editing/