Next.jsで作ったActivityPub実装を育てている

SNSを作るのってやることが多いですね。

2023年の1月ごろに作り始めて ↓

丸1年経ちこんな感じになっています。本当に最低限も最低限のさらに75%くらいのことしか出来ません。

Unsocialの現状のデザイン

リポジトリ名は「Unsocial」です。最初はSocialにしようかと思っていましたが、普通過ぎるのでNERV(@UN_NERV@unnerv.jp)さんとライブラリのunfetchから着想を得てUnsocialにしました。

動作確認もかねて立てているサーバーはこちら↓。今のところしゅうまい君(@shuumai@misskey.io)閲覧専用サーバーになっています。

開発のきっかけとしては2022年の末にTwitterがもうだめかもしれないみたいな話題が何度かあり、それが二度三度続いていてさすがに分散SNSが来るのでは?みたいな期待からActivityPubを調べて作り始めました。

(結果的にはMisskeyが一時盛り上がったものの、落ち着いてきた印象がありますね)

ただ、どういうソフトウェアにしたいとかは全然決めておらず、今のところ自分が楽しむためだけに開発しています。分散SNS自体もたまに触るくらいであまり使いこなせておらず...。

とはいえ開発自体は面白いです。今回は開発の気分転換にActivityPubを実装した感想や、こだわったところなどをまとめようと思います。

仕様を厳密に実装しなくても動くものはできる

分散SNSを実装するためにはActivityPubという仕様と、その他いくつかの仕様をある程度理解していることが必要です。

僕はこの手の文書を読んだ経験がまったく無く、W3CのActivityPubのページを読みながらSHOULDとMUSTをメモする などして(そういえば教科書の要点をまとめて自分用の冊子を作るのが学生時代の勉強スタイルでした)、ようやくおおまかに理解しました。

とはいえ、実際はActivityPubの仕様に完全に準拠させなくても最低限必要なポイントを押さえていれば動くものは作れます。

自分の場合は下記の記事で公開されている実装を改造して挙動を理解したり、理解が進んでない部分はコードをそのままコピペして理解が進んでからリファクタリングして進めました。素晴らしい記事をありがとうございます。

最低限動くものを作る(使う) → 挙動を見ながら理解を進める → 改善するの流れですね。

連合相手のコードを読むのは面白い

ActivityPubの理解を進めるときには、仕様書よりもMisskeyかMastodonの実装を確認することが多いです。

実装してみて思ったのですがActivityPub自体は統一された規約というよりは、ある意味ではなんとなく参考にできるベースのアイデアのようなもので、実際は連合したい相手がどのように実装しているかに強く依存することになります。

例えばActivityPubには投稿が改ざんされていないことを検証すべきとあるのですが、肝心の検証方法が決まっていません。

Unfortunately at the time of standardization, there are no strongly agreed upon mechanisms for authentication.

https://www.w3.org/TR/activitypub/#security-considerations

現状はMastodonが採用しているHTTP SignaturesとLinked Data Signatures(RSASignature2017)が事実上の標準となっているようで、自分が見た範囲ではMisskeyもこれと同様な実装をしています。Unsocialでも同じように実装しています。

他の実装は見ていないので違うかもしれませんが、Mastodonと連合するためには互換性のあるアルゴリズムで署名する必要があるはずので、結局Mastodon側の実装に引っ張られます。

このように仕様が強く指定していない部分の挙動は連合したい相手の実装に合わせる必要があります。おそらくMisskeyはMastodonに合わせているのだと思いますし、ほとんどの実装も同様だと思います。今はActivityPubとはほぼMastodonの実装を指しているのではないかと個人的には思っています。

このあたりの事情を知っていると、ThreadsやTumblrのような人気サービスがFediverseに参加することのインパクトも違って感じられます。

いろいろなこだわり

完全個人のプロジェクトなのでいろいろなことにこだわりました。

まず実装回りですが、フレームワークとしてNext.js、ライブラリは主にTailwindCSS、Prismaを使って実装しています。

定番?の構成で固めようと思っていて、(そういう機会があるかは分からないですが)この辺のライブラリに明るい人が改造しやすいようにしています。

まだまだ完全ではないですが、ディレクトリ構造はコロケーションを活用しています。

E2Eテストも早い段階で整備しました。Mastodon、Misskeyとの連合が常にチェックされているのでリファクタリングがかなり安心感高く行えます。この辺はZennで記事にもしました。

あとはあまり使ったことのないツールや設定を試す場にもしています。eslintでは未使用importの削除、ソートを自動で行うように整備しました。

  "rules": {
    // simple-import-sort
    "simple-import-sort/imports": "error",
    "simple-import-sort/exports": "error",
    // unused-imports
    "unused-imports/no-unused-imports": "error",
    // @dword-design/import-alias
    "@dword-design/import-alias/prefer-alias": [
      "error",
      {
        "alias": {
          "@": "./app/"
        }
      }
    ]
  }

そのほか、未使用コードを検出してくれるKnipや、ミューテーションテストのStrykerも導入しています。

おわり

ActivityPubを試していると、中央集権型では意識しなくて良かった問題がいろいろ出てくるのを感じます。

ユーザー視点で言えば、分散しているかどうかはほとんどの人にとってはどうでも良い問題なので、おそらく流行らないか、流行ったとしても分散自体は(仕組み自体がそうさせない限り)成立しないのではないかと思います。

とはいえ分散SNSの技術自体はどれも魅力的です。いつかはNostrやBlueskyにも触れてみたいですね。

Unsocialの開発は少し挫折しかかっていますが、もう少しは続けるつもりです。