Yayakaプロトコルで実装する真の分散SNS (ja)

English version

Yayakaプロトコルは分散SNSのためのプロトコルです。 この記事では、これがなぜ作られて、なぜ良いのかを説明します。

目的

Yayakaプロトコルは大まかに以下の目的があります。

  • 脱中央集権
  • 民主化
  • 小さなサーバーでのネットワークへの参加
  • 階層化されたプロトコルによる高い拡張性

脱中央集権は自由を得るための最初のステップです。 独占プラットフォームはあなたの自由を奪うことがあります。

民主化は自由を得るための第2ステップです。 Yayakaプロトコルは独占プラットフォームを複数の独裁プラットフォームには分割しません。

小さなサーバーでのネットワークへの参加はそれぞれのサーバーが分散SNSのすべての機能を実装する必要がないことを意味します。 それぞれのサーバーは最低限の機能だけを実装します。

階層化されたプロトコルによる高い拡張性は同じ目標をもつサーバー同士で協力しながら独自に拡張することが可能ということを意味します。

特徴

前述した目的を達成するためにYayakaプロトコルは以下の特徴を持ちます。

  • サーバー間を自由に移住できる。
  • それぞれのサーバーは分散SNSの一部分を実装する。
  • サーバー間の通信の方法はソケット通信やHTTPSなど任意のものが使える。
  • 追加のプロフィール属性やユーザーによるイベントの種類、コンテンツの種類は上層のプロトコルで定義される。

コンセプト

Yayakaサービス

YayakaサービスはIdentityサービス、Repositoryサービス、Social Graphサービス、Presentationサービスの4つあります。 それぞれのユーザーはIdentityサービスで登録を行い、他のサービスを認可します。 Repositoryサービスはユーザーによる投稿やお気に入り、リポストなどのイベントを保存します。 Social Graphサービスはユーザー同士の関係を保持し、イベントを配信します。 PresentationサービスはウェブフロントエンドやAPIを提供します。 それぞれのユーザーは複数のRepositoryサービスで投稿し、複数のSocial Graphサービスにイベントを配信し、用途に合わせて複数のPresentationサービスを利用することができます。 もしRepositoryサービスが不当な検閲を行ったり、PresentationサービスがUIやAPIの改悪を行ったらすぐに離れることができます。

Yayaka Meta/Messaging Protocol

Yayaka Meta/Messaging Protocol (YMP) は分散ウェブアプリケーションのためのメッセージングプロトコルです。 それぞれのサーバーはYMPの上層プロトコルとして複数のConnectionプロトコルやMessageプロトコルを実装します。 Connectionプロトコルはどのようにしてサーバー間で通信を行うかを定義し、Messageプロトコルはアプリケーションがメッセージの交換によりどのように動作するかを定義します。 YayakaプロトコルはYMPのMessageプロトコルの一つです。

Yayakaプロトコルの動作

登録

  1. ユーザーがどのサーバーで登録するかを決める。
  2. ユーザーはそのサーバーに信頼されたPresentationサービスの一つにユーザー登録のための情報を入力する。
  3. Presentationサービスは登録を行うサーバーのIdentityサービスにメッセージを送信する。
  4. Identityサービスはユーザーを作成する。

フォロー

  1. ユーザーがPresentationサービス上でフォロー操作をする。
  2. Presentationサービスはそのユーザーが認可したSocial Graphサービスの一つにメッセージを送信する。
  3. Social Graphサービスはフォロー先のSocial Graphサービスにメッセージを送信する。
  4. 2つのSocial Graphサービスはフォロー関係を保存する。

イベントの作成

  1. ユーザーがPresentationサービス上でイベント作成操作をする。
  2. Presentaitionサービスはそのユーザーが認可したRepositoryサービスの一つにメッセージを送信する。
  3. Repositoryサービスはイベントを保存する。
  4. Repositoryサービスは自分を購読しているSocial Graphサービスにイベントを配信する。
  5. それぞれのSocial Graphサービスも自分を購読しているサービスにイベントを配信する。

現状

YMPの定義はほとんど完成しています。 現在はYayakaプロトコルとElixirによるリファレンス実装を同時進行で進めています。

計画

以下が今後の計画です。

Yayakaへの貢献

YayakaプロトコルスイートのためのGitHub Organizationがあります。 気軽にIssueを立ててください。

