最近、Obsidianのプラグインを作りました。
その名も、obsidian-livesync。

ブログの更新、サボっていてすみません。
vorotamoroz aka きみのぶです。きみのぶつけました。

それはそうと最近、Obsidianのプラグインを作りました。
その名も、obsidian-livesync。

どんなプラグイン?

Obsidianのデータを、

  • セルフホストしたサーバを使って
  • LiveSyncする

プラグインです(直球)。

こんなの。
obsidian_live_sync_demo

きっかけ

「どんな」とだいたいイコールなのですが、きっかけは2つあります。

1つ目が、セルフホスト。
私がノートアプリで書く内容って、結構センシティブだったりして。とくにまだ出せない知財ネタなんかを書いてるときに、いくらE2Eの暗号化が行われてるとはいえ、自社が監査していない外部サーバにデータを預けることは難しかったりします。たとえ公式提供でも。

もう1つが、ライブシンク
私、プライベートでも仕事場でも、それぞれ数台の端末を同時に使用しています。
仕事する部屋ではMacとWindows、そして居間ではChromebookを使ってたりしますが、コロコロ端末変えるにあたって、移動した先では「さっき書いてた状態」がすぐに反映されててほしい。
OneNoteはこれができるんです。メチャクチャ便利。
なので、これをObsidianでもやりたい。

技術選定と実装

慣れてる技術を選びました。

凄く雑に考えまして。
僕の中でマルチマスターレプリケーションとなると、CouchDBとCouchDB互換のブラウザ用データベース、PouchDBが出てきます。ほぼ一択かなぁ。

このCouchDB&PouchDBというのが凄くて、データベース間の同期と、衝突の検知をほとんど何も書かなくてもやってくれます。あと、データベースへの変更をイベントで拾う、なんかも1。マルチマスターレプリケーションでです。すごい。

となると、次はノートへの変更をどうやってPouchDBに反映させていくかなのですが、これも簡単。なんとObsidian側からVault内での変更に関するあらゆるイベントが発報されます。なのでイベントハンドラーとして書くだけ2。(1)

そんな感じでPouchDBにデータが入ってくると、PouchDBはデータをライブレプリーションでサーバに随時転送します3(2)。

そしてサーバにデータが入ると、次は別の端末にライブレプリケーションされていきます4。これで、他の端末のPouchDBに、変更が反映されていきます(3)。

そうするとなんと。データベースへの変更をローカルに反映(4)するだけで、ライブシンクができてしまう。ローカルに反映したら、Obsidianに対して「ファイル作ったよ!」「ファイル変更したよ」とイベントを起こしてやれば、なんと表示が……更新されます。それも、キャレット位置は維持されたまま。

これを図にすると、こんな感じです。

図1

なんと、ここまで800行でできてしまいました。エグい。そもそも色々足した今だって2500行ぐらいしかない。
若干、沼りかけることはあっても、ストレートに実装できてしまいびっくり。仕組みは思いついていたとはいえ、その間およそ数時間。ライブラリ周りに関してはとても良い時代になったと思います。
また、TypeScriptも非常に使いやすく、これは感激しています。変数名をいったん雑にwやiとか使って、適宜に置換していますが、完璧ですね。噂通り型のサポートも厚く、割と無茶なデータ構造でも耐えてくれそうです。まだ慣れてなくて、まだまだよくわかんない型は書いてますが。

自分が工夫したところ

ほとんど乗っかって作れたのですが、流石に長いノートを編集してると重かったので、チャンクに分割して同期することに。
最初は長さを基準にしてたのですが、それだとファイル先頭の方を編集したときに増分がほとんど1ファイル分発生して辛い。
なので、markdownにありがちなデリミターを利用してチャンクを分割することにしました5。これで、普通にマークダウン書いてる分には多分いい感じに分割されてます。
また、チャンクのキーに、内容のcrc32を使うことによって、めちゃくちゃdedupeできてます。このcrc32はxxhash-wasmというライブラリを使用していて、これがまためちゃくちゃ早い。
CouchDBはまったく同じ内容のドキュメントは同期対象に入れないので、転送量をかなり絞ることができています。
この仕組みは少し遅れて作ったWebClipでも有効になっていて、Clipするときも同様、転送量が絞れます。

こんな感じですね。
図2

あと、衝突したらObsidian-livesync内でdiff見て反映できます。たまにミスりますが。
地味に便利。
ちなみに、スマホでも見れますしマージできます。
図2

サーバの選定

最初、完全にセルフホストのみを想定していたのですが、あまりにユーザを絞るかな…と思ったりして。
ちょっと前に、IBM Cloudantにつばをつけていたので、今回はメインに使うようにReadmeに書いてみました。
私も自分のVaultのうちのひとつに使ってますが、これはこれでめちゃくちゃ良いです。
あとは、Oracle Cloud Infrastructureにテスト用のサーバをこしらえました。Docker使って、めちゃくちゃハマったものの、最終的にはいい感じに動いてます。
なので、SaaS、クラウドサーバー、自宅サーバー、Raspberry piで検証している形になるのですが、まあ柔軟性のあるアーキテクチャになって良かったな、と思ったり。

最後に

Obsidianは神かな、と。

OSSでこういうものを公開するのがほぼはじめてで、なんかハラハラしていますし、英語はGrammaly様におんぶにだっこですが、まあ気長にメンテしつつ長く使っていこうと思ってます。

次はObsidian自体の事とか、WebClipの事とか書きます。


  1. はい、ほとんど乗っかってます。 ↩︎

  2. ええ、これも乗っかってます。 ↩︎

  3. これもPouchDB/CouchDBの標準機能です。 ↩︎

  4. 々々 ↩︎

  5. バイナリの分割サイズは定数のままです。 ↩︎