今回は備忘録としてこのブログについての解説記事を書いていきます。

きっかけ

自宅サーバーでMisskeyを始めたり他にも様々なWebアプリをホスティングしていく中で、 次第に外部サービスにデータを預けたくないなと思い始めたのがきっかけです。1
特にZennが出始めたころ、何も考えずにQiitaからZennに乗り換えたら、いつまでも自分のブログを作らないままかもしれないと思っていました。 プラットフォームを乗り換えるくらいなら、自分でブログを運営しようと考えていました。 また、普段からMisskeyを通じてFediverseを楽しんでいたので、作るならFediverseにもつながるブログにしたいと思いました。 そこで、必要な機能を整理し、それを実現できる仕組みを探すところから始めました。

開発の流れ

開発は以下の流れで行いました。

  1. 要件定義・技術選定
    • Claudeと壁打ちをして方針を固める
  2. 実装・動作確認
    • 実装はGitHub Copilotに丸投げ、人間は動作確認をしてフィードバックをする
  3. デプロイ

要件

Claudeと壁打ち・技術選定をする中で決めた要件は次の通りです

docker composeで動かすようにする

Misskey開発や業務でメンテナンスの大変さを体感していたため、メンテナンス性を意識する上でなるべく自前のプログラムは減らしたいという思いがありました。 そのため、なるべく既存のOSSの公開されているイメージを活用し、コマンドを1回叩くだけで開発/本番環境が立ち上がるようにすることを要件に加えました。 これの副次的効果として開発・運用時にホストの環境を汚さず、環境構築の手間を大幅に省くことができました。

記事ごとにディレクトリを作れるようにする

記事と画像などを別々の場所に保存すると、対応関係が分からなくなったり、探す手間が増えたりします。 そのため、記事ごとにディレクトリを作り、記事とメディアを一緒に保存できることを要件にしました。

記事はMarkdownで書けるようにする

軽いメモを取るときでもMarkdownで書いてしまうくらいMarkdownに飼い慣らされてしまったのでこれは必須だと考えていました。 直近で自分のHPもMarkdownで書けるようにしたらものすごく快適になったからというのもあります。

記事はFediverseにも配信する

Fediverse上のメジャーなプラットフォームであるMastodonやMisskeyはどちらも(マイクロ/ミニ)ブログサービスを提供するためのプラットフォーム基盤と謳っています。

Mastodon

Your self-hosted, globally interconnected microblogging community

Misskey

Misskeyはオープンソースの分散型マイクロブログプラットフォームプロジェクトです。

加えて分散型SNSユーザー有志のホームページでは以下のように接続できるプラットフォームの例としてマイクロブログ・ブログを最初に挙げています。

マイクロブログ、ブログ、フォトブログ、ビデオホスティングなどの多様なプラットフォームが接続されています。

であるならばブログをFediverseに配信するのは当然でしょう。

記事に対するFediverse上での反応(いいね・ブースト/リノート・リアクション・リプライ・引用)はすべて記事上から見れるようにする

自分がなぜここまでMisskeyにハマっているのかを考えたときに真っ先に浮かんだのがいろんな人から多様なリアクションを受け取れることでした。 そのため、Fediverseにブログ記事を配信する際にこの楽しみが失われてしまっては勿体ないと考え、要件に加えました。 記事に反応がたくさんついているとちゃんと読まれているんだなと感じ、承認欲求が満たされて精神衛生上とてもよいです。

技術選定

WordPress+ActivityPubプラグイン

この要件を満たすことができるOSSがないかClaudeに聞いたところ、WordPress+ActivityPubプラグインをおすすめされました。 しかしphp2shellでPHPを使っているサービスが軒並みメンテナンスに入って大変なことになっているのを見ていたこと、PHPは脆弱性の数でアピールしていることから自宅でホスティングしてメンテナンスしていくのはかなりきついだろうと思い採用を見送りました。

WriteFreely

つぎにおすすめされたのがWriteFreelyでした。

https://writefreely.org

これひとつでFediverseとの連携が可能でかなり魅力的に思えたのですが以下の点から採用を見送りました。