Yayaka Protocol and Highly Distributed Social Blogging

Yayaka Protocol is a yet another protocol for distributed social blogging. I explain “Why I create”, “Why we love”, and “Why you will love” in this article.

Purposes

Yayaka Protocol has following purposes roughly:

  • Decentralization
  • Democratization
  • Participation with minimum servers
  • High extensibility with layered protocols

Decentralization is the first step to get our freedom. Monopolized platform may take your freedom.

Democratization is the second step to do that. It means Yayaka Protocol doesn’t divide a monopoly service into many authoritarian services.

Participation with minimum servers means we don’t have to implement all functions of distributed social blogging for each server. Each server can have a minimum implementation.

High extensibility with layered protocols means we can extend our web services uniquely in cooperation with other services which have a common view.

Features

Here are the features of Yayaka Protocol to perform the purposes.

  • Easily migration between servers.
  • Each server only has to implement a partial set of functions of distributed social blogging.
  • Any way to connect two different hosts is allowed. (Socket, HTTPS with token, etc.)
  • Additional profile attributes, type of social blogging events, and types of contents are specified in higher-level protocols.

Concepts

Yayaka Services

There are 4 yayaka services, identity service, repository service, social graph service, presentation service. Each user registers themselves with identity services and authorizes other services to use. Repository services store events like posts, favorites, reposts, etc. Social graph services store users' relations and deliver events properly. Presentation services provide interfaces to be used by users like front-end UI, API, etc. Each user can have multiple repository services to post, social graph services to broadcast, and presentation services to use UIs which match for each purpose. If a repository service censors unjustifiably or a presentation service regress its UI or API, each user can exit right away.

Yayaka Meta/Messaging Protocol

Yayaka Meta/Messaging Protocol (YMP) is a messaging protocol for distributed Web applications. Each server can implement multiple connection protocols and message protocols as higher-level protocols of YMP. Connection protocols specify how to establish a connection between two different hosts and message protocols specify how the application works with messaging between services. Yayaka Protocol is a message protocol of YMP (Yayaka Meta/Messaging Protocol).

How Yayaka Protocol works

Registration

  1. A user chooses a server to register him/herself.
  2. The user enters registration information on one of presentation services trusted by the server.
  3. The presentation service sends a message to the identity service of the server.
  4. The identity service creates a user.

Following

  1. A user enters who to follow on a presentation service.
  2. The presentation service sends a message to one of social graph services authorized by the user.
  3. The social graph service sends a message to target social graph.
  4. Both two social graph services store the following relation.

Creating an event

  1. A user enters data to submit on a presentation service.
  2. The presentation services send a message to one of repository services authorized by the user.
  3. The repository service stores the data.
  4. The repository service broadcasts the event to social graph services which subscribe the repository service.
  5. The each social graph services broadcast the event to services which subscribe them.

Present condition

Writing the YMP specification is almost finished. Currently, I’m simultaneously writing Yayaka Protocol specification and a reference implementation with Elixir.

Plans

Here are the plans of Yayaka Protocol and YMP:

  • Create a first connection protocol
  • Create a message protocol for distributed registry to register higher-layer protocols of YMP.
  • Create a message protocol for distributed git hosting service.

How to contribute to Yayaka Protocol

There is an organization of Yayaka Protocol Suite. Please feel free to open issues.

kuyさんと話した

はじめに

以前からお会いしたいと思っていたkuyさんに会って話をすることができた。 結構色々なことを話したので備忘録的にブログに書くことにした。

技術的な話

※ 以下には私の考えや思ったことが含まれるので、実際に会話したときの結論やkuyさんと共有した考えというわけではない。

Redux

Reduxのコンセプトを破った方がうまいこといくならそうするべきか、みたいな話をした。 後からredux-thunkには「別に無理して守る必要は無い」というメッセージが含まれているのかもしれないと思ったりした。

ReduxとReact

ReduxかReactのどちらが将来的に技術的負債になりやすいかでいうと断然Reactじゃないかみたいな話。 Reduxは割りと簡単に移行できそう?

React Vue Redux redux-saga の教育コスト

