おひとり様ActivityPubサーバー実装メモ
ActivityPubを通じてMastodonなどFediverseと呼ばれるネット連合からアカウントとして認識されるように最低限のサーバー構成を作ったのが7/12。その後、秘密鍵・公開鍵を使えるperlのモジュールをlolipopレンタルサーバーにインストールできたことで、それっぽいものにしあがって、実際に運用して今日に至る。
「ため池」
↑perlで自作実装したおひとり様ActivityPubサーバー
そもそもMastodonやMisskeyなどのサーバーを立てるスキルがない。わたしには難易度が高すぎる。
自分のわかる範囲で自作実装したので、間違ってる・勘違いしてるところもあるかも知れないけど、今のところ意図通り。
この雑記帖と同じレンタルサーバーに展開して追加の費用なども必要ない。
自作とはいえ、スグ忘れるのでまとめてメモしておこう、というのがこのエントリ。
5分以上前の自分はアカの他人。
Fediverseからアカウントとして認識されるために必要なもの
1)nodeinfo(json)
サーバー情報。
2)host-meta(xml)
webfingerのURL。
3)webfinger(json)
ユーザー情報のURL。
4)actor.json(json)
ユーザー情報。type:Personのjson。
上から順番にアクセスされて4のユーザー情報にたどり着けばActivityPubサーバーのアカウントとして認識される。
1と2はなくても大丈夫らしいけど、webfingerのURLは「/」だったり「/.well-known/」だったりするので、2の「/host-meta」で教えてあげる。
「ActivityPubを使ってFediverseにたどり着く」
↑各ファイルの詳細。
おひとり様サーバーとして動作しているスクリプトは2つだけ。
・飛んでくるリクエストをさばく門番的スクリプト
・タイムラインを表示、投稿、管理するスクリプト
【飛んでくるリクエストをさばく門番スクリプト】
ActivityPubでの会話を求めて飛んでくるリクエストはいろいろ。
アカウント情報を取得するためにactor.jsonをリクエストされるし、ActivityとしてCreate(投稿)Follow(フォロー)などのリクエストが飛んでくる。
まず「.htaccess」を設置
rewrite ruleでパラメータをつけて門番スクリプトにリダイレクトするところからスタート。
リダイレクトされてリクエストを受けつける門番スクリプトの仕事は以下。
サーバー情報やユーザー情報はGETでリクエストが飛んでくる。
リクエストされたURLに応じて、門番は用意してある静的ファイル、jsonやxmlファイルを返す。
ActivityPubのjsonファイルは「Content-Type: application/activity+json; charset=utf-8」をヘッダにつけるのが必須。
followerやfollowingにアクセスされた時はfollower、followingを記載したテキストファイルを読んで、静的ファイルにそのリスト情報をくっつけて返すようにした。
Activityリクエストは「ユーザー名/inbox」にPOSTでリクエストが飛んでくる。
これはまとめて門番で受け取って。
・HTTPヘッダ類
・content(ボディ)
これをひとつのログファイルとして、所定のディレクトリに保存する。
ボディ=jsonを読んで、CreateだのFollowだのUndoだのログファイル名の一部にしておく。
門番スクリプトの仕事はここまで。
ヘッダ類もログファイルに収録するのは。
公開鍵とhttp signatureで、飛んできたリクエストが正しく署名されているか検証する必要がある。
Mastodonなんかはたぶんリクエストが来たらその場で検証までしてるはずで、ログファイルなど必要ないと思う。
わたしの場合、まだよくわかってないことが多くて、ログファイルで確認したいことがあるので、そのためにログファイルを残している。
ActivityのPOSTリクエストのうちいくつかは今時点スルーしている。
仕様的にはMUSTとかSHOULDだったりするものもあって、対応が必要と思われるけど、ウチの運用的にそれ必要?と微妙なもの…要検討。
【タイムラインを表示、投稿、管理するスクリプト】
あまり考えず「ため池」という名前にした。
Mastodonでいうところの「ローカルタイムライン」のために投稿用のデータベースを用意。
といっても、テーブル2つだけ。それも1つはログインに使うだけのもの。投稿用のテーブルはテキストと、画像のURLや幅・高さぐらいを記録するメモ帳。
投稿するには重複しない一意のIDが必要で、それにはデータベースが便利に使える…ほぼそのため。
おひとり様サーバー、ユーザーはわたしだけ。サーバーに登録しているユーザーの投稿を表示する「ローカルタイムライン」は、わたしの壁打ちとなる。
フォローしたひとの投稿を表示する「ホームタイムライン」はファイル管理。
自分以外、ひと様のデータを持たないのが原則。ひと様のデータを溜め込むといろいろ考えなきゃいけないことが出てくるから。
ため池にアクセスすると、門番スクリプトが保存したログファイルを開いて。
まずはHTTP SignatureのVerify。
actor情報をリクエストして、公開鍵を取得。公開鍵を使ってログファイルのヘッダ情報にあるSignatureを検証する。
Signatureに問題がなければ、Activityに応じた所定のフォルダに、所定の命名規則で名前をつけたjsonファイルを保存。元のログファイルを削除。
「RSAモジュールで公開鍵と秘密鍵」
「HTTP Signatureをlolipopレンタルサーバーで作成」
↑秘密鍵、公開鍵について
タイムライン用に保存するjsonファイルはCreateかAnnounceのNote
・上限20個
・期間1日
ディレクトリのファイルを読んで制限以上になっていたら削除。
フォローイングが増えてくると、読まないまま削除される方が多いし、さらにフォローは増やすつもりなので、読む・目にする投稿の方が圧倒的に少なくなる。知らない間に流れてきて知らないうちに削除されている。
でも、SNSはそのぐらいで適正だと思う。
その時その場で見たものがすべて。「袖すり合うも他生の縁」だ。
「ホームタイムライン」用に保存するjsonファイルは「to」か「cc」に「Publish」(全公開)が指定されているもの限定(サークル投稿を除く)
この「ホームタイムライン」を表示するにはログインが前提。つまり、見ることができるのはわたしだけだ。
ホームタイムラインの投稿にフォローしたひとのアイコンや名前を表示させるために、actor.jsonをリクエストしてpersonを取得してそこからアイコンのURLや名前を取ることになる。
これだけのためにリクエストが生じるんで、ホームタイムラインの表示が重い。
[10/24 09:27:53]
さすがに重すぎでキレた…ので、不本意ながら、表示名とアイコンURLに限っては、キャッシュすることにした。
当然、表示は劇的に早くなった。
キャッシュの期間は30日(要検討)。
Updateのリクエストがpersonだったら確認してキャッシュに反映。
本当だったら非同期で情報を更新したいところだけど、Javascriptとか面倒くさいし、メンテやエラーの特定にあっちもこっちもになってコスト高なので却下。手動更新。
見るのが自分ひとり、一般公開はしないということから、表示が重いのは我慢、また、同じ理由でいくつかのリクエストはスルーしてもいいかな、と。
[10/04 12:53:50]
というのが、ログファイルが貯まるとさすがに重すぎるので、このフェーズだけ抜き出したスクリプトを作ってcronで5分間隔で回すようにした。
MentionとFollowは別扱い。
両方とも確認してから返事するなりフォロー返しするなりしてから手動で削除することにした。
FollowをAcceptしたら、Followのjsonを保存して、followersのリストに登録する。
Following、こちらからフォローする場合も同じ。Followingに使ったjsonを保存して、followingのリストに登録する。Followingのjsonを保存するのは、フォロー解除するUndoのために、元になったjsonが必要だから。uuidでIDを作ることにしてしまって、残しておかないと元のjsonを再現できないというオチ。
Mention
連絡先としてサイトや名刺など今まではtwitterのアカウントを入れたんだけど、twitterがなくなったんで、こっちを「連絡先はこちら」にしたい。Mentionを見落としちゃまずいんで、これも確認からの手動にした。
[09/21 10:05:12]
リクエストとして飛んでくるブーストは対応済みで、こちらからブーストのリクエストを飛ばすのも実装してみた。
「おひとり様ActivityPubサーバーにブースト実装」
いろいろ考えなきゃいけないことあった(改めて)
[10/03 10:57:44]
「おひとり様ActivityPubサーバーのjsonの実例」
投稿するのに実際に使ってるActivityPubのJSONの実例をメモした。
[10/06 10:26:14]
「おひとり様ActivityPubサーバーにLike実装」
Likeされる方じゃなくて、する方の実装。
WEB拍手みたい感じで使うのがいいかな、と。
[12/22 07:40:43]
「ActivityPubサーバーのGETリクエストに署名」
GETリクエストでactor情報などを取得する時にHTTP Signatureが必要なサーバーもあった。
GETにも署名することにした。
[12/27 10:01:38]
「おひとり様ActivityPubサーバーにBlock実装」
レアケースだけどBlockも対応した。
[2024/01/23 09:08:36]
「おひとり様サーバーの改修」
Fedibird.comやkmy.blueなど一部のサーバーが実装しているサークル機能に対応…といっても読むだけ。サークル作成やサークル投稿に対する返信は未対応。
■以上、雑なまとめでした■
ディテールというか無駄の多いエントリはカテゴリからどうぞ。
「ActivitiyPub::ひまつぶし雑記帖」
以下のサイトを参考にさせてもらっておひとり様ActivityPubサーバーを実装することができた。感謝深謝。
「NetlifyとSupabaseでほぼ静的なActivityPubサーバ」
「Fediverse入門―非中央集権型SNSサーバを作ろう!」
「田舎の昼のサイレンbotをActivityPubで実装する(マストドンにアカウントを認識してもらう編)」
「ActivityPubの実装についてのメモ」
ActivitiPub本家本元。
「ActivityPub」
「ActivityPubの仕様(和訳)」
[12/13 08:30:17]追記。
Activityなどの実装・対応状況
[済]Create
noteを投げ込むのに使用
[済]Follow
followしたらfollowingリストに追加。json保存。
followされたらfollowersリストに追加
[済]Accept
followされたら許可
[未]Reject
followされたら拒絶
[済]Update
noteやpersonなどオブジェクトの更新…だけど、personだけ対応
[済]Delete
自分がポストしたNoteの削除。
ポストされて流れてきたNoteの削除(20個しか保存しないので、すでに削除済みのことが多いけど念のため)
[未]Add
オブジェクトの追加
[未]Remove
オブジェクトの削除
[済]Block
ブロックされたらリストに登録。間違えてFollowするといけないので、Followリクエストを飛ばす時にブロックリストも確認する。
[済]Like
いいね。
したらjson保存。14日間。
されたら1日だけ表示。jsonは3日間だけ保存。
[済]Announce
ブースト。
したら、7日間ため池データベースに保存。期限がすぎたら削除、undo
されたものは削除
ブーストされたらLikeと同じ対応にした
[済]Undo
やり直し。
ブーストの取り消しとフォロー解除は対応済み。Likeは未対応。
[済]Mention
DMみたいなものだからJSON保存。確認して手動で対応。
[済]Hashtag
お一人さまだと意味はないけどとりあえず
[未]Replies
リプライは実装しない方向で
Json-LDの署名について不明。手がかりにできそうなURL
・ jsonの日本語訳PP版
・ MastodonのJSON-LD署名と検証
・ MastodonのJSON-LD署名の実装rubyのコード
・ JSON-LD関連のコード
・ graph rdfとかよくわからんけど、関係しそうなところ
・ LDのAPI
おひとり様ActivityPubサーバーの自作実装::On Golden Pond
↑まとめはこちら
» ローカル環境で電子書籍を作る、Macアプリ・Windows版ツール 「かんたんEPUB3作成easy_epub」
【電子書籍発売中】