Skip to content

みなさんお久しぶりです、きみのぶです。いかがお過ごしですか? 僕はようやく元気になってきました。

さて、今回は大ニュースをお伝えすべく筆を執りました[1]

Peer-to-Peer Sync is coming!

v0.24.11からなんと! Self-hosted LiveSyncに、サーバを必要とせずにデバイス同士で直接接続し、同期できるPeer-to-Peer Replicatorが実装されました(試験実装です)。

このPeer-to-Peer Replicator、WebRTCというブラウザtoブラウザの直接接続技術を使って実現されています。一昔前まではこれ、上手く通らないことも多かったのですが、なんと! 昨今のウェブ会議の隆盛によって、かなりの環境でスッと繋がるようになりました。

WebRTCの通信は、基本的にはそもそもE2Eで暗号化されているため、暗号化関連のオーバーヘッドがかかることもありません。また、最適っぽい経路で繋いでくれるので、ローカルネットワーク内端末同士が居れば、ローカルネットワークで通信を完結してくれます。とてもセキュア。

ただ、この「経路」を発見するために、お互いの情報を交換する必要があります。これには通常、シグナリングサーバーというものを使用するのですが、今まではこのシグナリングサーバ自体も作ったり、あるいはホストしたりする必要がありました[2] 。これも驚くべき事に、Trysteroというライブラリのおかげで、シグナリングサーバーにNostrのリレーを使用することができています。オープン、実にオープン。今回も、あらゆる「僕が書いてない実装」をサーバサイドに使用することが出来ます。

ただ、いきなり、このexperimentalな実装のまま、外部のリレーを使うのも怖いので、まずまずは私の建てた検証用リレー[3] を使って貰えればと思います。もちろん、自分のサーバを建てて貰っても全然大丈夫です。

シグナリングサーバーに、実データが転送されることはありません。ただし、暗号化された端末情報は流れます。気にされる方はご自分でリレーを作るか、同期においてLiveSync的な機能(後で説明しています)をオフにしてもらえれば…と思います。

セットアップはめちゃくちゃ簡単になっています。これもこの記事で説明しますね。

また、Self-hosted LiveSyncの機能のうち、使えない機能もほとんどありません。リモートをリビルドするなどはできなくしていますが、その程度です。PouchDBのマルチマスターレプリケーションが輝いております。

さて、安い、安全、速い、この三拍子揃ったかのようなPeer-to-Peer同期、一つだけデメリットがあります。それは、データを預かってくれる端末が起動していない限り、二つのデバイスを同時に起動して同期しなければいけないということです。そらそうなんですが、いささか面倒ですよね。だからこそ安全という見方もあるのですが、この面倒を避けるために、ブラウザ版のPeer-to-Peer同期機能を作っておきました。少しずつ、プラットフォームに張り付いている機能を剥がして抽象化してきた苦労がようやく…ウウッ…。まだホスティングはしていないので、折りを見てホスティングしますね。

ObsidianをPeer-to-Peer同期を有効にして起動したままにしておくか、どこかの端末でこのウェブ版を開きっぱなしにしておいてもらえれば、それをハブにして接続することが出来ます。

Peer-to-Peer 同期の使い方

さて、ここからはざっくりPeer-to-Peer Syncの設定方法と、使い方を解説していきたいと思います。

設定方法

実の所、あまり設定項目はありません。リボンのアイコン、またはCommand paletteからOpen P2P Replicatorを実行すると、下記のようなタブが表示されます。
長々と書いているのでびっくりされないように申し上げておくと、必要なのはEnable P2P Replicator以外は、Room IDPasswordThis device nameのみです。

※保存されていない変更がある場合はハイライトされます。

  1. Enable P2P Replicator
    P2P Replicatorを有効化するかどうかの設定です。間違いがあって、勝手に同期されてしまうことの内容に全体を無効化できるようになっています。

  2. Relay settings
    シグナリングのための、接続先リレーを指定します。
    これは、基本的には今は僕のテストサーバを指定しておいて貰えると安心です。[4] Use vrtmrz's relay でテストサーバを指定できます。

  3. Room ID
    これは、シグナリングサーバーの中で、自分を一意にするためのIDです。パスフレーズとセットで一意になりますが、出来ればランダムなものを使用してください。Use Random NumberでOKです。入力しやすいように3文字区切りです。アルファベットは最後の枠にしか現れません。一応、自分で入力するときは任意のフォーマットが使えます。めっちゃ長い文字列を使用すると相手が見つからなかったりします。

  4. Password
    暗号化用のパスワードです。Room IDが一致しても、パスワードが一致しなければ接続できなくなってます。なんか良い感じのものを設定してください。

  5. This device name
    その端末のデバイス名です。注意点として、あまりに長いと上手く接続できないです。そしてデフォルトではVault名が入るので、モバイルだと長すぎるかもしれません。
    後述しますが、接続可否の決定はこのデバイス名で保存されます。長さとわかりやすさのバランスが取れれば、ユニークなものにできれば理想型です。

  6. Auto Connect
    チェックをつけると起動時にシグナリングサーバーに接続しに行きます。十分に設定が安定してから行った方が無難です。自分の存在をできるだけ目立たなくしたい方は、自分のリレーを構築してから有効にした方が良いです。

  7. Start change-broadcasting on Connect
    LiveSync、つまりリアルタイム同期を行うためには、変更があったことを通知する必要があります。接続中は手動で、Signaling Server Connectionの、Start Boardcasting で開始できます。これも、できるだけトラフィックを減らすのであれば無効のままにできます(ただし、これはPeer-to-peerで配信されます)

さて、いろいろと説明しましたが、最初に申し上げたとおり、基本的にはRoom IDPasswordThis device nameを埋めるだけで実行できます。やってみましょう。