ReactかVueかで迷ったときにReactの方が教育コストが小さいと思って選んだ話とその理由、 redux-sagaでのsagaの作り方よりもReduxのreducerの作り方のほうが難しく感じる人がいて驚いた話などをkuyさんに話した。 各個人が普段やっている分野によってどちらの方が教育コストが高い/低いというのが逆転する場合があるみたいな話や、習得が簡単に感じたとしてもちゃんと理解して使えているかは分からないみたいな話をした。

Reduxルーターライブラリ合戦

kuyさん作のredux-towerと私のredux-pagesの話。 redux-towerは解決すべき問題に出会う機会が少なくてなかなか進まないという話だった。 redux-pagesはもうほとんど完成形だが、使っているpathのテンプレートエンジンに多少不満があるので別のに乗り換えたいと考えている話をした。 bouzuyaさん作のbathが良さそうという話になった。

ElixirとPhoenix

ElixirやPhoenixについて聞かれたので、WebサーバーのようなErlang/OTPが向いている分野ではElixirは本当に書きやすいという話をした。 あとPhoenix Channelsが便利。

オートマトン

オートマトンの魅力や可能性などについてkuyさんに紹介してもらった。 オートマトンを使って最近ボトルネックになりつつあるマシンなどの全体に対して適用されるようなエラー対策(メモリのエラーチェック等)の必要性を無くし、コンピューターの処理速度を更に向上させることを目指す研究があるみたいな話だった(と思う)。

Ocaml

OcamlとかReasonとかの話を聞いた。 Ocaml製Webサーバーを作る厳しさについての話など。

Keras

Kerasを使って色々やろうとしていることやKerasとChainerについてよく言われる話を紹介した。 Define and runかDefine by runみたいな話。 一応Keras作者のツイートも紹介した。

GAN

GANの紹介。深層学習のヤベー奴。

Word2Vec

Word2Vecは結構応用できるみたいな話。

Docker

Dockerについてぼんやりとした知識しか持っていなかったのでkuyさんに教えてもらった。

Kubernetes

Kubernetesで実際にサーバーの再起動とかが行われる様子を見せてもらったりした。

標準ライブラリ

標準ライブラリが洗練されていない言語は辛い、みたいな話。

Yayaka.net

Yayaka.netのソースコードはElixirの学習に使えるか?みたいな話になり、一部に役立つコードもあるかもしれないが、ほとんどのコードは雑に書かれていることを説明した。

Yayakaプロトコル

話した合計時間的にはこの話題が一番長い。 Yayakaプロトコルはマイクロサービス的で、さらにマイクロフロントエンド的な面も持つみたいな話。 マイクロフロントエンドについては初めて聞いたのでkuyさんに教えてもらった。 時代は脱モノリシック。

その他の話

アニメ、音楽、ゲーム、ドラマ、映画

趣味についての話。 ものすごく雑な概要としては、私がフリフラの布教活動をした。

マストドンに対抗して分散SNSを作った

はじめに

タイトルは釣り。s/対抗/便乗

あと、「作った」という表現は厳密には正しくなくて、以前作ったYayaka19サイトを”分散化した”というほうが近い。

記事の目的

  • いろいろと忘れてしまう前に記録しておく
  • アナウンスメント
  • 連邦全体のユーザー数を5人くらい増やす
  • サーバーが1つくらい増えたらすごい

目的に記録が含まれているというのもあって結構余計なこととか書く。

経緯

Yayaka19

もともと(Share)という名前で作り始めた。最初は、増田からパーマリンクや時系列表示を無くしたようなサービスにするはずだったが、 機能を足したり引いたりしているうちに今のような形に。 Yayaka19という名前に変えることになったのは、利用者から「Shareという名前はググラビリティが悪いのでKOKOMINECOCONAみたいな感じのエントロピー高い名前にしてほしい」と言われたため。

OStatus

OStatus*1マストドンで知ったかというと違っていて、 マストドン流行以前からGNU Socialのアカウントを持っていたため、OStatusについても知っていた。 マストドン流行以前にもYayaka19にOStatusを実装して分散SNSにしようという話はしていたが、 周りは「自分ならOStatusは実装したくない」という反応で、自分も実装したくなかった。 また、独自プロトコルで実装する(Yayakaプロトコル構想)という考えもあったが、 そもそもYayaka19を分散SNS化する必要はあるのか?分散SNSの需要は本当にある?というところで確信がなかったため、 時間をかける気は起きなかった。