Hugo+GoToSocial+GoatCounter

次におすすめされたのがHugo+GoToSocial+GoatCounterの構成でした。

https://gohugo.io

https://gotosocial.org

https://www.goatcounter.com

これは実際にClaudeに実装させてローカルでの動作環境を試してみました。 しかし試しているうちに公開されているリアクションを読み取るだけでも認証が必要になり、サーバー側で認証をしアクションを中継する常駐プログラムが必要になることから採用を見送りました。

Madblog

これを踏まえてブログとFediverseサーバーが一体化したOSSを使った方がいいのではとなり、Claudeに調査させたところMadblogが見つかりました。

https://github.com/blacklight/madblog

実際に使って試してみたのですが、以下の点から採用を見送りました。

  • 記事とメディアデータの保存先のディレクトリが異なる
    • メディアデータについては/img直下にサブディレクトリを作成できない
  • x64向けイメージしか配布していない
    • Raspberry Piなどでホスティングしたくなったときにイメージをビルドしなくてはならず、手間が増える
  • 数式を表示できるようにするためにフルでイメージをビルドするとイメージサイズが2GBを超える

    Includes all plugins - including LaTeX and Mermaid; > 2 GB in size.

  • バックエンドがPython
    • 個人的にバックエンドがPythonのWebアプリはなるべく動かしたくないという思いがあった(個人の感想)

最終的な構成

最終的にHugoで記事ごとにディレクトリを作成できるなど執筆体験が非常によかったため、以下のようなHugo+Hollo+GoatCounterの構成にしました。

https://docs.hollo.social/

Hugo+GoToSocial+GoatCounterの構成とかなり似ていますが、投稿・リプライ数・フォロワー数を認証無しで読み取ることができ、中継するための常駐プログラムが不要であるからHolloを採用しました。

システム構成図
システム構成図

それぞれの役割はこのようになっています。

  • Caddy: Hugoが生成したページを配信し、連合や計測の要求をHollo・GoatCounterに転送する
  • Hugo: ブログ本体
  • Publisher: RSSから新しい記事を見つけ、Holloに投稿する
  • Hollo: 新規投稿記事のリモートへの配送とリアクションの受信を行う
  • GoatCounter: ブログ記事の閲覧数を取得する

ブログのページでは、HolloとGoatCounterから反応や閲覧数を取得し表示しています。 返信や引用は、Holloが取得できる範囲のものを表示しています。

工夫した点

工夫した点として初回の管理者作成画面は公開側からのアクセスを遮断し、外部に公開しない管理用ポートから設定するようにした点があります。

Holloは初回起動直後にページにアクセスし、アカウントを作成する必要があります。 しかしこれをCloudflare Tunnelで公開した状態でその公開URLにアクセスし、アカウント作成を行おうとすると悪意のある第三者が先にアクセスしてアカウントを作成してしまう懸念がありました。 そのため、Holloの初回の管理画面には外部には公開しない管理用ポートからしかアクセスできないようにすることでこの問題を解決しました。 普段Holloの管理画面に入ることはほとんどないのでこの機能により安心感は増したのですが、いざ管理画面に入りたくなったときに管理用ポートを開くのが面倒でもうすこしいい方法がなかったかなと思っています。

今後の展望

今後以下の機能・ページを実装したいと考えています。

  • aboutページの追加
  • フォロワー一覧を表示できるように
  • 各記事から前のエントリと次のエントリに飛べるように

Fediverseの連携機能は現状満足していますが見た目の作り込みはまだ甘いと感じています。 そのため、フロントエンドの強化を今後頑張る予定です。

最後に

今回作成したブログシステムはGitHub上で公開しているのでぜひ使ってみてください。

https://github.com/jj1guj/blog

今後もいろんな記事を投稿する予定なので以下のハンドルをフォローしたり記事にリアクションやリプライをくださると嬉しいです!

@[email protected]


  1. といいながらGoogleに年間1.5万円くらい払ってデータを全部預けるGoogleの犬をやっている ↩︎