接続!

Signaling Server Connection の、Connectをクリックすると、シグナリングサーバに無事に接続できた場合は下記のように表示されます。

そして、Peersに、同じ設定を行ったデバイスが表示されるはずです。
こんな具合ですね(左がdev_1、右がdev_recvの画面です)。

ここで、デバイス名の下に、Peer IDが表示されます。意図したデバイスか確認できたら、Accept してみましょう。(ちなみに、不穏な端末の場合は容赦なくDeny してください。Deny した事は相手方には伝わりません。まぁ、そもそも接続が成立しないため、あまりないとは思いますが、逆に不穏な場合は絶対してください)。

Accept すると、表示が下記のようになります。

許可を取り消す場合は、Revokeで取り消せます。

さて、いつもの絵文字のアイコンでぱっと見はわかりにくいですが、下記のような機能になっています。

アイコン機能
🔄同期をします
Watchを開始します。相手がChange-broadcastingを行っている場合にWatchしていると、LiveSyncが行われます。
...サブメニューを表示します

サブメニューには下記のものがあります。

項目機能
Only Fetch相手方の変更のみを反映します。
Only Send相手方から、自分の変更をすべて取り込んでもらいます。
Get Configuration相手方から設定をコピーします。Peer-to-peerだけ設定したあと、2台目の設定をこれで行う事を想定しています。Peer-to-peerだと結構早い。
Toggle Sync on connectこのデバイスが見つかり次第Syncする設定を有効化/無効化します
Toggle Watch on connectこのデバイスが見つかり次第Watchする設定を有効化/無効化します
Toggle Sync on Replicate now commandPeer-to-peer同期を有効にしている場合で、同期モードがOnly P2Pの時のReplicate Now、または同期モードに他のものが設定されている場合のReplicate now by P2Pを実行した場合の、同期対象として設定/解除します。

まぁ、通常意識して使うのは、🔄ぐらいだと思います。さて、押してみましょう。

相手方の端末で、Accept していない場合は、下記のようなダイアログが表示され、許可するかの決断を求められます(ちなみに、返答に時間が掛かった場合は承認してもその回は、同期は失敗します。放置した場合はIgnore temporaryが選択されます。Temporaryの決断は再起動するまで有効です)。Acceptします。

うまく許可が通ると、下記のように端末名の下にFETCHING等、状況表示が行われるようになります。

また、Show status inside the editor している場合は、ほかの同期方法と同様に転送状況がデバイス名とともに表示されます。

Get Configuration の使い方

Get Configuration を使うと、全設定をPeer-to-peerで相手方端末から取得できます。この際、CouchDBのパスワードやE2EEのパスフレーズも含まれるため、リクエストをされた側の端末で、パスフレーズが求められます。

このパスフレーズを答えるかキャンセルすると、要求した側にダイアログでもダイアログが表示されます。

キャンセルした場合は、ランダムなパスフレーズで暗号化した、ダミーのパスワードが返却されます。意図しないパスフレーズの要求に遭遇した場合は、キャンセルし、可及的速やかにRoom IDとパスフレーズを変更してください。

無事に複合できた暁には、その適用方法を問われます。基本的には一番上、ローカルデータベースを捨てて、相手方からFetch相当のアクションをするのがおすすめです。

答えると、Fetchのために、一時的に同期モードをP2P Onlyにするか問われます。Yesで良いです。

いつものConfirmationが表示され…

チャンクをローカルで作るかも問われ…

準備ができると、P2P Syncが無効になっているから、有効にして良いかを最終確認されます。Yesで。

そうすると、シグナリングサーバに接続し、他の端末が現れるのを待機できます。
このときに、[*] がついているのが、前回Get Configurationした端末です。
(もし一台も出てこない場合は、Refresh Listしてください。しばらく待ちます。

合計で2回聞かれます(これは実はCouchDBやMinIOとの接続でも同じなのですが、Peer-to-peer同期の場合はPeer選択があるため、ちょっと目立ちます)確実に同期するための仕組みですね。

その他

このPeer-to-Peerモード、何度か実装しているのですが、毎回「全然WebRTCが確立しない」や「ライブラリがバイナリを上手にやりとりできない」みたいな問題があって、没にしてたんですよね。今回、これを実現できて、結構感無量です。ほぼ完全に自由な同期が実現できたのではないでしょうか。

まぁ、ちょっと命名が何というか、Self-hosted LiveSyncなのにSelf-hostedなHostが要らない…トゲアリトゲナシトゲトゲみたいな命名になっちゃったのが悩ましいですね。これは今Issueにも上がっているLiveSyncが、プラグイン名称なのか同期モードなのかわかりづらい、という問題にも通じるのですが。なんともはや…。最近、メッセージをがっつり変更できる機能を実装したので、ローカライズがてら対応できればなと思います。

あと、Peer-to-peer同期も含めて、何かあったらいつでもTwitter(現X)とか、Nostrでお声がけください。もちろんGitHubへのIssueも大歓迎[5]です。

では、また!


  1. もう少しこまめに書きたいのですが、すっかり超大作に。 ↩︎

  2. ちなみに、Peer-to-peer同期自体は3回目ぐらいの実装です。peerjsを使ったものとかがありました。ようやく世に出せました…。 ↩︎

  3. この検証用リレーですが、めちゃくちゃ非力なマシンで動かしているので、もし音を上げた場合仕切り直す可能性があります。 ↩︎

  4. Trysteroのリポジトリを見ると、リレー管理者の人に少し顰蹙を買ってるのも見受けられます。僕はこれも含めてNostrだと思ってるんですけどね… ↩︎

  5. 貯まっちゃってますけどね…なんとかせな。 ↩︎