そんなところに、あの日本でのマストドン流行が起こった。 少なくとも私の観測範囲では流行っていることを確認できた。 GNU Socialと違っていろんな界隈でたくさんのアカウントが作られていたし、Twitterでの投稿数が減少した人も見た。 少なくとも私の観測範囲では流行っていることを確認できた、分散SNSにも一定の需要があると判断し、 独自プロトコル(Yayakaプロトコル)を用いてYayaka19を分散SNS化することを思い立った。

設計と実装

とりあえず方針としてはサーバー同士をWebSocketで接続して、なにかあるたびにメッセージを送受信する。 例えば、あるユーザーが外部サーバーのユーザーをフォローしていたら、タイムラインをロードする度に外部サーバーから投稿を取得する。 他には、新しい投稿があったときに投稿者のフォロワーに外部サーバーのユーザーがいれば、新しい投稿を外部サーバーに送る。

一応Yayakaプロトコルにする予定があるので、ある程度留意して設計した。

主なモジュール

実装

実装するには、まず、WebSocketのサーバー側とクライアント側を作る必要がある。 しかし、両方を自分で作るのは面倒だったため、Yayaka19が使っているWebフレームワークであるPhoenixフレームワークのChannelsという機能を無理やりサーバー側として使うことにして、 クライアント側だけ自作した。それはライブラリとして別に公開している(phoenixchannelclient)。 SocketRegistryにはElixirのRegistryという比較的新しいモジュールを利用した。 全体的に実装していて楽しかった。

結果と反応

分散化できた。 結構面白がってもらえている気がする。 早速、私以外が運営するサーバー(19.fubaiundo.org)が立ったので良かった。

画像集

プロセスの関係図

f:id:ryo12redstone:20170511013401p:plain

VisualixirというツールでErlang VMのプロセスの関係図を出した。 真ん中の花火みたいなやつが多分HTTPのコネクションプール、左下のはRDBのコネクションプール、右側はフレームワークのやつ、という感じなので左上が実装した部分。

タイムライン読み込みの状態

f:id:ryo12redstone:20170511013507p:plain

errorとかtimeoutとかloadingとかになる。この表からRetryもできる。

リモートフォローの様子

f:id:ryo12redstone:20170511013636p:plain

写真が暗いのはブラウザの拡張機能でページが黒くなってるから。

military or bomb

f:id:ryo12redstone:20170511013734p:plain

if式くれ。

AA

f:id:ryo12redstone:20170511013850p:plain

ウーーイヌ

今すぐダウンロー

f:id:ryo12redstone:20170511014006p:plain

収益

f:id:ryo12redstone:20170511014052p:plain

サーバー代は700$/monthミス

サーバー代は$7/month

今後のアレ

  • Remote Mystery(やばい機能)
  • テスト書く
  • Dockerイメージ
  • Yayakaプロトコル
  • 外側をOStatusに見せるためのプロキシ
  • Editor Pluginsが流石にゴミ置き場すぎる

最後に

特になし。

リンク

*1:マストドンGNU Socialが使っている分散SNSのためのプロトコル

ReduxでのRoutingはこれでやっていこうと思います。

Reduxでの理想のRoutingライブラリを求めて作ったredux-pagesの紹介記事です。

リポジトリ: ryo33/redux-pages

実用例: ryo33/Yayaka19

以前の方法での不満

redux-pagesを作るに当たって、まず、前にSPAサイトを作っていて不満に感じたことをまとめました。 その中で、特に解決したかったものが以下の4つです。

  1. Pathのパース結果はRouter(View)だけでなく、Middlewareでも必要である
  2. 現在のページがStateに格納されていてほしい
  3. Pathをリダイレクト処理などで手書きしたくない
  4. ページ遷移に関連する処理はMiddlewareに書きたい

(1)で言いたいことは、Pathのパース結果は画面描画時だけではなく、 表示されているページによってMiddlewareでの処理内容を変えたいときにも必要だということです。 redux-pagesのREADMEに書かれている

Parse once, route anywhere

は、このことを言っています。

