HacoLdevelopers 一般向けのサイト

セルフホスト

HacoL は Compose の6サービスで動きます。既定の経路は、手元の Mac や常時オンの機械で Cloudflare Tunnel を使う経路です。ホストが 80 と 443 を受けられるときは、VPS で 80 と 443 を開ける経路も使えます。どの経路でも、Compose の6サービスは同じです。

このページの内容
  1. Compose の6サービス
  2. 前提
  3. 共通の準備
  4. 経路B 手元の機械で Cloudflare Tunnel を使う
  5. 経路A VPS で 80 と 443 を開ける
  6. LINE と WhatsApp にログインする
  7. 確認する
  8. ログの確認
  9. バックアップと更新
  10. 別のマシンから移すときの注意
  11. 人だけが行う操作
  12. サポートの範囲

Compose の6サービス#

postgres
Synapse、ブリッジ、Context の DB(postgres:17-alpine)
synapse
Matrix のホームサーバー(v1.159.0)
line-bridge
LINE のブリッジ(beeper/line を固定した版。イメージはキットのスクリプトで作ります)
whatsapp-bridge
WhatsApp のブリッジ(mautrix/whatsapp を固定した版。イメージはキットのスクリプトで作ります)
context-app
管理画面と REST API(MCP が呼ぶ API)
caddy
リバースプロキシ(caddy:2.11.4-alpine)

7つ目のサービス、Redis、キューは追加しません。ホストに公開するのは caddy のポートだけです。PostgreSQL は1つのコンテナで4つのデータベースを持ちます。

DB用途
synapseホームサーバー
line_bridgeLINE のブリッジのセッションとポータル
whatsapp_bridgeWhatsApp のブリッジのセッションとポータル
line_context正規化した部屋、メッセージ、監査ログ

前提#

  • 常時オンのホストを用意します。スリープしない Mac や PC、または VPS です。VPS の場合は Ubuntu 24.04 で動作を確認しています。
  • 手元の Mac では OrbStack を使います。代わりに Docker Desktop か Docker Engine も使えます。どれも Docker Compose v2.24 以上が必要です。
  • ドメインを用意します(apex に加えて matrix 用と context 用)。
  • Cloudflare のアカウントを用意します。Tunnel を使う経路では必須です。無料のプランで使えます。
  • LINE にメールアドレスとパスワードでログインできる、個人のアカウントを用意します。
  • WhatsApp を使う場合は、WhatsApp を入れたスマートフォンを用意します(QR の読み取りに使います)。
  • 経路B(既定)手元の機械と Cloudflare Tunnel

    Mac や、ポートを開けられない常時オンの機械で使います。Caddy は 127.0.0.1 だけで待ち受け、外からは Tunnel を通してだけ届きます。手順

  • 経路A VPS で 80 と 443 を開ける

    ホストが 80 と 443 を直接受けられるときの経路です。Caddy が Let's Encrypt で証明書を取ります。手順

どちらの経路でも、Compose の6サービスは同じです。.env の PUBLIC_PATH と COMPOSE_FILE で切り替えます。

共通の準備#

  1. 手順1 .env を作る#

    bash
    cp .env.example .env
    chmod 600 .env

    .env の CHANGE_ME_LONG_RANDOM をすべて置き換えます。値は次のコマンドで作ります。Postgres の初期化 SQL にパスワードを埋め込むので、シングルクォートを含まない値を使います。

    bash
    openssl rand -base64 36

    管理画面のパスワード(ADMIN_PASSWORD)、セッションの秘密(SESSION_SECRET)、MCP が使う API キー(CONTEXT_READ_API_KEY と CONTEXT_AGENT_API_KEY)も .env に入れます。送信の停止スイッチの初期値は SEND_ENABLED=false(送信しない)です。部屋を意図して許可するまで、このままにします。

    やってはいけない.env の中身をチャットに貼らないでください。

  2. 手順2 ブリッジのイメージを作る#

    LINE と WhatsApp のブリッジのイメージは、bootstrap の前に作ります。bootstrap と Compose はブリッジをビルドしません。版は .env の LINE_BRIDGE_COMMIT と WHATSAPP_BRIDGE_COMMIT に固定しています。

    bash
    ./scripts/build-line-bridge-image.sh
    ./scripts/build-whatsapp-bridge-image.sh

    ホストのメモリが少ないとき(4GB ほどの VPS など)は、別のマシンで --save を付けて tar に書き出し、ホストに運んで読み込みます。

    bash
    ./scripts/build-line-bridge-image.sh --save line-bridge-pinned.tar
    ./scripts/build-whatsapp-bridge-image.sh --save whatsapp-bridge-pinned.tar
    # ホストで
    docker load -i line-bridge-pinned.tar
    docker load -i whatsapp-bridge-pinned.tar

