Matrix のサーバー一式(m.doany.io)。Zulip からの移行先(2026-10-06 に移して、Zulip は畳んだ)。
Zulip の最後のバックアップ(pg_dump と添付ファイル)は R2 に final / retired のタグで残っている。
| 名前 | 何か | ここで動くか |
|---|---|---|
| Matrix | チャットのプロトコル(メールでいう SMTP) | ─ |
| Element | 使うアプリ。スマホは Element X、PC は Element Desktop か https://e.doany.io | Web 版だけ動かす(element-web.yaml) |
| Tuwunel | Matrix のサーバー(tuwunel.yaml)。DB(RocksDB)も中にある | 動かす |
| hookshot | 外からの通知をルームに流す(hookshot.yaml) | 動かす |
| LiveKit | 通話(Element Call)の音声と映像の中継(livekit.yaml、lk-jwt-service.yaml)。lk.doany.io |
動かす |
- サーバーは Tuwunel 1 つ。 Tuwunel は自前の OAuth/OIDC サーバーを持ち、ログインを Entra に回せる。
スマホの Element X は「OAuth か パスワード」しか受け付けない(legacy SSO を使わない。element-x-android の
LoginModePresenter.kt)ので、Synapse だと MAS と Postgres が別に要る - Element Web だけ自分で立てる(
e.doany.io)。サーバーを doany.io に決め打ちにし、外のサービス (統合マネージャー、利用統計など)を切ってある。中身は静的なファイルなので軽い。スマホと PC は公式のアプリ - サーバー(
m.doany.io)は隠せない。 Element Web はブラウザの中で動き、ブラウザが直接サーバーと話す。 スマホのアプリと外からの webhook も同じ。Web とサーバーのドメインを分けているのは、一緒にする利点が薄いため - フェデレーションは無し。 家の中だけで使う
- 日本語の検索は弱い。 Tuwunel は記号と空白でしか単語を区切らないので、ひらがなや漢字の続きは
ひとかたまりでしか当たらない(
src/service/rooms/search/mod.rs)。Synapse も英語用の検索に決め打ちで (to_tsvector('english', …))、日本語圏の共通の悩み。後から見たい情報はメッセージではなくドキュメントに書く 前提で選んだ(本人、2026-10-06) - Zulip の履歴は持ってこない。 Mattermost からも Zulip からも、Matrix への公式の取り込みが無い
- 通知のルームは暗号化しない。 hookshot は暗号化したルームに書けない(Redis と暗号化の設定が要る)。 Element は非公開のルームを既定で暗号化するので、通知のルームは作るときに暗号化を切る
Entra だけ(Tuwunel の [[global.identity_provider]])。パスワードのログインは無い。
- ID は
@<メールの @ の前>:doany.io(Entra の userinfo にpreferred_usernameが無いためemailから作る)。 info@doany.io は@info:doany.io - 初回のログインで自動で作られる。最初にログインした人が管理者になる。新規登録の画面は無い (誰が入れるかは Entra が決める)
- リダイレクト URI は
pulumi/entra.ts(https://m.doany.io/_matrix/client/unstable/login/sso/callback/<client_id>) doany.io/.well-known/matrix/clientだけ Tuwunel に回している(httproute.yaml)。 Element でサーバーを聞かれたらdoany.ioと入れればよい
**スペース「通知」**にまとめてある(2026-10-07)。部屋の名前は接頭辞を付けず短く(server・argocd・ashi・
todoroku・worklog・denpa・forgejo・feed)。Slack のような notify- の接頭辞の代わりにスペースで束ねる
(名前は表示用で、hookshot も webhook も部屋の ID で動くので、変えても影響しない)。
2026-10-06 に notify-* として 8 つ作り、notify-github は 2026-10-07 に畳んだ。Zulip のメッセージは持ってきていない(ほとんどが bot の通知のため)。
- 作ったのは hookshot(
@hookshot:doany.io)。@info:doany.ioも作成者(room version 12 のadditional_creators) なので、権限は最上位で外されない - 暗号化していない。 hookshot は暗号化したルームに書けない。暗号化を後から入れると戻せないので、入れないこと
- webhook は hookshot.yaml の
connections。URL はhttps://m.doany.io/webhook/<ID>で、ID は Infisical/matrix/matrixのhook-<部屋>(公開のリポジトリなので git に置かない) - 投稿は全部 m.notice(bot の発言)(2026-10-07)。通知と音は Element の設定で決める: グループの会話は「音あり」、bot のメッセージは「オン」(音無しで通知)、要らない部屋は部屋ごとに「メンションのみ」。 (以前は通知したい部屋だけ m.text で投稿していたが、グループの会話を音ありにすると bot まで鳴るのでやめた)
| ルーム | 送り元 | URL の在処 |
|---|---|---|
server |
k8up の通知 | Infisical /k8up/k8up-global の mattermostWebhook(名前は据え置き) |
| 〃 | Infisical(秘密の変更) | Infisical のプロジェクトの webhook(Project Settings → Webhooks。環境 prod、パス /) |
| 〃 | Cloudflare(証明書・DDoS・オリジン不達など) | Cloudflare の通知の webhook の送り先(../../pulumi/notifications.ts) |
| 〃 | ホストのバックアップ | ホストの /etc/k3s-backup/env の MATTERMOST_WEBHOOK(sed で書き換えない。URL の & で壊れる) |
argocd |
Argo CD | bootstrap/argocd/helmchart.yaml(SOPS)の service.webhook.mattermost |
ashi |
ashi | Infisical /ashi/ashi-secrets の notify-webhook-url |
todoroku |
todoroku | Infisical /todoroku/todoroku-secrets の mattermost-webhook-url |
worklog |
worklog | Infisical /worklog/worklog-secrets の mattermost-webhook-url |
denpa |
denpa | denpa の DB(webhooks テーブル。画面の設定) |
forgejo |
Forgejo | Forgejo の組織 doa の webhook(種類は Slack。DB の webhook id 2) |
足すとき:ルームを暗号化なしで作り、hookshot を招待して権限を上げる → Infisical に hook-<部屋> を足す →
connections と render の env に足す。
使っていない(2026-10-07)。GitHub の通知は GitHub のアプリで受ける。GitHub App doa-hookshot は消したので、
hookshot の設定からも github: を外した。App を消して設定を残すと、hookshot が Integration not found で
起動しなくなり、通知が全部止まる(2026-10-07 に踏んだ)。また入れるなら、GitHub App を作り直して
github:・秘密・/github の経路・@_github_ の名前空間を戻す(git の履歴の #243 が手順)。
hookshot の feeds で RSS / Atom を購読する(10 分おき)。購読はルームに feed の接続(state)を書く。
ルームで !hookshot feed <URL> を打つか、hookshot の bot として state
uk.half-shot.matrix-hookshot.feed(state key と本文の url に URL)を書く。
流す先はスペース「通知」の feed(2026-10-07 に作った。暗号化なし、@info:doany.io も作成者)。
| 購読 | URL |
|---|---|
| doany.io のブログ | https://doany.io/rss.xml |
LiveKit が Matrix の通話(Element Call)の音声と映像を中継する。2026-10-06 に入れた。手順の元は Tuwunel の
docs/calls/matrix_rtc.md。
| 部品 | 役目 | 在処 |
|---|---|---|
| LiveKit | 音声と映像の中継(SFU) | livekit.yaml |
| lk-jwt-service | Matrix のユーザーを確かめて、LiveKit に入るトークンを出す | lk-jwt-service.yaml |
- Element が Tuwunel に聞く(
/.well-known/matrix/clientかrtc/transports)→https://lk.doany.io(Tuwunel のlivekit_url。tuwunel.yaml) - Element が Tuwunel の OpenID のトークンを
lk.doany.io/get_tokenに出す。lk-jwt-service はdoany.io/.well-known/matrix/server→m.doany.ioの/_matrix/federation/v1/openid/userinfoで確かめる (フェデレーションは切っているが、この口は開いている) - もらったトークンで
wss://lk.doany.io(LiveKit の合図)につなぐ - 音声と映像は ノードの 8443(UDP。だめなら TCP) に直接流れる
8443 の UDP と TCP をノード(10.0.0.2)に転送しておくこと。 Mattermost Calls のときと同じ番号なので、 その転送をそのまま使う(消していなければ)。Gateway と Cloudflare は通らない。
外から見える IP は LiveKit が STUN で調べる。家の中の端末も外の IP に向けて送るので、ルーターの NAT ループバック(ヘアピン)が要る。家の中だけつながらないときはこれを疑う(Tuwunel の docs の Troubleshooting)。
Infisical /matrix/matrix の livekit-secret。API キーの名前は matrix(秘密ではない)。
- TURN(LiveKit 内蔵のもの)。UDP がだめなときの TCP 8443 で足りる見込み。会社のネットワークなど 8443 も塞がれた所からつなぐなら、TURN の TLS(443)が要る
- 昔ながらの 1 対 1 の通話(Element Web の「レガシー」)。Element Web は Element Call だけを使う設定
(
element_call.use_exclusively)にした。Element X と同じ仕組みに揃える
k8up(毎日 13:30 UTC)。Tuwunel の DB は動いたまま写すと壊れうるので、ファイルとしては取らない。
- k8up が backup サイドカーの
k8up.io/backupcommandを流す - サイドカーが Tuwunel に SIGUSR2 を送る。Tuwunel は
admin_signal_executeでserver backup-databaseを流し、 整合の取れたバックアップをbackups/に作る(2 世代) - できたら
backups/とmedia/(アップロードされたファイル)を tar で k8up に渡す
戻すとき:tar を PVC に展開し、Tuwunel を 1 回だけ tuwunel --restore-backup で起動する
(Tuwunel の docs/backups.md「Restoring a managed backup」)。