(2)は、Pathと画面表示は常に一致しているわけではないということです。 例えば、閲覧権限の無いページのPathにアクセスして、エラーページのPathにリダイレクトされるまでの間は何を描画するべきでしょうか。 また、Webサイトの中にはページ移動後にコンテンツのダウンロードが終わるまでは前のページが表示されたままのものがあります。 このように、Pathと画面表示は常に一致するわけではありません。 しかし、Routerで描画したり、Middlewareで処理の分岐をしたりするためには、 現在のページが常に分かるようになっている必要があります。

(3)は、Pathを手書きすると、間違っていても発見しづらく、またPath形式の変更が容易でなくなってしまうためです。

(4)は個人的な好みです。

redux-pagesの基本的なアイデア

以上で述べた不満を元にした基本的な考えは以下の3つです。

  1. Stateには現在どのページを表示すべきなのかが保持される
  2. ページオブジェクトを定義して、ページ遷移Actionや、そのページへのPathはメソッドで生成する
  3. ページ遷移は単一のActionをdispatchすることで行う

不満1を解消するのは(1)(2)、不満2を解消するのは(1)、不満3を解消するのは(2)、不満4を解消するのは(3)です。

(3)で単一のActionになっているのはMiddlewareでのページ遷移処理を単純なコードで書くためです。 例えば別のページへのリダイレクトはdispatchするだけでいいですし、 ページ遷移を遅らせるときも単にnextDispatchを呼ぶのを遅らせるだけです。

基本的な動作イメージ

redux-pagesを使ってページ遷移を行うときの基本的な動きを説明します。

Actionをdispatchしてページ遷移

  1. redux-pagesのMiddlewareにActionが到達したときにPathが変更される
  2. Middlewareで様々な処理が行われる
  3. Stateが変更されViewもそれに応じて変更される

これが、基本的なページ遷移時の処理の流れです。 ユーザー認証やリダイレクトなどは(2)のMiddlewareの中で行います。

locationの変化によるページ遷移

こちらは、ブラウザの戻るボタンなどが押されたときに使われます。

  1. Pathのパース結果でActionが投げられる
  2. Middlewareで様々な処理が行われる
  3. Stateが変更されViewもそれに応じて変更される

実用

redux-pagesはYayaka19というSPAアプリケーションで実際に使用しました。 とりあえず今のところは大きな問題など無く運用できてます。

前述の不満の解消以外の利点としては複数のページに適用したい処理をまとめて書けることが挙げられます。 例えば、アクセス権限のあるページかどうか調べてリダイレクトを行う処理を各ページごとに分けて書く必要はなく、 単にallowedPageNames.includes(action.payload.name)というような条件文に応じてリダイレクトを行うだけです。

あと、使ってから気づいたのですが、nextDispatchを遅らせることによりページ遷移を遅らせる技は滅多に使わないと思います。 ロードが終了するまで前ページを表示するよりは、ページ遷移時のHookでisLoading状態にして、あのグルグルを表示したほうがSPAぽくなります。

ちなみにページ遷移時のMiddlewareの処理にはredux-middlewaresを用いています。 例えば、ログイン確認とそれによるリダイレクトにはcreateReplacerを、ページ遷移時のHookにはcreateAsyncHookを使います。 個人的に直感的にMiddlewareを書くことができるので気に入っています。 ただ、既にredux-sagaredux-logicなどを導入している場合はそちらを使ったほうがいいです。

課題

例えば/posts/:postNumberというパスがあってpostNumberを文字列型から数値型にキャストしたいといったときに、 エラー処理も含めるとMiddlewareでActionを変換する必要があります。 これをどうにかページ定義時に書くことができるようにしたいと考えています。

あとは、導入時の雛形が比較的多いのもどうにかして減らしたいです。

さいごに

割とまともに使えて、辛いポイントもそんなにないので、とりあえずしばらくはこれを使っていこうと思います。

技術記事投稿サイト

本記事は殴り書きしたものをそのまま投稿したので、非常に読みにくくなっています。 また結論まで書ききっていません。 期末テストが終了して落ち着いたら加筆して推敲する予定です。(予定とは言っても、60%くらいの確率でやりません)

技術記事投稿サイトについてずっと思っていることについて書きます。 また、いちいち「主観ですが」と書くのが面倒臭いので、主観で書くときには「見る」という表現を使うことにします。

まず、技術記事投稿サイトは「技術記事が楽に書ける投稿サイト」と「質の高い技術記事が読めるサイト」の両方を目指しているように見えます。 明確な記述は多分ありませんが、ほとんどのユーザーはそのつもりで利用しているように見えます。 本記事ではこれが大前提です。 あと、私の見解が「質の低い記事を投稿する人が悪いわけではない」というのも前提です。