経路B 手元の機械で Cloudflare Tunnel を使う#

  • OrbStack の対応は macOS 14 以降です。OrbStack を業務で使う場合は、OrbStack の有料ライセンスが必要です。
  • Mac や PC がスリープすると、Tunnel も Docker も止まり、メッセージを受け取りません。電源につなぎ、スリープしない設定にします。
  • cloudflared は Compose のサービスにせず、ホストで動かします。
  1. 手順1 OrbStack を入れる#

    公式サイトからアプリをダウンロードするか、Homebrew で入れます。

    bash
    brew install --cask orbstack

    OrbStack を起動し、docker と docker compose が使えることを確かめます。

    bash
    docker version
    docker compose version
  2. 手順2 .env を経路B の値にする#

    .env.example の既定は経路B です。example.com を自分のドメインに置き換えます。

    .env
    PUBLIC_PATH=tunnel
    COMPOSE_FILE=compose.yml:compose.tunnel.yml
    CADDY_HTTP_PORT=18080
    BASE_DOMAIN=example.com
    MATRIX_HOST=matrix.example.com
    CONTEXT_HOST=context.example.com
    MATRIX_SERVER_NAME=matrix.example.com
  3. 手順3 bootstrap を実行する#

    bash
    ./scripts/bootstrap.sh

    bootstrap は Synapse に owner ユーザーを作り、LINE と WhatsApp の appservice の登録を Synapse に入れ、6サービスを起動します。経路B では、最後に http://127.0.0.1:18080 の Caddy が応答すれば完了です。Tunnel がまだでも止まりません。

    注意bootstrap は再実行しても既存の owner とトークンを残します。データがある環境では、実行の前に backup.sh を取ります。

  4. 手順4 Tunnel を作り、DNS を向ける#

    bash
    cloudflared tunnel login
    cloudflared tunnel create <tunnel-name>
    cloudflared tunnel route dns <tunnel-name> context.example.com
    cloudflared tunnel route dns <tunnel-name> matrix.example.com

    route dns は Cloudflare に Proxied の CNAME を作ります。A レコードは要りません。設定ファイルは、キットの infra/cloudflared/config.example.yml を写して、Tunnel の ID、認証情報のファイルの場所、ホスト名を自分の値に置き換えます。

    注意認証情報のファイルの中身は秘密です。チャットに貼りません。cloudflared tunnel login で別のゾーンを選ぶと、使うドメインの DNS が更新されません。ダッシュボードで、使うドメインのゾーンを確認します。

  5. 手順5 Tunnel を起動する#

    bash
    CLOUDFLARE_TUNNEL_NAME=<tunnel-name> ./scripts/tunnel-run.sh

    別のターミナルで確認します。

    bash
    ./scripts/doctor.sh
    curl -sI https://context.example.com/health | grep -i cf-ray

    常に動かすときは、cloudflared をサービスとして登録します(Mac では launchd に登録されます)。

    bash
    sudo cloudflared service install --config "<config-file>"

待ち受けるポート

ポート用途
127.0.0.1:18080cloudflared の転送先(HTTP)。管理画面のログインには使いません
127.0.0.1:18443同じホストからの管理画面のログイン(HTTPS。証明書は Caddy の内部 CA)

やってはいけない18080 と 18443 を外へ出さないでください。ルーターのポート転送、ngrok、ssh -R などで公開しません。18080 に接続するのは、同じホストの cloudflared だけです。

