mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-11-15 20:50:12 +00:00
Tracked to v1.0.46; mentioning provenance improvements
This commit is contained in:
1 parent
c2e4e9198b
commit
ad8655a08b
4 files changed
+114
-67
No files matched your search
+4
-4
@@ -2,7 +2,7 @@
|
||||
|
||||
This directory contains a technical manuscript prepared in the format of the Journal of Open Source Software (JOSS) to document the design intent, architecture, and workflow context of Self-hosted LiveSync.
|
||||
|
||||
This document is not currently published as a formal journal paper; rather, it serves as an architectural overview explaining the project's background and replication model (describing Self-hosted LiveSync 1.0.23 pinned to Commonlib 0.1.21). If you reference or utilise Self-hosted LiveSync in academic research, laboratory workflows, or technical publications, citing the software via [CITATION.cff](../CITATION.cff) or this manuscript is greatly appreciated.
|
||||
This document is not currently published as a formal journal paper; rather, it serves as an architectural overview explaining the project's background and replication model (describing Self-hosted LiveSync 1.0.46 pinned to Commonlib 0.4.0). If you reference or utilise Self-hosted LiveSync in academic research, laboratory workflows, or technical publications, citing the software via [CITATION.cff](../CITATION.cff) (which records the Zenodo-archived release 1.0.38) or the version-specific entries in [paper.bib](paper.bib) is greatly appreciated.
|
||||
|
||||
## Contents
|
||||
|
||||
@@ -12,7 +12,7 @@ This document is not currently published as a formal journal paper; rather, it s
|
||||
|
||||
## Citing Self-hosted LiveSync
|
||||
|
||||
Please refer to the repository's [CITATION.cff](../CITATION.cff) file or the metadata recorded in [paper.bib](paper.bib) if you wish to cite this software in your research papers, technical reports, or presentations.
|
||||
Please refer to [CITATION.cff](../CITATION.cff) for the Zenodo-archived software record (version 1.0.38, DOI `10.5281/zenodo.23438598`), or to [paper.bib](paper.bib) when citing the specific release described in this manuscript (Self-hosted LiveSync 1.0.46 and Commonlib 0.4.0).
|
||||
|
||||
## Feedback and Contributions
|
||||
|
||||
@@ -26,5 +26,5 @@ If I have overlooked or misrepresented anyone's contribution, please let me know
|
||||
|
||||
本ディレクトリーには、Journal of Open Source Software(JOSS)の形式を想定し、Self-hosted LiveSync の設計意図やアーキテクチャー、および運用の背景をまとめた原稿を配置しています。
|
||||
|
||||
本稿は現時点で正式に出版された論文ではなく、プロジェクトの背景や同期モデルを整理した技術資料として作成されたものです(Commonlib 0.1.21 に固定された Self-hosted LiveSync 1.0.23 を基準としています)。もし学術研究、実験ノートの管理、あるいは技術レポート等で Self-hosted LiveSync を利用・言及される機会がありましたら、リポジトリーの [CITATION.cff](../CITATION.cff) や本稿を引用していただけますと幸いです。
|
||||
日本語版は翻訳メモリーの使用を想定した逐語訳的な参考訳として位置づけられており、技術的意味論の正確性は英語版を基準としています。
|
||||
本稿は現時点で正式に出版された論文ではなく、プロジェクトの背景や同期モデルを整理した技術資料として作成されたものです(Commonlib 0.4.0 に固定された Self-hosted LiveSync 1.0.46 を対象として説明しています)。もし学術研究、実験ノートの管理、あるいは技術レポート等で Self-hosted LiveSync を利用・言及される機会がありましたら、Zenodo アーカイブ版(1.0.38、DOI `10.5281/zenodo.23438598`)を記録したリポジトリーの [CITATION.cff](../CITATION.cff)、または本稿の対象版(Self-hosted LiveSync 1.0.46/Commonlib 0.4.0)を記録した [paper.bib](paper.bib) および本稿を引用していただけますと幸いです。
|
||||
日本語版は翻訳メモリーの使用を想定した逐語訳的な参考訳として位置づけられており、技術的意味論の正確性は英語版を基準としています。また、語彙においては、記者ハンドブックを参考にしています。
|
||||
+49
-8
@@ -9,14 +9,23 @@
|
||||
url = {https://doi.org/10.1145/3359591.3359737}
|
||||
}
|
||||
|
||||
@software{selfhostedlivesync1046,
|
||||
author = {{vorotamoroz} and {Self-hosted LiveSync Contributors}},
|
||||
title = {vrtmrz/obsidian-livesync: 1.0.46},
|
||||
version = {1.0.46},
|
||||
year = {2026},
|
||||
publisher = {GitHub},
|
||||
url = {https://github.com/vrtmrz/obsidian-livesync/releases/tag/1.0.46}
|
||||
}
|
||||
|
||||
@software{selfhostedlivesync,
|
||||
author = {{vorotamoroz} and {Self-hosted LiveSync Contributors}},
|
||||
title = {vrtmrz/obsidian-livesync: 1.0.23},
|
||||
version = {1.0.23},
|
||||
title = {vrtmrz/obsidian-livesync: 1.0.38},
|
||||
version = {1.0.38},
|
||||
year = {2026},
|
||||
publisher = {Zenodo},
|
||||
doi = {10.5281/zenodo.22247183},
|
||||
url = {https://doi.org/10.5281/zenodo.22247183}
|
||||
doi = {10.5281/zenodo.23438598},
|
||||
url = {https://doi.org/10.5281/zenodo.23438598}
|
||||
}
|
||||
|
||||
@software{commonlib,
|
||||
@@ -29,13 +38,13 @@
|
||||
url = {https://doi.org/10.5281/zenodo.22074979}
|
||||
}
|
||||
|
||||
@software{commonlib021,
|
||||
@software{commonlib040,
|
||||
author = {{vorotamoroz} and {livesync-commonlib Contributors}},
|
||||
title = {livesync-commonlib: Platform-independent replication and synchronisation engine for Self-hosted LiveSync},
|
||||
version = {0.1.21},
|
||||
version = {0.4.0},
|
||||
year = {2026},
|
||||
publisher = {npm},
|
||||
url = {https://registry.npmjs.org/@vrtmrz/livesync-commonlib/-/livesync-commonlib-0.1.21.tgz}
|
||||
url = {https://registry.npmjs.org/@vrtmrz/livesync-commonlib/-/livesync-commonlib-0.4.0.tgz}
|
||||
}
|
||||
|
||||
@software{fancykit,
|
||||
@@ -53,7 +62,39 @@
|
||||
title = {Self-hosted LiveSync source repository},
|
||||
year = {2026},
|
||||
url = {https://github.com/vrtmrz/obsidian-livesync},
|
||||
urldate = {2026-09-02}
|
||||
urldate = {2026-11-07}
|
||||
}
|
||||
|
||||
@misc{livesynctest1046,
|
||||
author = {{vorotamoroz} and {Self-hosted LiveSync Contributors}},
|
||||
title = {Self-hosted LiveSync test procedures (version 1.0.46)},
|
||||
year = {2026},
|
||||
url = {https://github.com/vrtmrz/obsidian-livesync/blob/1.0.46/test/README.md},
|
||||
urldate = {2026-11-07}
|
||||
}
|
||||
|
||||
@misc{livesyncnfc1046,
|
||||
author = {{vorotamoroz} and {Self-hosted LiveSync Contributors}},
|
||||
title = {Unicode path identity: Verification contract and limits (Self-hosted LiveSync 1.0.46)},
|
||||
year = {2026},
|
||||
url = {https://github.com/vrtmrz/obsidian-livesync/blob/1.0.46/docs/design_docs/unicode_path_identity.md#verification-contract-and-limits},
|
||||
urldate = {2026-11-07}
|
||||
}
|
||||
|
||||
@misc{livesyncci1046,
|
||||
author = {{vorotamoroz} and {Self-hosted LiveSync Contributors}},
|
||||
title = {GitHub Actions CI checks for Self-hosted LiveSync 1.0.46 (commit 3d1bcc3)},
|
||||
year = {2026},
|
||||
url = {https://github.com/vrtmrz/obsidian-livesync/commit/3d1bcc3498740c99761a60d1714e6c8f3e7d833e/checks},
|
||||
urldate = {2026-11-07}
|
||||
}
|
||||
|
||||
@misc{livesyncrecovery1046,
|
||||
author = {{vorotamoroz} and {Self-hosted LiveSync Contributors}},
|
||||
title = {Recovery and flag files: Unicode NFC migration and journal recovery procedures (Self-hosted LiveSync 1.0.46)},
|
||||
year = {2026},
|
||||
url = {https://github.com/vrtmrz/obsidian-livesync/blob/1.0.46/docs/recovery.md},
|
||||
urldate = {2026-11-07}
|
||||
}
|
||||
|
||||
@misc{obsidian,
|
||||
|
||||
+30
-27
@@ -1,76 +1,79 @@
|
||||
# Summary
|
||||
|
||||
Self-hosted LiveSync は、ローカルの Markdown ファイルとして文書を保存するノートアプリ Obsidian [@obsidian] 向けのオープンソース同期プラグインである。ユーザーが管理するストレージまたは直接のピアツーピア接続を介し、ノートや添付ファイルを収めたディレクトリー(Vault)をデスクトップとモバイルデバイス間で同期する。
|
||||
Self-hosted LiveSync は、ノートをローカルの Markdown ファイルとして保存する Obsidian [@obsidian] 向けのオープンソース同期プラグインである。セルフホスト型ストレージまたは直接のピアツーピア接続を介し、ユーザーの Vault(ノートや添付ファイルを収めたディレクトリー)をデスクトップとモバイルデバイス間で複製する。
|
||||
|
||||
本プラグインにより、ユーザーはオフラインで編集を行い、再接続後に同期できる。2台のオフライン端末で同一ノートを別々に編集した場合のように編集の衝突が生じても、即座の解決を強制したり競合する変更を無条件に上書きしたりすることはない。ファイル形式や設定されたポリシーに応じて、重複しない変更箇所の自動マージや、競合する版を後から比較・解決するための保持が可能である。組み込みの検査ツールは、競合や内容の欠落の調査を支援し、コピーが残っている場合の復旧を支援する。
|
||||
ユーザーはオフラインで編集でき、同じノートに並行した編集があっても、再接続時に同期できる。即座の解決を強制したり変更を無条件に上書きしたりするのではなく、本プラグインはファイル形式やポリシーに応じ、編集箇所が重ならない変更を自動マージするか、後からレビューできるよう競合する版を保持し、あわせて競合の分析や、残存コピーがある場合の復旧に用いる検査ツールを備える。
|
||||
|
||||
本ソフトウエアは、データの保存先を自ら管理しながら複数デバイスで記録を継続する必要がある研究者、エンジニア、および実務者のワークフローに対応する。
|
||||
本ソフトウエアは、自ら管理するストレージの下、複数デバイスでのノート作成を必要とする研究者、エンジニア、および実務者に対応する。
|
||||
|
||||
# Statement of Need
|
||||
|
||||
研究やエンジニアリングのワークフローは、長期間蓄積されるノート、観察記録、設計上の決定事項、および関連ファイルに依存している。著者の業務では、管理下にある各デバイスの導入ソフトウエアを制御し、業務ファイルを自身の管理下にあるインフラで扱い、運用実績のあるサーバーソフトウエアを採用する必要があった。これらの制約から、デスクトップとモバイル双方の Obsidian 内で直接動作し、外部クライアントデーモンを必要としない同期エンジンを開発した。
|
||||
研究やエンジニアリングのワークフローは、長期間蓄積されるノート、観察記録、設計記録、および添付ファイルに依存している。著者の業務では、各デバイスの導入ソフトウエアを管理し、自ら管理するインフラ上でファイルを保持し、かつ運用実績のあるサーバーソフトウエアを用いる必要があった。これらの制約が動機となり、外部クライアントデーモンを用いず、デスクトップとモバイルの Obsidian 内で直接動作する同期エンジンを開発した。
|
||||
|
||||
オフライン端末で別々に行った編集の競合は、変更を交換した際に認識される。意図しない編集や削除、並行した変更の乖離、あるいはデータベースの状態とは独立した外部ツールによるファイル変更も起こりうる。競合する版を保持せずに単一の版で上書きしてしまうと、ユーザーが変更を確認して判断する前に情報が失われるおそれがある。
|
||||
デバイスが切断された状態で並行して行われた編集は、更新が交換された際にのみ表面化する。意図しない編集や削除、分岐した変更、あるいは外部による変更がデータベースイベントの外で起こり得るため、競合するリビジョンを保持せずに単一の版で上書きすると、レビュー前に情報が失われる恐れがある。
|
||||
|
||||
フィールドワークやモバイルでの作業中、研究者や実務者は、競合する編集内容をレビューする前であっても、観察の記録とデバイス間でのノート転送を続ける必要がある。Self-hosted LiveSync は、このように記録と競合解決を分けて進める作業を支援する。並行ブランチが未解決のままでも複製を継続でき、競合する編集は、レビューまたは設定されたポリシーによって解決されるまで保護される。
|
||||
フィールドワークやモバイルでの利用中、ユーザーは競合をレビューする前にノートを記録して転送することが多い。Self-hosted LiveSync は記録と競合解決を分離する。並行ブランチがレビューされるか、ポリシーによって解決されるまで未解決のままでも、複製は継続する。
|
||||
|
||||
# State of the Field
|
||||
|
||||
ローカルファーストソフトウエアは、ユーザーデータの唯一の所有者としてのホスト型サービスへの依存を避けつつ、ローカルにおける可用性と、複数デバイス間での同期や協調を両立させる [@kleppmann2019localfirst]。Obsidian エコシステム内では、いくつかのツールが異なるアプローチから複数デバイス間の同期に対応している。Obsidian Sync は統合されたホスト型サービスを提供し [@obsidiansync]、Obsidian Git はバージョン管理指向の push/pull ワークフローを提供し [@obsidiangit]、Syncthing はファイルシステム層で動作し [@syncthing]、Remotely Save はクラウドやセルフホスト型ストレージの複数の API に Obsidian を接続する [@remotelysave]。
|
||||
ローカルファーストソフトウエアは、ホスト型サービスをデータの唯一の所有者とすることなく、ローカルにおける可用性と複数デバイス間での同期を両立させる [@kleppmann2019localfirst]。Obsidian 向けには、Obsidian Sync がホスト型サービスを提供し [@obsidiansync]、Obsidian Git が Git ワークフローを提供し [@obsidiangit]、Syncthing がファイルシステムツリーを同期し [@syncthing]、Remotely Save がクラウドやセルフホスト型ストレージの API に接続する [@remotelysave]。
|
||||
|
||||
これらのアプローチは、並行する変更の表現方法が異なる。Syncthing は競合コピーを通常のファイルとして他のデバイスへ転送し [@syncthingsync]、Git は分岐した履歴をマージ前に取得できる [@gitfetch]。Obsidian Git はデスクトップおよびモバイル上でこの操作を自動化している [@obsidiangit]。Conflict-free Replicated Data Types(CRDT)でも複数の選択肢を検査でき、Automerge は同じオブジェクトプロパティーへの並行した代入を保持する [@automergeconflicts]。Self-hosted LiveSync は、競合するファイルの各バージョンを、メタデータドキュメントのリビジョンツリー上の末端リビジョン(leaf、各分岐の現在の版)として保持し、設定されたポリシーまたはユーザーによる明示的な操作によって解決されるまで維持する。
|
||||
これらのツールは並行性を異なる方法で表現する。Syncthing は競合コピーを通常のファイルとして伝搬させ [@syncthingsync]、Git は分岐した履歴をマージ前に追跡ブランチへ取得し [@gitfetch](Obsidian Git により自動化されている [@obsidiangit])、Automerge などの CRDT は並行したプロパティー代入を保持する [@automergeconflicts]。Self-hosted LiveSync は、競合するファイルの各版を、メタデータのリビジョンツリー上の leaf(分岐した各ブランチにおける現在の版)として、解決されるまで保持する。
|
||||
|
||||
これらの既存ツールはそれぞれ異なる運用上の要請に応えている。ホスト型サービスは導入の平易さを重視し、外部のファイル同期ツールは任意のファイルシステムツリーを対象とし、バージョン管理ツールは明示的なコミットワークフローを前提としている。一方、著者の環境では管理対象デバイス上でバックグラウンドクライアントデーモンの実行が制限されており、かつ競合する版の保持と、その版に結び付いたファイル更新を扱うためには、複製処理とローカルファイル操作を直接統合する必要があった。そのため、Self-hosted LiveSync は外部デーモンを介さず Obsidian 内で直接動作するプラグインとして構築され、複数のバックエンドで同一のリビジョンセマンティクスを維持するために、中核ロジックをプラットホーム非依存のエンジンとして分離する構成が採用された。
|
||||
ホスト型サービスは利便性を重視し、外部の同期ツールは生のファイルシステムを管理し、バージョン管理ツールは明示的なコミットを用いる。一方、著者の環境では管理対象デバイスでバックグラウンドクライアントデーモンが禁止されており、かつ競合するリビジョンとそれに伴うファイル更新を保持するには、複製とローカルファイル操作を直接結合する必要があった。そのため、Self-hosted LiveSync は分離されたプラットホーム非依存のエンジンを介し、統一されたリビジョンセマンティクスを保ちながら Obsidian のアプリケーション内で動作するよう実装された。
|
||||
|
||||
Self-hosted LiveSync は新しいデータベース複製アルゴリズムを導入するものではない。むしろその貢献は、リビジョン認識可能なデータベースのセマンティクスを、外部から編集できるファイル Vault へ適用した点にある。Content-addressed なチャンク、デバイスローカルな来歴情報、および組み込みの復旧ツールにより、通常のノート作成ワークフローを損なうことなく、競合のレビューを保留しながら編集を継続できるようにしている。
|
||||
新しい複製アルゴリズムを提案するのではなく、Self-hosted LiveSync はリビジョン認識可能なデータベースのセマンティクスを外部から編集できる Vault へ適用し、Content-addressed なチャンク、デバイスローカルな来歴情報、および検査ツールを用いて、競合のレビューを保留しながら編集を支援する。
|
||||
|
||||
# Software Design
|
||||
|
||||
共通の複製サービスおよび競合処理サービスは `@vrtmrz/livesync-commonlib` [@commonlib021] として公開されており、Obsidian プラグイン、コマンドラインインターフェース(CLI)、Web アプリケーション、および Web Peer で利用されている。
|
||||
共通の複製サービス、競合処理サービス、および論理パス同一性サービスは、Obsidian プラグイン、コマンドラインインターフェース(CLI)、Web アプリケーション、および WebPeer 向けに `@vrtmrz/livesync-commonlib` [@commonlib040] として公開されており、論理名前空間およびパス比較の共有処理を、各ホスト環境が管理する物理ファイル操作から分離している。
|
||||
|
||||
## Revision-aware Vault representation
|
||||
|
||||
Vault の各ファイルは、ローカルの PouchDB [@pouchdb] 内で、パス、サイズ、更新日時、および分割されたチャンクドキュメントへの参照を含むメタデータドキュメントとして表現される。チャンクは Content-addressed であり、同一のコンテンツ領域を持つリビジョン間や異なるファイル間で再利用できる。複数デバイス間で並行して更新が行われると、メタデータドキュメントの周囲に競合する複数の leaf(子を持たない末端リビジョン)が形成される。PouchDB はデフォルトの取得対象として決定論的な winner(選出された leaf)を選出するが、この選択は内部的なタイブレークに過ぎず、その winner がより新しい、より安全である、あるいは特定のデバイスの Vault に表示されているバージョンであることを証明するものではない。
|
||||
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)によって直接比較できる。
|
||||
並行する更新によってブランチ $\alpha$ と $\beta$ に分岐した場合、両方の leaf は解決前に複製される。自動3方向マージは、両方の leaf と最も近い利用可能な共通祖先が読み取り可能である場合に、Markdown(`.md`)、Canvas(`.canvas`)、および JSON(`.json`)ファイルへ適用される(Markdown の同一オフセットへの挿入は更新日時で連結される)。CouchDB の複製は祖先の本文を伴わずに祖先識別子を転送するため [@couchdbreplication]、祖先の内容が欠落している場合や編集箇所が重なる場合は、自動マージを保留する。自動マージを行わない場合、読み取り可能な JSON および内容の異なるバイナリーの競合は「常に新しいファイルで上書きする」が無効であっても更新日時によって解決され、このオプションは更新日時による解決をテキストの競合へ拡張する。それ以外の場合、テキストの競合は手動の2方向差分(two-way diff)による解決を待つ。
|
||||
|
||||
## Device-local branch provenance
|
||||
|
||||
データベースは競合する複数のブランチを同時に保持できるが、ローカルの Vault は任意のパスに対して単一の実体ファイルしか配置できない。ローカルファイルがどのブランチを表しているかを識別するため、本プラグインは正確なデータベースリビジョンと観測されたローカルの更新日時をデバイスローカルな Key-Value ストアに保存する。このリビジョンは当該パスの**ブランチアンカー**として機能し、データベースから Vault への実体化、または Vault からデータベースへの書き込みが成功した後に更新される。
|
||||
データベースは競合する複数のブランチを保持するが、Vault は一つのパスにつき単一のファイルを実体化する。データベースから Vault への反映、または Vault からデータベースへの書き込みの後、本プラグインはデータベースリビジョン、ローカルの更新日時、SHA-256 ハッシュ、およびバイト長をデバイスローカルなストアへ記録する。
|
||||
|
||||
未解決の競合が存在する状態において、ローカルで行われた編集や論理削除はアンカーされたリビジョンの子となり、競合する leaf を損なうことなく、その特定のブランチを前進させる。パスをまたぐリネームでは、移動先を保存した上で、アンカーされた移動元のブランチのみを論理削除する。この状態で来歴情報が利用できない場合、本プラグインはファイルのバイト列が利用可能な既存の単一リビジョン本文と厳密に一致する場合に限り Vault 内のファイルをそのリビジョンにひもづけ、それ以外の場合はパスや日時から勝手に推測せず、手動解決すべき競合として保持する。競合が存在しない通常時は、通常の書き込みによって単に現在のデータベースリビジョンが前進する。
|
||||
この**ブランチアンカー**は、保存と競合の双方にわたって表示ブランチを追跡する。読み取り可能な記録済みリビジョン、または保存されたハッシュおよびサイズとの一致は、そのリビジョン本文が削除(prune)された後であっても Vault ファイルが未変更であることを示し、未編集のローカルファイルがより新しいデータベースの状態を上書きしないようにする。
|
||||
|
||||
組み込みのコンフリクトインスペクターは、現在の winner、すべての conflict leaf、および最も近い利用可能な共通祖先を検査する。インスペクターは欠落したチャンクやファイル/データベース間の差異を報告し、現在の leaf を明示的に選択して操作できるようにする。変更を伴う操作は実行前にリビジョンを再確認し、古い画面状態によってすでに末端ではなくなったリビジョンを誤って削除したり前進させたりするのを防止する。
|
||||
ローカルのバイト列が読み取り可能な記録済みリビジョンと異なる場合、編集は競合する leaf を損なうことなくそのアンカーされたブランチを前進させる。読み取り可能な来歴や一致する内容の証拠がない場合、保存および反映は過去の祖先ではなく現在の非削除 leaf を確認する。一致が一つだけであれば来歴を復元し、複数一致する場合は表示ブランチを特定せずに重複書き込みを避け、一致がない場合はパスやタイムスタンプから推測せず独立した競合ブランチとして保持する。競合時、削除は証明された表示ブランチを論理削除し(証明できない場合は全ブランチを保持し)、同一のドキュメント ID に解決される大文字・小文字のみのリネームは表示ブランチをその場で前進させ、パスをまたぐリネームは移動先を書き込んだ上で証明された移動元ブランチのみを論理削除する。来歴はデバイスローカルな証拠であり、アトミックなトランザクションや欠落した祖先の代替ではない。保存が中断された後、テスト済みのシナリオでは、起動時の再実行(replay)が重複を収束させるか、並行する更新をマージまたはレビューへ委ねる。
|
||||
|
||||
コンフリクトインスペクターは、winner、全ての conflict leaf、および最も近い利用可能な共通祖先を検査し、変更操作の前にリビジョンを再確認することで、古い表示状態から、既に置き換えられたブランチを削除または前進させるのを防ぐ。読み取り専用の整合性診断(integrity diagnostic)は、Vault、ローカルデータベース、および CouchDB 間でメタデータと未解決の競合を比較し、未検査の内容と検証済みの内容を区別するとともに、任意の仮名化診断結果の出力に対応する。
|
||||
|
||||
## Transport-independent replication
|
||||
|
||||
CouchDB のリビジョンモデルを基準に、Self-hosted LiveSync はデータベースの表現を通信トランスポートから分離し、バックエンドにかかわらずファイルメタデータのリビジョン識別子と競合する leaf をそのまま複製する。CouchDB [@couchdb] ではネイティブなリビジョン複製を利用する。S3 互換オブジェクトストレージでは、メタデータドキュメントの末端リビジョンと祖先リビジョンの識別子をジャーナルに記録し、新しいローカルリビジョンを作成せずに適用する一方、チャンクドキュメントは内容由来の識別子を保持し、新しいローカルリビジョンとして保存される。WebRTC ピアツーピア(P2P)アダプター [@webrtc] は、Trystero [@trystero] の DataChannels と RPC ベースのレプリケーション shim によりドキュメント要求をバッチ処理し、同一のリビジョンセマンティクスをピア間で直接保持する。CouchDB およびジャーナル転送においては、Web Streams が転送をパイプライン処理し、転送中にメモリーへ保持されるデータ量を抑制する。
|
||||
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 やオブジェクトストレージを併用することで、オフライン期間を挟んだデバイス間でも同期できる。すべての通信方式でコンテンツのエンドツーエンド暗号化とパス難読化をサポートしている。P2P では接続交渉時のセッション記述が暗号化されるが、シグナリングリレーやネットワークサービスからは接続時刻やネットワークアドレスを観測できる。
|
||||
同期するクライアントがパス同一性モードを共有する場合には各トランスポートを組み合わせることが可能であり、オフライン期間をまたいで P2P を CouchDB やオブジェクトストレージと併用できる。対応するファイルシステムホスト(Obsidian および CLI)では、任意の Unicode NFC パス同一性により、物理ファイル名の変更やチャンク ID の変更を行わずに、CouchDB、オブジェクトストレージ、および P2P にわたって合成形(NFC)と分解形(NFD)のパス表記を一つの論理パス同一性へそろえることができ、大文字・小文字の扱いは別のまま維持される [@livesyncnfc1046]。既存の環境は既定で設定済みのモードを維持し、従来互換のデータベースを NFC へ移行するには、衝突を解消した上で明示的な Rebuild とピアの Fetch を行う必要がある [@livesyncrecovery1046]。
|
||||
|
||||
全てのトランスポートは、エンドツーエンド暗号化、パス難読化、および任意の独立した ID 鍵をサポートする。必要な暗号化設定を有効にした CouchDB では、新規書き込み時に内部同期メタデータのプロパティーが暗号化される一方、ドキュメント ID、リビジョン構造、ドキュメント数、および P2P リレーのメタデータは観測可能なまま残る。
|
||||
|
||||
## Retention and recovery
|
||||
|
||||
分岐した各ブランチは未変更のチャンクを共有するため、競合する leaf を保持するために生じるコストは主に新規チャンクとリビジョンメタデータに限られる。蓄積した保存領域はリモートデータベースの再構築によって回収できるほか、CouchDB 向けには、明示的に開始するベータ版のガベージコレクションにより、現在の winner、すべての conflict leaf、および未解決の競合を検査するために必要な、利用可能な祖先から到達可能なチャンクを保護しながらインプレースで回収できる。過去のリビジョンで置き換えられたチャンクは後から回収されうるため、過去のリビジョン本文は無条件のバックアップではない。
|
||||
分岐した各ブランチは未変更のチャンクを共有するため、競合する leaf を保持するストレージおよび転送コストは主に新規チャンクとリビジョンメタデータに限られる。リモートの再構築によって領域を回収できるほか、明示的に実行するベータ版のガベージコレクションワークフローにより、winner、全ての conflict leaf、および利用可能な競合の祖先から到達可能なチャンクを保護しながら CouchDB をインプレースで整理できる。置き換えられたチャンクは回収され得るため、過去のリビジョンは無条件のバックアップではない。
|
||||
|
||||
必要なチャンクが欠落している場合でも、読み取り不能な現在のリビジョンはリビジョンツリーに残り、競合処理によって自動的に破棄されることはない。欠落したチャンクが他のデバイスに残っている場合があるため、それらの再接続と同期を待って復旧操作を保留できる。競合インスペクターは影響を受けるリビジョンを明示し、取得の再試行や明示的な復旧操作を支援する。復旧には、デバイス、リモートストレージ、またはバックアップに内容が残っている必要がある。
|
||||
チャンクが欠落している場合、読み取り不能な現在のリビジョンは競合処理中もツリーに残り、残存コピーが再接続するまで復旧を保留する。同様に、S3 互換ジャーナルの受信においては、デコード、処理、またはローカル保存の失敗時にチェックポイントを進める前に複製を停止するため、保持されているリモートジャーナルの範囲内で再試行により失敗したジャーナルを再受信できる [@livesyncrecovery1046]。残存コピーが存在する場合、復旧ワークフローおよびコンフリクトインスペクターが再試行と復旧を支援する。
|
||||
|
||||
# Research Impact Statement
|
||||
|
||||
Self-hosted LiveSync は、著者が複数のデバイスやプラットホームを対象に行うソフトウエア開発業務から生まれた。この作業では、主たるデバイスを利用できない状況でも、各デバイスでスクリーンショットを取得し、観察記録を保存する必要があった。同じワークフローは、現在では著者の先行技術調査にも利用されており、先行文献の読解に伴うメモや考察を同期するために用いられている。競合する版が保持されることで、分岐した記録が即座に上書きされず、後から比較・確認することが可能になる。
|
||||
Self-hosted LiveSync は、主たるデバイスを利用できない状況でも複数デバイス間でスクリーンショットを取得し観察記録を保存する、著者のマルチプラットホーム開発業務から生まれた。現在では同じワークフローが著者の特許先行技術調査も支援しており、特許文献のレビュー中にノートや考察を同期しつつ、競合するリビジョンを即座に上書きせず後から比較するために保持している。
|
||||
|
||||
ユニットテストおよび結合テストは、リビジョンの系譜、チャンクの到達可能性、利用できない内容、およびホストの構成を対象とする。CLI および実環境の Obsidian によるシナリオでは、競合する leaf が残っている状態での編集、論理削除、およびリネームを含め、競合の伝播と解決を検証する。3ノードの P2P シナリオでは、未解決の leaf が解決前にデバイス間を移動できることを確認している。再利用可能なヘッドレステスト基盤は独立してアーカイブされている [@fancykit]。決定論的なフィクスチャーを用いて同一の生成データ上で P2P と CouchDB の経路を比較しているが、制御されたローカル測定値が普遍的な性能を示すわけではない。
|
||||
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 リポジトリーでは 12,200件以上のスター、440件のフォーク、および広範なユーザーコミュニティーからの貢献が記録されている [@selfhostedlivesyncrepo]。これらの数値自体は研究上の直接的な影響を証明するものではないが、本ソフトウエアがコミュニティーに受容され、単一のプライベートなワークフローを超えて運用されている証拠を提供する。
|
||||
2026年9月2日時点で、Obsidian プラグインディレクトリーでは90万回以上のダウンロード、デスクトップおよびモバイル対応、ならびに Research カテゴリーへの配置が報告されており [@obsidianplugin]、GitHub では1万2200件以上のスター、440件のフォーク、およびコミュニティーからの貢献が記録されている [@selfhostedlivesyncrepo]。これらの数値は直接的な研究上の影響ではなくコミュニティーでの受容を示すものであるが、本ソフトウエアが単一の個人ワークフローを超えて運用されている証拠となっている。
|
||||
|
||||
本稿で説明したソフトウエアは Self-hosted LiveSync 1.0.23 [@selfhostedlivesync] であり、MIT ライセンスの下でリリースされ、Commonlib 0.1.21 [@commonlib021] に固定されている。プラグイン、再利用可能なテストハーネス [@fancykit]、および以前の Commonlib 0.1.19 のスナップショット [@commonlib] は Zenodo に恒久的にアーカイブされており、プラットホーム非依存のロジックが独立したテストと再利用を可能にしている。
|
||||
本稿で説明するソフトウエアは Self-hosted LiveSync 1.0.46 [@selfhostedlivesync1046] であり、MIT ライセンスの下で公開され、Commonlib 0.4.0 [@commonlib040] に固定されている。Zenodo には、プラグイン [@selfhostedlivesync] および Commonlib [@commonlib] の以前のスナップショットが、テストハーネス [@fancykit] とともにアーカイブされている。
|
||||
|
||||
# AI Usage Disclosure
|
||||
|
||||
2026年7月から9月にかけて、コード探索、テストおよびベンチマークの足場作り、CI およびドキュメントの編集、原稿の推敲および校正、レビュー、引用の検証、ならびに結果の要約に GPT-5 を使用した OpenAI Codex が利用された。また、Codex を通じて GPT-6 も 9月の原稿レビューおよび改訂を支援した。本原稿の準備において、その他の生成 AI ツールは使用されていない。GitHub Copilot(モデルおよびバージョンは未記録)は、本リリースに含まれるコミットの実装、テスト、およびドキュメント作成を支援した。Google Gemini(Gemini Flash バージョン 3.5 から 3.8)は、リソースチェックおよび関連するコードベースの検証に使用された。人間の著者自身がすべての支援出力をレビュー、編集、および検証し、主要な設計判断を行い、関連する検証コマンドおよびベンチマークコマンドを実行した。著者は、提出された資料の正確性、独創性、ライセンス、および倫理的コンプライアンスについて引き続き全責任を負う。
|
||||
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 プログラムによる支援を受けている。
|
||||
著者は、貢献者、ユーザー、ならびに PouchDB、CouchDB、および Trystero のメンテナーに感謝するとともに、GitHub Sponsors、JetBrains のツールライセンス、および OpenAI の Codex for Open Source プログラムに謝意を表する。
|
||||
|
||||
# References
|
||||
|
||||
+31
-28
@@ -15,83 +15,86 @@ authors:
|
||||
affiliations:
|
||||
- name: 'Independent Researcher'
|
||||
index: 1
|
||||
date: 5 September 2026
|
||||
date: 7 November 2026
|
||||
bibliography: paper.bib
|
||||
---
|
||||
|
||||
# Summary
|
||||
|
||||
Self-hosted LiveSync is an open-source synchronisation plug-in for Obsidian [@obsidian], a note-taking application that stores documents as local Markdown files. It replicates a user's vault—a directory containing notes and attachments—across desktop and mobile devices using user-controlled storage or direct peer-to-peer connections.
|
||||
Self-hosted LiveSync is an open-source synchronisation plug-in for Obsidian [@obsidian], which stores notes as local Markdown files. It replicates a user's vault—a directory containing notes and attachments—across desktop and mobile devices via self-hosted storage or direct peer-to-peer connections.
|
||||
|
||||
The plug-in allows users to continue editing offline and synchronise upon reconnection, even when conflicting edits arise—such as when two disconnected devices modify the same note concurrently. Rather than forcing immediate reconciliation or unconditionally overwriting competing changes, the system supports automatic merging of non-overlapping edits and preserves competing versions for deferred review, depending on file formats and configured policies. Built-in inspection tools help users investigate conflicts or missing content and recover files when surviving copies exist.
|
||||
Users can edit offline and synchronise upon reconnection, even after concurrent edits to a note. Rather than forcing immediate reconciliation or unconditionally overwriting changes, the plug-in merges non-overlapping edits automatically or retains competing versions for deferred review by format and policy, with inspection tools for conflict analysis and recovery when surviving copies exist.
|
||||
|
||||
The software serves researchers, engineers, and practitioners who require continued note-taking across multiple devices while controlling their data storage.
|
||||
The software serves researchers, engineers, and practitioners requiring multi-device note-taking under self-managed storage.
|
||||
|
||||
# Statement of Need
|
||||
|
||||
Research and engineering workflows depend on long-lived notes, observations, design decisions, and supporting files. The author's work required managing the software installed on each device, keeping files on infrastructure under personal control, and using server software with an established operational record. These constraints motivated a synchronisation engine running directly inside Obsidian across desktop and mobile platforms without external client daemons.
|
||||
Research and engineering workflows rely on long-lived notes, observations, design records, and attachments. The author's work required managing the software installed on each device, keeping files on self-managed infrastructure, and using server software with an established operational record. These constraints motivated an in-app synchronisation engine running directly inside Obsidian across desktop and mobile platforms without external client daemons.
|
||||
|
||||
Conflicts arising from concurrent edits on disconnected devices are recognised only after devices exchange updates. An edit or deletion may be unintended, concurrent modifications may diverge, or external tools may update files independently of database events. Overwriting with a single version without retaining competing revisions risks irreversibly discarding information before users can evaluate the divergence.
|
||||
Concurrent edits on disconnected devices surface only when updates are exchanged. Because unintended edits or deletions, divergent changes, or external modifications can occur outside database events, overwriting with one version without retaining competing revisions risks discarding information before review.
|
||||
|
||||
During fieldwork and mobile operations, researchers and practitioners often need to continue recording observations and transferring notes between devices before reviewing competing edits. Self-hosted LiveSync supports this separation of recording and reconciliation: replication proceeds while concurrent branches remain unresolved, protecting competing edits until they can be reviewed or resolved according to configured policies.
|
||||
During fieldwork and mobile use, users often record and transfer notes before reviewing conflicts. Self-hosted LiveSync separates recording from reconciliation: replication continues while concurrent branches remain unresolved until reviewed or resolved by policy.
|
||||
|
||||
# State of the Field
|
||||
|
||||
Local-first software combines local availability with multi-device synchronisation and collaboration while avoiding dependence on a hosted service as the sole owner of user data [@kleppmann2019localfirst]. Within the Obsidian ecosystem, Obsidian Sync provides an integrated hosted service [@obsidiansync]; Obsidian Git provides version-control-oriented push and pull workflows [@obsidiangit]; Syncthing operates at the filesystem layer [@syncthing]; and Remotely Save connects Obsidian to several cloud and self-hosted storage APIs [@remotelysave].
|
||||
Local-first software combines local availability with multi-device synchronisation without a hosted service as sole data owner [@kleppmann2019localfirst]. For Obsidian, Obsidian Sync offers a hosted service [@obsidiansync], Obsidian Git provides Git workflows [@obsidiangit], Syncthing synchronises filesystem trees [@syncthing], and Remotely Save connects to cloud and self-hosted storage APIs [@remotelysave].
|
||||
|
||||
These approaches differ in how they represent concurrent changes. Syncthing propagates conflict copies as ordinary files [@syncthingsync], while Git can fetch divergent histories into separate tracking branches before merging them [@gitfetch], an approach automated on desktop and mobile by Obsidian Git [@obsidiangit]. Conflict-free replicated data types (CRDTs) can also expose alternatives: Automerge retains concurrent assignments to an object property for inspection [@automergeconflicts]. Self-hosted LiveSync retains competing file versions as leaves—the current versions of divergent branches—in the metadata document's revision tree until resolved by configured policies or explicit user action.
|
||||
These tools represent concurrency differently: Syncthing propagates conflict copies as ordinary files [@syncthingsync], Git fetches divergent histories into tracking branches before merging [@gitfetch] (automated by Obsidian Git [@obsidiangit]), and CRDTs such as Automerge retain concurrent property assignments [@automergeconflicts]. Self-hosted LiveSync retains competing file versions as leaves—current versions of divergent branches—in the metadata revision tree until resolved.
|
||||
|
||||
These existing tools address distinct operational needs: hosted services prioritise turnkey convenience, external file synchronisers manage arbitrary filesystem trees, and version-control tools introduce explicit commit workflows. In the author's environment, however, managed devices prohibited background client daemons, while retaining competing revisions alongside their associated file updates required integrating replication directly with local file operations. Self-hosted LiveSync was therefore implemented as an Obsidian plug-in running entirely within the application runtime, backed by a decoupled, platform-independent engine to maintain uniform revision semantics across backends.
|
||||
Hosted services prioritise convenience, external synchronisers manage raw filesystems, and version-control tools use explicit commits. In the author's environment, however, managed devices prohibited background client daemons, while retaining competing revisions alongside their associated file updates required coupling replication directly with local file operations. Self-hosted LiveSync was therefore implemented to run in-app inside Obsidian over a decoupled, platform-independent engine with uniform revision semantics.
|
||||
|
||||
Self-hosted LiveSync does not introduce a new database replication algorithm; rather, its contribution lies in applying revision-aware database semantics to an externally editable file vault. Content-addressed chunks, device-local branch provenance, and built-in recovery tools support continued editing while conflict review is deferred, protecting divergent work without altering standard note-taking workflows.
|
||||
Rather than proposing a new replication algorithm, Self-hosted LiveSync applies revision-aware database semantics to an externally editable vault, using content-addressed chunks, device-local provenance, and inspection tools to support editing while conflict review is deferred.
|
||||
|
||||
# Software Design
|
||||
|
||||
The shared replication and conflict-handling services are published as `@vrtmrz/livesync-commonlib` [@commonlib021] and used by the Obsidian plug-in, command-line interface (CLI), web application, and web peer.
|
||||
Shared replication, conflict-handling, and logical path-identity services are published as `@vrtmrz/livesync-commonlib` [@commonlib040] for the Obsidian plug-in, command-line interface (CLI), web application, and web peer, separating shared logical namespaces and path comparisons from host-owned physical file operations.
|
||||
|
||||
## Revision-aware Vault representation
|
||||
|
||||
Each Vault file is represented in local PouchDB [@pouchdb] by a metadata document containing its path, size, modification time, and references to separate chunk documents. Chunks are content-addressed and can therefore be reused across revisions and files with identical content regions. When concurrent updates occur across devices, they form multiple competing leaves around the metadata document. PouchDB selects a deterministic winner for default retrieval, but this choice is an internal tie-breaker rather than evidence that the winner is newer, safer, or the version represented by a particular device's Vault.
|
||||
Each Vault file is stored in local PouchDB [@pouchdb] as a metadata document recording its path, size, modification time, and references to content-derived chunks from the V3 Rabin–Karp rolling-hash splitter by default, reusing unchanged chunks across revisions and files. Concurrent updates form competing leaves around the metadata document; PouchDB's deterministic default winner does not prove that branch is newer, safer, or displayed in the Vault.
|
||||
|
||||
When concurrent updates diverge into branches $\alpha$ and $\beta$, both leaves replicate to other devices before resolution. Automatic three-way merging applies to Markdown (`.md`), Canvas (`.canvas`), and JSON (`.json`) files when both leaves and their nearest available shared ancestor are readable; other formats are excluded. In Markdown, concurrent insertions at the same offset concatenate sequentially by modification time. Because CouchDB replication transfers ancestry identifiers without ancestor content [@couchdbreplication], missing ancestral history or conflicting edits defer automatic merging. When automatic merging is disabled or inapplicable and both versions are readable, JSON and differing binary files resolve by modification time as a compatibility fallback, even when 'Always overwrite with a newer file' is disabled; enabling that option extends modification-time resolution to text conflicts. Otherwise, competing text versions remain for manual resolution, and can be compared via a two-way diff when both leaves are readable.
|
||||
When concurrent updates diverge into branches $\alpha$ and $\beta$, both leaves replicate before resolution. Automatic three-way merging applies to Markdown (`.md`), Canvas (`.canvas`), and JSON (`.json`) files when both leaves and their nearest available shared ancestor are readable (same-offset Markdown insertions concatenate by modification time). Because CouchDB replication transfers ancestry identifiers without ancestor bodies [@couchdbreplication], missing ancestor content or overlapping edits defer automatic merging. Without automatic merging, readable JSON and differing binary conflicts resolve by modification time even with 'Always overwrite with a newer file' disabled; that option extends modification-time resolution to text conflicts, which otherwise await manual two-way diff resolution.
|
||||
|
||||
## Device-local branch provenance
|
||||
|
||||
While the database can retain competing branches concurrently, a local Vault can instantiate only a single concrete file at any given path. To resolve which branch a local file represents, the plug-in stores an exact database revision and observed local modification time in a device-local key-value store. This revision serves as the path's **branch anchor**, updated after a successful database-to-Vault reflection or Vault-to-database write.
|
||||
While the database retains competing branches, a Vault instantiates one file per path. After database-to-Vault reflection or Vault-to-database writes, the plug-in records the database revision, local modification time, SHA-256 hash, and byte length in a device-local store.
|
||||
|
||||
During active conflicts, local edits or logical deletions become children of the anchored revision, advancing that branch while keeping competing leaves intact. Cross-path renames store the target before logically deleting only the anchored source branch. If provenance is unavailable, the plug-in binds a file to an existing revision only when its bytes match exactly one available revision body; otherwise, it retains the conflict for manual resolution rather than guessing from paths or timestamps. Without active conflicts, ordinary writes simply advance the database revision.
|
||||
This **branch anchor** tracks the displayed branch across saves and conflicts. Matching the readable recorded revision or saved hash and size shows a Vault file is unchanged—even after pruning that revision body—so unedited local files do not overwrite newer database state.
|
||||
|
||||
The built-in conflict inspector examines the current winner, every conflict leaf, and the nearest available shared ancestor. It reports missing chunks and file/database differences, permitting operations on an explicitly selected current leaf. Mutating operations recheck the revision beforehand, preventing a stale inspection from deleting or extending a superseded branch.
|
||||
When local bytes differ from the readable recorded revision, edits extend that anchored branch while keeping competing leaves intact. Without readable provenance or matching content evidence, saves and reflection check current non-deleted leaves rather than ancestors: a unique match reconstructs provenance, multiple matches avoid duplicate writes without identifying a displayed branch, and no match preserves an independent conflict branch rather than guessing from paths or timestamps. During conflicts, deletions logically delete the proven displayed branch (or retain all branches if unproven), case-only renames resolving to the same document ID extend the displayed branch in place, and cross-path renames write the target before logically deleting only the proven source branch. Provenance is device-local evidence, not an atomic transaction or missing-ancestor substitute; after an interrupted save, start-up replay converges duplicates or defers concurrent updates to merge or review in tested scenarios.
|
||||
|
||||
The conflict inspector examines the winner, every conflict leaf, and the nearest available shared ancestor, rechecking revisions before mutating actions so stale views cannot delete or extend superseded branches. A read-only integrity diagnostic compares metadata and active conflicts across the Vault, local database, and CouchDB, distinguishing uninspected from verified content with optional pseudonymised reporting.
|
||||
|
||||
## Transport-independent replication
|
||||
|
||||
Using CouchDB's revision model as a baseline, Self-hosted LiveSync decouples database representation from network transport, replicating file-metadata revision identifiers and competing leaves intact across backends. CouchDB [@couchdb] provides native revision-aware replication. S3-compatible storage journals metadata leaf revisions and ancestry identifiers without synthesising new revisions, while chunk documents are stored as new local revisions with content-derived identifiers. The WebRTC peer-to-peer (P2P) adapter [@webrtc] uses Trystero [@trystero] DataChannels and an RPC-based replication shim to batch document requests while preserving identical revision semantics directly between peers. In CouchDB and journal transfers, Web Streams pipeline data to limit the amount of data buffered in memory during transfer.
|
||||
Using CouchDB's revision model, Self-hosted LiveSync decouples database representation from transport, replicating metadata revision IDs and competing leaves intact. CouchDB [@couchdb] provides native revision-aware replication; Fast Setup verifies and retains all competing leaves—including logically deleted branches—and hands eligible fresh fetches to ordinary replication. S3-compatible storage journals metadata leaf revisions and ancestry identifiers without synthesising new revisions, storing content-addressed chunks. The WebRTC peer-to-peer (P2P) adapter [@webrtc] batches requests over Trystero [@trystero] DataChannels and an RPC shim while preserving revision semantics, and Web Streams pipeline CouchDB and journal transfers.
|
||||
|
||||
Transports combine flexibly: while P2P requires concurrent online presence, pairing it with CouchDB or object storage bridges offline intervals. All transports support end-to-end content encryption and path obfuscation. P2P encrypts session descriptions during connection negotiation, though signalling relays and network services can still observe connection timing and network addresses.
|
||||
Transports combine when synchronising clients share a path-identity mode, pairing P2P with CouchDB or object storage across offline intervals. On supported filesystem hosts (Obsidian and the CLI), optional Unicode NFC path identity equates composed (NFC) and decomposed (NFD) path spellings into one logical path identity across CouchDB, object storage, and P2P without renaming physical files or changing chunk IDs, while keeping letter-case handling separate [@livesyncnfc1046]. Existing setups retain their configured mode by default, and migrating a legacy database to NFC requires resolving collisions before an explicit rebuild and peer fetch [@livesyncrecovery1046].
|
||||
|
||||
All transports support end-to-end encryption, path obfuscation, and an optional independent ID key; on CouchDB with required encryption settings, new writes encrypt internal synchronisation metadata properties, while document IDs, revision structure, document counts, and P2P relay metadata remain observable.
|
||||
|
||||
## Retention and recovery
|
||||
|
||||
Because alternative branches share unchanged chunks, retaining competing leaves incurs storage and transfer costs primarily for new chunks and revision metadata. Remote database rebuilds reclaim space, while an explicitly initiated beta garbage-collection workflow provides in-place CouchDB cleanup by protecting chunks reachable from the current winner, every conflict leaf, and the available ancestry needed to inspect active conflicts. Because superseded chunks may be collected, historical revisions are not an unconditional backup.
|
||||
Because divergent branches share unchanged chunks, retaining competing leaves adds storage and transfer costs mainly for new chunks and revision metadata. Remote rebuilds reclaim space, while an explicit beta garbage-collection workflow cleans CouchDB in place by protecting chunks reachable from the winner, every conflict leaf, and available conflict ancestry; because superseded chunks may be collected, historical revisions are not an unconditional backup.
|
||||
|
||||
When chunks are missing, unreadable current revisions remain in the tree rather than being automatically discarded during conflict processing. Because missing chunks may still exist on other devices, users can defer recovery until they reconnect and synchronise. The conflict inspector identifies affected revisions and facilitates retrieval retries and explicit recovery actions. Recovery ultimately requires surviving content on a device, in remote storage, or in a backup.
|
||||
When chunks are missing, unreadable current revisions remain in the tree during conflict processing, deferring recovery until surviving copies reconnect. Likewise, during S3-compatible journal reception, decode, processing, or local-save failures halt replication before advancing the checkpoint so that retrying can re-receive the failed journal within the retained remote journals [@livesyncrecovery1046]. Recovery workflows and the conflict inspector assist retries and recovery when surviving copies exist.
|
||||
|
||||
# Research Impact Statement
|
||||
|
||||
Self-hosted LiveSync originated in the author's multi-platform software engineering workflows, capturing screenshots and recording observations across multiple devices, including when a primary device was unavailable. Today, the same workflow supports the author's patent prior-art investigations, synchronising notes and reflections made while reading prior patent literature. Retaining competing revisions allows divergent observations to be compared after the fact rather than overwritten immediately.
|
||||
Self-hosted LiveSync originated in the author's multi-platform software engineering workflows, capturing screenshots and recording observations across devices even when a primary device was unavailable. Today, the same workflow also supports the author's patent prior-art investigations, synchronising notes and reflections while reviewing patent literature and retaining competing revisions for later comparison rather than overwriting them immediately.
|
||||
|
||||
Unit and integration tests cover revision ancestry, chunk reachability, unavailable content, and host composition. CLI and real-Obsidian scenarios exercise conflict propagation and resolution, including edits, logical deletions, and renames while competing leaves remain active. A three-node P2P scenario verifies that unresolved leaves move between devices before resolution. Reusable headless test infrastructure is archived independently [@fancykit]. Deterministic fixtures compare P2P and CouchDB paths over identical generated data; controlled local measurements do not establish universal performance.
|
||||
Test procedures in 1.0.46 [@livesynctest1046] cover revision ancestry, provenance reconstruction, chunk reachability, unavailable content, CLI and real-Obsidian conflict propagation and resolution (edits, logical deletions, renames, interrupted saves, and start-up reconciliation), and three-node P2P leaf transfer, alongside the Unicode NFC acceptance matrix and dated Linux milestone E2E verification across CouchDB, object storage, and P2P [@livesyncnfc1046]. Public CI for 1.0.46 [@livesyncci1046] confirms automated unit, contract, and CLI/Docker suites with Commonlib 0.4.0 [@commonlib040], without claiming real-Obsidian milestone E2E reruns on 1.0.46 or native iOS/macOS filesystem validation. Archived headless infrastructure [@fancykit] and deterministic fixtures compare P2P, CouchDB, and chunking paths without claiming universal performance.
|
||||
|
||||
As of 2 September 2026, the Obsidian plug-in directory reported more than 900,000 downloads, desktop and mobile support, and placement in its Research category [@obsidianplugin]. The GitHub repository recorded over 12,200 stars, 440 forks, and contributions from a broad user community [@selfhostedlivesyncrepo]. These figures demonstrate community adoption rather than direct research impact, but they provide evidence that the software operates beyond a single private workflow.
|
||||
As of 2 September 2026, the Obsidian plug-in directory reported over 900,000 downloads, desktop and mobile support, and placement in its Research category [@obsidianplugin], with over 12,200 GitHub stars, 440 forks, and community contributions [@selfhostedlivesyncrepo]. These figures indicate community adoption rather than direct research impact, providing evidence that the software operates beyond a single private workflow.
|
||||
|
||||
The software described here is Self-hosted LiveSync 1.0.23 [@selfhostedlivesync], released under the MIT licence and pinned to Commonlib 0.1.21 [@commonlib021]. Zenodo archives the plug-in, the reusable test harness [@fancykit], and an earlier Commonlib 0.1.19 snapshot [@commonlib]. Platform-independent logic supports independent testing and reuse.
|
||||
The software described here is Self-hosted LiveSync 1.0.46 [@selfhostedlivesync1046], released under the MIT licence and pinned to Commonlib 0.4.0 [@commonlib040]. Zenodo archives earlier snapshots of the plug-in [@selfhostedlivesync] and Commonlib [@commonlib], alongside the test harness [@fancykit].
|
||||
|
||||
# AI Usage Disclosure
|
||||
|
||||
OpenAI Codex using GPT-5 was used from July to September 2026 for code navigation, test and benchmark scaffolding, CI and documentation edits, manuscript editing, proofreading, review, citation verification, and result summarisation. GPT-6 assisted with September manuscript review and revision through Codex. No other generative AI tools prepared the manuscript. GitHub Copilot assisted with commits in this release, and Google Gemini (Flash versions 3.5 to 3.8) supported codebase verification. The human author validated all assisted outputs, made core design decisions, ran verification commands, and remains responsible for the accuracy, originality, licensing, and ethical compliance of the submitted materials.
|
||||
OpenAI Codex (GPT-5 and GPT-6) was used from July to November 2026 for code navigation, test and benchmark scaffolding, CI and documentation edits, and manuscript drafting and review. GitHub Copilot assisted with commits, and Google Gemini (Flash 3.5–3.8 and Gemini 4) supported codebase verification and manuscript revision. The author validated all outputs, made core design decisions, ran verification, and remains responsible for the accuracy, originality, licensing, and ethical compliance of the submission.
|
||||
|
||||
# Acknowledgements
|
||||
|
||||
The author acknowledges project contributors, users, and upstream maintainers of PouchDB, CouchDB, and Trystero. The project has received community support through GitHub Sponsors, development-tool licensing from JetBrains, and support through OpenAI's Codex for Open Source programme.
|
||||
The author thanks contributors, users, and maintainers of PouchDB, CouchDB, and Trystero, alongside GitHub Sponsors, JetBrains tool licensing, and OpenAI's Codex for Open Source programme.
|
||||
|
||||
# References
|
||||
|
||||
Reference in new issue
Block a user