最近になって記事や記事の投稿者がSNSなどの外部のメディアで批判されたり、運営によって質が低いとされた記事が削除されたりするのを見るようになりました。 これが嫌で最近はほとんど利用していません。 以下ではなぜ嫌なのか、どうなったら積極的に利用したいのかを書いていこうと思います。

「技術記事が楽に書ける投稿サイト」に最低限必要なのは以下の2点だと思います。

  1. シンタックスハイライトなど技術記事を書く上で難しいとされることが簡単にできる
  2. めったなことがないと投稿が削除されない

1は十分に満たされていて、2は満たされてないように見えます。 「投稿サイト」として使うのは怖いため、私は最近は外部で技術記事を書くようになりました。

次に「質の高い技術記事が読めるサイト」に最低限必要なのは以下の2点だと思います。

  1. 記事の評価を行うための適切なシステムがあり、それによって質の高い記事が優先して表示される
  2. 外部サイトのURLを投稿することができ、内部の記事と同じように一覧に表示され、同じ記事評価システムが使われる

こちらは1も2も満たされていないように見えます。

1が満たされていないため、批判がサイト内ではなく外部で行われます。 外部で批判されるのは非常によくないと思っていて、なぜかというと批判が投稿者にフィードバックされる可能性が低いからです。

また、2が満たされていないため、私はサイト内で直接記事を検索するのではなく、検索エンジンから記事を検索して間接的にアクセスするようになりました。

明日からテストなので、とりあえずここまで。はい やたらと批判したがる

追記 とりあえず記事が消されなくなるのと、何かしらことが起こって質の低い記事やその投稿者が外部で批判されない雰囲気になったら積極的に使っていきたいです。

Cruxがあればゲームのコードが良くなると思った

ryo33.hatenablog.com

24日目です。


動機

昔何かの記事を読んで、その時からメッセージ指向でゲームを作りたいと考え始めました。 しかし、慣れてしまったやり方からはなかなか離れられないもので、 長らく実現することができていませんでした。 後述するcdb-rustも「いつものやり方でいいか」という消極的な考えのまま作られたものです。

最近、Webのフロントエンドをやる機会があって、 JavaScriptのRedux.jsというライブラリを使っていました。 僕の認識では、状態の変更に関して、 Storeに送られたActionが Middlewareを通って変形・フィルタリングされ、 それを受け取ったReducerが状態を書き換えるような アーキテクチャにするためのライブラリです( Fluxを実現するライブラリの一つと書いたほうが正確ですが、 動機的にはこの書き方のほうが伝わりやすいはずです)。 それを使っていく中で、ゲームプログラミングも似たようなやり方でやりたいという思いが高まってきたためCruxというライブラリを作ることになりました。

Cruxについて

基本的な使い方は、状態を管理する構造体をもとにStoreを作って store.dispatch(action)で非同期にstore.dispatch_sync(action)で同期的にActionを 送ります。最新の状態はstore.state()で取れます。

Actionのキューや非同期処理、排他処理などはいい感じに隠蔽されています(コードもいい感じとは言っていない)。

あとActorっぽく使うこともできます。

ryo33.hatenablog.com

実際に使ってみる

github.com

前に作ったCDBというゲームがあったので、それをCruxを使って書き換えました。 Crux本体のコードを書くのは本当に楽しくてしょうがなかったのですが、 こっちの作業はかなりつまらなかったです。 ウィンドウ切り替えなどをした時に固まるなどの不具合がありますが、とりあえず動くところまではできました。

さて、コードが良くなったどうかですが、正直に言うとあまり良くなっていません。 時間がなくてActionの単位が大きかったり、 Middlewareを使うようにしたりできなかったためです。

幾つかの処理をActionとして切り出してMiddlewareから送るようにすれば、もうちょっとCruxの良さが分かるコードになると思います (具体的にはボールやバーの大きさが徐々に変化する部分やボールがアイテムに当たる部分などです)。

これから

コードに危ういところがあり警告も出るのでそれを直していきたいです。 あとはドキュメントの整備もやっていきたいです。


最後まで読んでいただきありがとうございました。