mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-11-14 20:20:12 +00:00
80 lines
18 KiB
Markdown
80 lines
18 KiB
Markdown
# Summary
|
||
|
||
Self-hosted LiveSync は、ノートをローカルの Markdown ファイルとして保存する Obsidian [@obsidian] 向けのオープンソース同期プラグインである。セルフホスト型ストレージまたは直接のピアツーピア接続を介し、ユーザーの Vault(ノートや添付ファイルを収めたディレクトリー)をデスクトップとモバイルデバイス間で複製する。
|
||
|
||
ユーザーはオフラインで編集でき、同じノートに並行した編集があっても、再接続時に同期できる。即座の解決を強制したり変更を無条件に上書きしたりするのではなく、本プラグインはファイル形式やポリシーに応じ、編集箇所が重ならない変更を自動マージするか、後からレビューできるよう競合する版を保持し、あわせて競合の分析や、残存コピーがある場合の復旧に用いる検査ツールを備える。
|
||
|
||
本ソフトウエアは、自ら管理するストレージの下、複数デバイスでのノート作成を必要とする研究者、エンジニア、および実務者に対応する。
|
||
|
||
# Statement of Need
|
||
|
||
研究やエンジニアリングのワークフローは、長期間蓄積されるノート、観察記録、設計記録、および添付ファイルに依存している。著者の業務では、各デバイスの導入ソフトウエアを管理し、自ら管理するインフラ上でファイルを保持し、かつ運用実績のあるサーバーソフトウエアを用いる必要があった。これらの制約が動機となり、外部クライアントデーモンを用いず、デスクトップとモバイルの Obsidian 内で直接動作する同期エンジンを開発した。
|
||
|
||
デバイスが切断された状態で並行して行われた編集は、更新が交換された際にのみ表面化する。意図しない編集や削除、分岐した変更、あるいは外部による変更がデータベースイベントの外で起こり得るため、競合するリビジョンを保持せずに単一の版で上書きすると、レビュー前に情報が失われる恐れがある。
|
||
|
||
フィールドワークやモバイルでの利用中、ユーザーは競合をレビューする前にノートを記録して転送することが多い。Self-hosted LiveSync は記録と競合解決を分離する。並行ブランチがレビューされるか、ポリシーによって解決されるまで未解決のままでも、複製は継続する。
|
||
|
||
# State of the Field
|
||
|
||
ローカルファーストソフトウエアは、ホスト型サービスをデータの唯一の所有者とすることなく、ローカルにおける可用性と複数デバイス間での同期を両立させる [@kleppmann2019localfirst]。Obsidian 向けには、Obsidian Sync がホスト型サービスを提供し [@obsidiansync]、Obsidian Git が Git ワークフローを提供し [@obsidiangit]、Syncthing がファイルシステムツリーを同期し [@syncthing]、Remotely Save がクラウドやセルフホスト型ストレージの API に接続する [@remotelysave]。
|
||
|
||
これらのツールは並行性を異なる方法で表現する。Syncthing は競合コピーを通常のファイルとして伝搬させ [@syncthingsync]、Git は分岐した履歴をマージ前に追跡ブランチへ取得し [@gitfetch](Obsidian Git により自動化されている [@obsidiangit])、Automerge などの CRDT は並行したプロパティー代入を保持する [@automergeconflicts]。Self-hosted LiveSync は、競合するファイルの各版を、メタデータのリビジョンツリー上の leaf(分岐した各ブランチにおける現在の版)として、解決されるまで保持する。
|
||
|
||
ホスト型サービスは利便性を重視し、外部の同期ツールは生のファイルシステムを管理し、バージョン管理ツールは明示的なコミットを用いる。一方、著者の環境では管理対象デバイスでバックグラウンドクライアントデーモンが禁止されており、かつ競合するリビジョンとそれに伴うファイル更新を保持するには、複製とローカルファイル操作を直接結合する必要があった。そのため、Self-hosted LiveSync は分離されたプラットホーム非依存のエンジンを介し、統一されたリビジョンセマンティクスを保ちながら Obsidian のアプリケーション内で動作するよう実装された。
|
||
|
||
新しい複製アルゴリズムを提案するのではなく、Self-hosted LiveSync はリビジョン認識可能なデータベースのセマンティクスを外部から編集できる Vault へ適用し、Content-addressed なチャンク、デバイスローカルな来歴情報、および検査ツールを用いて、競合のレビューを保留しながら編集を支援する。
|
||
|
||
# Software Design
|
||
|
||
共通の複製サービス、競合処理サービス、および論理パス同一性サービスは、Obsidian プラグイン、コマンドラインインターフェース(CLI)、Web アプリケーション、および WebPeer 向けに `@vrtmrz/livesync-commonlib` [@commonlib040] として公開されており、論理名前空間およびパス比較の共有処理を、各ホスト環境が管理する物理ファイル操作から分離している。
|
||
|
||
## Revision-aware Vault representation
|
||
|
||
Vault の各ファイルは、ローカルの PouchDB [@pouchdb] 内にメタデータドキュメントとして保存される。このドキュメントは、パス、サイズ、更新日時に加え、既定では V3 の Rabin–Karp ローリングハッシュ分割処理で得られる内容由来のチャンクへの参照を記録し、未変更のチャンクをリビジョン間やファイル間で再利用する。並行する更新はメタデータドキュメントの周囲に競合する複数の leaf を形成するが、PouchDB の決定論的なデフォルト winner は、そのブランチがより新しい、より安全である、あるいは Vault に表示されていることを証明するものではない。
|
||
|
||
並行する更新によってブランチ $\alpha$ と $\beta$ に分岐した場合、両方の leaf は解決前に複製される。自動3方向マージは、両方の leaf と最も近い利用可能な共通祖先が読み取り可能である場合に、Markdown(`.md`)、Canvas(`.canvas`)、および JSON(`.json`)ファイルへ適用される(Markdown の同一オフセットへの挿入は更新日時で連結される)。CouchDB の複製は祖先の本文を伴わずに祖先識別子を転送するため [@couchdbreplication]、祖先の内容が欠落している場合や編集箇所が重なる場合は、自動マージを保留する。自動マージを行わない場合、読み取り可能な JSON および内容の異なるバイナリーの競合は「常に新しいファイルで上書きする」が無効であっても更新日時によって解決され、このオプションは更新日時による解決をテキストの競合へ拡張する。それ以外の場合、テキストの競合は手動の2方向差分(two-way diff)による解決を待つ。
|
||
|
||
## Device-local branch provenance
|
||
|
||
データベースは競合する複数のブランチを保持するが、Vault は一つのパスにつき単一のファイルを実体化する。データベースから Vault への反映、または Vault からデータベースへの書き込みの後、本プラグインはデータベースリビジョン、ローカルの更新日時、SHA-256 ハッシュ、およびバイト長をデバイスローカルなストアへ記録する。
|
||
|
||
この**ブランチアンカー**は、保存と競合の双方にわたって表示ブランチを追跡する。読み取り可能な記録済みリビジョン、または保存されたハッシュおよびサイズとの一致は、そのリビジョン本文が削除(prune)された後であっても Vault ファイルが未変更であることを示し、未編集のローカルファイルがより新しいデータベースの状態を上書きしないようにする。
|
||
|
||
ローカルのバイト列が読み取り可能な記録済みリビジョンと異なる場合、編集は競合する leaf を損なうことなくそのアンカーされたブランチを前進させる。読み取り可能な来歴や一致する内容の証拠がない場合、保存および反映は過去の祖先ではなく現在の非削除 leaf を確認する。一致が一つだけであれば来歴を復元し、複数一致する場合は表示ブランチを特定せずに重複書き込みを避け、一致がない場合はパスやタイムスタンプから推測せず独立した競合ブランチとして保持する。競合時、削除は証明された表示ブランチを論理削除し(証明できない場合は全ブランチを保持し)、同一のドキュメント ID に解決される大文字・小文字のみのリネームは表示ブランチをその場で前進させ、パスをまたぐリネームは移動先を書き込んだ上で証明された移動元ブランチのみを論理削除する。来歴はデバイスローカルな証拠であり、アトミックなトランザクションや欠落した祖先の代替ではない。保存が中断された後、テスト済みのシナリオでは、起動時の再実行(replay)が重複を収束させるか、並行する更新をマージまたはレビューへ委ねる。
|
||
|
||
コンフリクトインスペクターは、winner、全ての conflict leaf、および最も近い利用可能な共通祖先を検査し、変更操作の前にリビジョンを再確認することで、古い表示状態から、既に置き換えられたブランチを削除または前進させるのを防ぐ。読み取り専用の整合性診断(integrity diagnostic)は、Vault、ローカルデータベース、および CouchDB 間でメタデータと未解決の競合を比較し、未検査の内容と検証済みの内容を区別するとともに、任意の仮名化診断結果の出力に対応する。
|
||
|
||
## Transport-independent replication
|
||
|
||
CouchDB のリビジョンモデルを用い、Self-hosted LiveSync はデータベースの表現を通信トランスポートから分離し、メタデータのリビジョン ID と競合する leaf をそのまま複製する。CouchDB [@couchdb] はリビジョン認識複製を標準で提供し、Fast Setup は論理削除されたブランチを含む全ての競合 leaf を検証・保持した上で、条件を満たす新規取得を通常複製へ引き継ぐ。S3 互換ストレージは、新しいリビジョンを合成せずにメタデータ leaf のリビジョンと祖先識別子をジャーナルへ記録し、Content-addressed なチャンクを保存する。WebRTC ピアツーピア(P2P)アダプター [@webrtc] は、リビジョンセマンティクスを保持しつつ Trystero [@trystero] の DataChannels と RPC shim 上で要求を一括処理し、Web Streams が CouchDB およびジャーナル転送をパイプライン処理する。
|
||
|
||
同期するクライアントがパス同一性モードを共有する場合には各トランスポートを組み合わせることが可能であり、オフライン期間をまたいで P2P を CouchDB やオブジェクトストレージと併用できる。対応するファイルシステムホスト(Obsidian および CLI)では、任意の Unicode NFC パス同一性により、物理ファイル名の変更やチャンク ID の変更を行わずに、CouchDB、オブジェクトストレージ、および P2P にわたって合成形(NFC)と分解形(NFD)のパス表記を一つの論理パス同一性へそろえることができ、大文字・小文字の扱いは別のまま維持される [@livesyncnfc1046]。既存の環境は既定で設定済みのモードを維持し、従来互換のデータベースを NFC へ移行するには、衝突を解消した上で明示的な Rebuild とピアの Fetch を行う必要がある [@livesyncrecovery1046]。
|
||
|
||
全てのトランスポートは、エンドツーエンド暗号化、パス難読化、および任意の独立した ID 鍵をサポートする。必要な暗号化設定を有効にした CouchDB では、新規書き込み時に内部同期メタデータのプロパティーが暗号化される一方、ドキュメント ID、リビジョン構造、ドキュメント数、および P2P リレーのメタデータは観測可能なまま残る。
|
||
|
||
## Retention and recovery
|
||
|
||
分岐した各ブランチは未変更のチャンクを共有するため、競合する leaf を保持するストレージおよび転送コストは主に新規チャンクとリビジョンメタデータに限られる。リモートの再構築によって領域を回収できるほか、明示的に実行するベータ版のガベージコレクションワークフローにより、winner、全ての conflict leaf、および利用可能な競合の祖先から到達可能なチャンクを保護しながら CouchDB をインプレースで整理できる。置き換えられたチャンクは回収され得るため、過去のリビジョンは無条件のバックアップではない。
|
||
|
||
チャンクが欠落している場合、読み取り不能な現在のリビジョンは競合処理中もツリーに残り、残存コピーが再接続するまで復旧を保留する。同様に、S3 互換ジャーナルの受信においては、デコード、処理、またはローカル保存の失敗時にチェックポイントを進める前に複製を停止するため、保持されているリモートジャーナルの範囲内で再試行により失敗したジャーナルを再受信できる [@livesyncrecovery1046]。残存コピーが存在する場合、復旧ワークフローおよびコンフリクトインスペクターが再試行と復旧を支援する。
|
||
|
||
# Research Impact Statement
|
||
|
||
Self-hosted LiveSync は、主たるデバイスを利用できない状況でも複数デバイス間でスクリーンショットを取得し観察記録を保存する、著者のマルチプラットホーム開発業務から生まれた。現在では同じワークフローが著者の特許先行技術調査も支援しており、特許文献のレビュー中にノートや考察を同期しつつ、競合するリビジョンを即座に上書きせず後から比較するために保持している。
|
||
|
||
1.0.46 のテスト手順 [@livesynctest1046] は、リビジョンの系譜、来歴の復元、チャンクの到達可能性、利用できない内容、CLI および実環境の Obsidian における競合の伝搬と解決(編集、論理削除、リネーム、中断された保存、および起動時の整合処理)、ならびに3ノード P2P での leaf 転送を対象とし、あわせて CouchDB、オブジェクトストレージ、および P2P にわたる Unicode NFC の受け入れマトリックスと日付付きの Linux マイルストーン E2E 検証 [@livesyncnfc1046] を備える。1.0.46 の公開 CI [@livesyncci1046] は、Commonlib 0.4.0 [@commonlib040] を用いた自動化されたユニットテスト、契約テスト、および CLI/Docker テストスイートの成功を裏付けているが、1.0.46 における実環境 Obsidian マイルストーン E2E の再実行や、iOS/macOS の実機ファイルシステムでの検証を主張するものではない。アーカイブされたヘッドレス基盤 [@fancykit] と決定論的な試験用データにより P2P、CouchDB、およびチャンク分割経路を比較しているが、普遍的な性能を主張するものではない。
|
||
|
||
2026年9月2日時点で、Obsidian プラグインディレクトリーでは90万回以上のダウンロード、デスクトップおよびモバイル対応、ならびに Research カテゴリーへの配置が報告されており [@obsidianplugin]、GitHub では1万2200件以上のスター、440件のフォーク、およびコミュニティーからの貢献が記録されている [@selfhostedlivesyncrepo]。これらの数値は直接的な研究上の影響ではなくコミュニティーでの受容を示すものであるが、本ソフトウエアが単一の個人ワークフローを超えて運用されている証拠となっている。
|
||
|
||
本稿で説明するソフトウエアは Self-hosted LiveSync 1.0.46 [@selfhostedlivesync1046] であり、MIT ライセンスの下で公開され、Commonlib 0.4.0 [@commonlib040] に固定されている。Zenodo には、プラグイン [@selfhostedlivesync] および Commonlib [@commonlib] の以前のスナップショットが、テストハーネス [@fancykit] とともにアーカイブされている。
|
||
|
||
# AI Usage Disclosure
|
||
|
||
2026年7月から11月にかけて、コード探索、テストおよびベンチマークの足場作り、CI およびドキュメントの編集、ならびに原稿の起草およびレビューに OpenAI Codex(GPT-5 および GPT-6)を使用した。コミットには GitHub Copilot が補助として使用され、コードベース検証および原稿改訂には Google Gemini(Flash 3.5〜3.8 および Gemini 4)が使用された。著者が全ての出力を精査し、中核的な設計判断を行い、検証を実行しており、提出物の正確性、独創性、ライセンス、および倫理的コンプライアンスについて引き続き責任を負う。
|
||
|
||
# Acknowledgements
|
||
|
||
著者は、貢献者、ユーザー、ならびに PouchDB、CouchDB、および Trystero のメンテナーに感謝するとともに、GitHub Sponsors、JetBrains のツールライセンス、および OpenAI の Codex for Open Source プログラムに謝意を表する。
|
||
|
||
# References
|