Tunnel のよくある失敗

症状意味まずやること
Cloudflare の 1033 か 530Tunnel が止まっています(スリープ、cloudflared の停止)Tunnel を起動し、docker compose ps を確認する
502Tunnel は動いていますが、Caddy や Compose が応答していません127.0.0.1:18080 にローカルで curl し、docker compose logs を見る
bash
curl -sS -o /dev/null -w '%{http_code}\n' -H 'Host: context.example.com' http://127.0.0.1:18080/health

経路A VPS で 80 と 443 を開ける#

  1. 手順1 DNS とファイアウォール#

    • matrix と context の A レコードを VPS に向けます。Cloudflare を使う場合も、この経路ではプロキシを使わない設定(DNS only)にします。VPS に IPv6 が無ければ AAAA は足しません。
    • 開けるのは TCP 22、80、443 だけです。5432、8008、29318、29322、3000、18080 は公開しません。
    • Caddy が Let's Encrypt で証明書を取ります。
  2. 手順2 .env を経路A の値にする#

    .env
    PUBLIC_PATH=public
    COMPOSE_FILE=compose.yml
  3. 手順3 bootstrap を実行する#

    bash
    chmod +x scripts/*.sh
    ./scripts/bootstrap.sh

    DNS が VPS を向いていないときは、bootstrap が止まって直し方を表示します。

LINE と WhatsApp にログインする#

非公式のブリッジですLINE と WhatsApp のブリッジは非公式です。LINE 公式の Messaging API ではなく、LINE ヤフー社との提携もありません。upstream の変更で止まることがあり、アカウントがログアウトされたり、利用を制限されたり、停止されたりするおそれがあります。動き続けることやログインの継続は約束しません。

管理画面のページすること
/setup/line(LINEの接続)LINE に登録したメールアドレスとパスワードを入力して「ログイン」を押します。表示された PIN を、スマートフォンの LINE に入力します。
/setup/linkLINE の PIN と WhatsApp の QR を表示します。「WhatsAppのログインを始める」を押し、QR をスマートフォンの WhatsApp で読み取ります。
/setup/invites(トークルーム)トークルームへの参加の進み具合を表示します。AUTO_ACCEPT_INVITES=true(既定)のとき、ブリッジからの招待は自動で参加します。ほかの招待は、「参加する」を押すまで取り込みません。
  • 経路B では、同じホストのブラウザで https://127.0.0.1:18443/setup/line を開きます。平文の HTTP では管理画面にログインできません。
  • 資格情報は、管理画面の入力欄にだけ入力します。ブリッジが Unknown command と返したときは、ログインの案内の期限が切れています。「やめる」を押してから、もう一度送ります。
  • 部屋に参加しても、送信は許可されません。読むための参加と、送信の許可は別の操作です。
  • 送信を許可する前に、安全なテスト用のチャットで、自分の LINE アカウントのやりとりとして取り込まれることを確認します。
  • LINE アプリで自分が送ったメッセージが受信として表示されるときは、.env の LINE_OWNER_GHOST_MXIDS に自分の LINE ghost の MXID を書きます(FAQ)。

やってはいけないLINE の Chrome 拡張は使わないでください。資格情報をチャットに書かないでください。

確認する#

診断のスクリプトを実行します。最後の行で結果を判断します。FAIL が1つでもあると、終了コードが1になります。WARN だけなら PASS です。

bash
./scripts/doctor.sh
text
Doctor finished with PASS (N warnings)   → 終了コード 0
Doctor finished with FAIL               → 終了コード 1

続けて smoke-test を実行します。実在しない部屋の ID だけを使い、LINE や WhatsApp へ実際には送信しません。read キーで送信できないこと、許可していない部屋への送信が拒否されることなどを確かめます。

bash
./scripts/smoke-test.sh

最後の行が Smoke test passed (no real LINE or WhatsApp send was attempted). なら成功です。あわせて次を確認します。

  • https://context.example.com/health が 200 を返す
  • https://matrix.example.com/_matrix/client/versions が 200 を返す
  • docker compose ps で6サービスが Up
  • MCP の line_list_rooms が部屋の一覧を返す

注意サービスを上げ直すときは docker compose up -d を使い、--build は付けません。/health は matrix_token: present|invalid も返しますが、トークンの値は返しません。

ログの確認#

bash
docker compose ps
docker compose logs --tail=200 postgres synapse line-bridge whatsapp-bridge context-app caddy
症状意味まずやること
MCP の 401キーがずれていますAPI キーを入れ替えるときの手順を実行する
doctor が FAIL6サービス、Synapse への登録、Context の応答などのどれかに問題がありますFAIL の行を読み、docker compose logs を見る
管理画面のログインが 429同じ接続元から15分間に5回失敗しました15分待つか、context-app を再起動する

バックアップと更新#

bash
./scripts/backup.sh
BACKUP_SECRETS_PASSPHRASE='<long-random-passphrase>' ./scripts/backup.sh --include-secrets-encrypted
  • オプションなしの backup.sh は、4つの DB のダンプを書き出します。ブリッジのセッション、そのほかの資格情報、.env、.runtime.env、署名鍵、メディアは含めません。このバックアップから戻したときは、LINE と WhatsApp にログインし直します。
  • --include-secrets-encrypted を付けると、セッション、秘密のファイル、メディアを含めて暗号化します。BACKUP_SECRETS_PASSPHRASE を設定します。戻すときも同じパスフレーズが要ります。
  • Context の DB のダンプには、メッセージの本文が平文で入ります。バックアップは秘密と同じように扱います。
bash
LINE_CONTEXT_DESTROY=yes BACKUP_SECRETS_PASSPHRASE='<long-random-passphrase>' \
  ./scripts/restore.sh --yes-i-understand --in-place backups/<stamp>
  • restore.sh は、--in-place(いまの構成のデータを置き換える)か --project(別のプロジェクトに戻す)のどちらか1つを指定したときだけ動きます。データを上書きするので、--yes-i-understand と LINE_CONTEXT_DESTROY=yes が要ります。
bash
BACKUP_SECRETS_PASSPHRASE='<long-random-passphrase>' ./scripts/update-line-bridge.sh <40-character-commit-sha>
BACKUP_SECRETS_PASSPHRASE='<long-random-passphrase>' ./scripts/update-whatsapp-bridge.sh <40-character-commit-sha>
  • ブリッジを更新するときは、40文字のコミット SHA を渡します。更新のスクリプトは main を自動では追いません。BACKUP_SECRETS_PASSPHRASE が無いと始まらず、始める前に暗号化したバックアップを取ります。
  • コンテナを更新するときは、6サービスのまま版の固定を更新し、起動のあと ./scripts/doctor.sh、health、MCP を確認します。
  • 2026-09-30 より前に作った環境には、管理用の部屋の本文が残っていることがあります。バックアップを取ってから、./scripts/purge-sensitive-bodies.sh --yes-i-understand で一度だけ消せます。

別のマシンから移すときの注意#

  • LINE と WhatsApp は、2台で同時にログインすると片方の接続が切れやすくなります。移すときは、元のマシンのブリッジを止め、データを最後に同期してから、新しいマシンで起動します。
  • 同期するのは data/postgres、data/synapse、data/line-bridge、data/whatsapp-bridge、.env、.runtime.env、必要なら backups/ です。.env と .runtime.env は、チャットを通さずに別の経路でコピーします。
  • macOS に付いている rsync には --info=progress2 が無いことがあるので、--progress を使います。

人だけが行う操作#

  • 支払いと購入
  • アカウントの作成(Cloudflare、VPS)と、LINE のメールアドレスとパスワードの入力、WhatsApp の QR の読み取り
  • ドメインの選択と購入
  • 2段階認証と OS の同意

やってはいけない秘密の値を AI のチャットに貼らないでください。

サポートの範囲#

  • 対象

    1人の運用者が動かす、1つの構成です。

  • 対象外

    マルチテナントの運用、他社向けの再販 SaaS、Compose へのサービスの追加です。標準の構成では Remote MCP を公開しません。

最終更新 2026年10月20日