ネットワークデプロイメント
ZeroClaw をデプロイして外部からの接続を受け入れるための設定:ゲートウェイの公開、Webhook チャネル、トンネル、LAN 内限定と公開向けの構成。Raspberry Pi やその他の家庭用ネットワークホストは、ここで主要な対象となります。
インバウンドポートが重要な場合
| モード | インバウンドポート? | 注釈 |
|---|---|---|
| Telegram(ロングポーリング) | いいえ | ZeroClaw は api.telegram.org をポーリングし、NAT 環境下でも動作します |
| Matrix / Mattermost / Nextcloud Talk | いいえ | 同期/WebSocket、送信のみ |
| Discord / Slack(Socket Mode) | いいえ | アウトバウンド WebSocket |
Signal (signal-cli-rest-api) | いいえ | ローカルホストのコンテナ |
| Nostr / IMAP / MQTT | いいえ | すべてのアウトバウンド |
| Webhooks(GitHub、Slack Events API、WhatsApp、Nextcloud Talk ボット、カスタム) | はい | 公開のPOSTエンドポイントが必要です |
| LANからのゲートウェイペアリング | はい(LANスコープ) | 0.0.0.0 にバインドするか、トンネルを使用してください。 |
| Discord / Slack (HTTP イベント) | はい | Socket Mode を使用しない場合 |
結論: Telegram専用のbotは、ポートフォワーディングなしで、家庭用ルーター背後のPi上で動作します。webhookベースのものはすべて到達可能なURLが必要であり、そこでトンネルの出番となります。
ゲートウェイのバインド
デフォルトでは、ゲートウェイは 127.0.0.1 にバインドされ、他のデバイスからはアクセスできません。これを公開する方法は3つあります。
オプション1: パブリックバインド (LAN)
LAN 上の任意のデバイスから http://<pi-ip>:42617 にアクセスできるようになります。インターネットから到達可能な webhook には役立ちません。ルーターのパブリック IP は Pi に転送されないためです。
安全性: 0.0.0.0 へのバインドは重大な設定変更となるため、allow_public_bind = true の設定が必要です。この設定がない場合、デーモンはバインドを拒否します。これは意図的な動作です。
オプション2: トンネル(インターネット経由でアクセス可能)
その後、デーモンを再起動すると、トンネルは設定から宣言的に管理され、ゲートウェイと一緒に起動します。
トンネルはパブリックURLからゲートウェイ(127.0.0.1)へ転送します。ルーター設定もポート開放も不要です。tunnel.tunnel_provider をサポートされている値のいずれかに設定してください。いずれも同様に動作します。
| プロバイダー | セットアップの摩擦 | コスト | 良い |
|---|---|---|---|
tailscale | アカウント + クライアント | 無料ティア | 長期にわたる安定したURL |
cloudflare | アカウント + cloudflared + トークン | 無料 | カスタムドメイン |
ngrok | アカウント + エージェント + トークン | 制限付きで無料 | テスト、短命 |
pinggy | SSH、アカウントなし | 無料ティア | 短時間のワンショットURL |
openvpn | 自分の OpenVPN 出口 | セルフホスト | 既存のVPNインフラ |
custom | [tunnel.custom] 配下のコマンド | 依存関係 | その他 |
tunnel_provider = "none"(デフォルト)の場合、ゲートウェイはローカルのままでトンネルを使用しません。各プロバイダーの [tunnel.<provider>] フィールドについては、設定リファレンスを参照してください。
Tailscale: WSS とエンロールメント
tunnel_provider = "tailscale" の場合、ゲートウェイは tailscale serve で公開されます([tunnel.tailscale].funnel = true の場合は tailscale funnel で公開されます)。[wss] が有効な場合、デーモンは tailnet 上で WSS RPC リスナーも公開し、[enroll] が有効な場合はエンロールメントエンドポイントも公開します。それぞれが独自のポートを保持します:
tailscale serve --tcp <wss.port> tcp://127.0.0.1:<wss.port>
tailscale serve --tcp <enroll.port> tcp://127.0.0.1:<enroll.port>
これらは生の TCP パススルー転送です。TLS は引き続きデーモン内で終端されるため、WSS プレーンは相互認証の状態を保ち、登録時の short-auth-string もデーモン自身の CA に紐付けられたままです。Tailscale の HTTPS プロキシはクライアント証明書を取り除いてしまうため、これらには使用されません。ワイルドカードバインド(0.0.0.0 / ::)はループバックに転送され、特定のバインドアドレスはそのアドレスに転送されます。
知っておくべきこと:
- Tailnet限定(Funnelを使用する場合も)。 Funnelが待ち受けるのはポート443、8443、10000のみです。mTLSプレーンをパブリックインターネットに公開するかどうかは、明示的に選択すべきものです。ゲートウェイをFunnelで公開しただけで自動的に有効になるものではありません。tailnet外からWSSに到達するには、
[relay]を使用してください。 - 証明書名は自動的に設定されます。 WSS およびエンロールメントのリスナーに Tailscale 経由でアクセスされる場合、デーモンは起動時に、自動生成されるサーバー証明書へノードの tailnet 名を追加します。対象は MagicDNS 名(
<node>.<tailnet>.ts.net)、ショートネーム(<node>)、ノードの tailnet IP、そして設定されている場合は[tunnel.tailscale].hostnameです。「Tailscale 経由でアクセスされる」とは、このトンネル経由であるか、[wss].bind/[enroll].bindが tailnet アドレス(100.64.0.0/10またはfd7a:115c:a1e0::/48)に直接設定されていることを指し、トンネルの有無は問いません。名前の検索はデーモンの起動ごと(およびリロードのたびに)1 回行われ、両方のリスナーで共有されるため、WSS とエンロールメントは常に同じ証明書を提示します。名前が変わった場合に再生成されるのはサーバー証明書のみです。CA は変わらないため、登録済みのクライアントは引き続き動作します。起動時に tailscaled に接続できない場合、またはネットワークマップを受信する前に応答した場合(ブート直後によく発生します)、デーモンは警告をログに出力し、既存の証明書が持つすべての名前を保持します。新しい[wss].sansのエントリは適用されますが、削除は tailscaled が応答した起動時まで保留されます。tailscaled が停止している状態での新規インストールでは、証明書には tailscaled なしで判明する名前(localhost、127.0.0.1、[wss].sans、[tunnel.tailscale].hostname)のみが含まれます。残りを追加するには、tailscaled が起動した後に再起動してください。ユーザー自身が用意したcert_pathの証明書は、決して変更されません。 - 登録のロックアウトは共有される。 転送された接続はループバックから登録エンドポイントに到達する。そのため、テイルネットの登録者全員が単一のペアリング試行ロックアウトを共有することになり、あるテイルネットピアからの不正なコードの繰り返しは、他の登録者の登録を一時的にブロックする。このロックアウトはフェイルクローズドで動作する。ペアリングコードによるゲートを弱めることは決してない。
- 転送は、ゲートウェイが所有するフォアグラウンドの
tailscale serveセッションのため、ゲートウェイが停止すると取り下げられます。後で単独で終了した場合、デーモンはtailscale serve --tcp exited after publicationという警告をログに記録します。ゲートウェイを再起動すると再公開されます。 - 登録はCAモードに従います。 独自のクライアントCA(
ca_cert_pathを指定して[wss.client_auth]が有効な場合)を使用する場合、デーモンは署名鍵を保持せず、登録エンドポイントも実行しないため、それも公開されません。
オプション3: リバースプロキシ
nginx / Caddy / Traefik をゲートウェイの前に配置し、そこで TLS を終了し、localhost:42617 にプロキシします。以下に適しています:
- 実在するパブリックIPアドレスを持つサーバー
- Let’s Encrypt を使用した既存のリバースプロキシ設定
- 同じホストで複数のサービスを提供する
最小限のCaddy設定:
agent.example.com {
reverse_proxy localhost:42617
}
ゲートウェイは 127.0.0.1 にバインドされたままで、リッスンはプロキシが行います。
汎用ゲートウェイの Webhook 認証
ゲートウェイの POST /webhook と SOP 専用の POST /sop/* ルートでは、ペアリングとは独立して、共有シークレットヘッダーの完全一致を要求できます:
[gateway]
webhook_secret = "replace-with-a-random-secret"
値を X-Webhook-Secret として送信します。require_pairing = true で webhook_secret が設定されている場合、呼び出し元はペアリング済みの Bearer トークンと webhook シークレットの両方を送信する必要があります。汎用ゲートウェイシークレットは [channels.webhook.<alias>].secret とは意図的に分離されています。チャネルエイリアスは独自のリスナーを実行し、代わりに本文の HMAC 検証を使用します。
ゲートウェイ設定の書き込みは、稼働中のゲートウェイ設定ビューを通じて反映されます。ファイルを直接編集した場合は、通常のデーモンの再読み込み(またはゲートウェイ単体の再起動)が必要です。
リモートデーモンの再読み込み
POST /admin/reload は config.toml を再読み込みし、すべてのサブシステムをその場で再構築します(PID は同一、ダウンタイムは 1 秒未満)。これは、完全な再起動を行わずに設定変更を適用するためにサポートされている方法です。デフォルトではループバックからの呼び出しのみを受け付けるため、リモートのダッシュボードや別マシンからの curl は 403 Forbidden を受け取ります。
認証されたリモートからのリロードを許可するには:
[gateway]
allow_remote_admin = true # off by default
require_pairing = true # required for remote reload (also the default)
これを有効にすると、ループバック以外の呼び出し元が /admin/reload にアクセスできるのは、ペアリング認証も通過した場合のみ です(Authorization: Bearer <token>)。ループバックの呼び出し元(ローカルのCLI)は常に許可され、トークンは不要です。/admin/shutdown とペアリングコードのエンドポイントは、このフラグに関係なくlocalhostからのみアクセス可能です。
リモートアクセスはペアリングを通じて強制されるため、require_pairing も有効になっていない限り allow_remote_admin は効果を持ちません。ペアリングが無効の場合、リモートの呼び出し元は認証できないため、リクエストは匿名で許可されるのではなく 403 Forbidden で拒否されます。これにより、単一のフラグを切り替えるだけで未認証のリモートリロードを公開してしまうことが不可能になります。
安全性: 別のホストからリロードする必要が特にない限り、allow_remote_admin はオフのままにしてください。リロードが匿名でトリガーされないように、require_pairing = true(デフォルト)を維持してください。
Raspberry Pi のデプロイメント
前提条件
- Raspberry Pi 3/4/5(または同様のSBC)とRaspberry Pi OSまたはAlpine
- ネットワーク接続(WiFi またはイーサネット)
- オプション: ハードウェア統合用のUSB周辺機器
インストール
クローンしてインストーラーを実行します。フラグを指定しない場合、対話形式の選択画面が表示され、ビルドタイプとコンパイルに含める機能(GPIO/I2C/SPI のハードウェア機能を含む)を選択できます。Pi では Pi 向けに調整された cargo プロファイルも使用します。スワップの設定とモデルごとのビルドマトリックスについては、Raspberry Pi のセットアップ を参照してください。
Raspberry Pi OS
git clone https://github.com/zeroclaw-labs/zeroclaw.git
cd zeroclaw
./install.sh
Alpine
apk add curl rust cargo openssl-dev pkgconf git
git clone https://github.com/zeroclaw-labs/zeroclaw.git
cd zeroclaw
./install.sh
ハードウェア機能を選択すると、rppal を介して GPIO、I2C、SPI へのアクセスを許可します。標準のサービスユニットでは、ユーザーがすでに gpio、spi、i2c グループに追加されています。
チェックリスト
- バイナリをインストールします(
./install.sh、ピッカーで機能を選択してください) -
zeroclaw quickstartを実行します - チャンネルを設定します。Telegramにはポートが不要ですが、Webhookにはトンネルが必要です
- サービスをインストールします:
zeroclaw service install && zeroclaw service start - LAN アクセスの場合:
[gateway] host = "0.0.0.0"およびallow_public_bind = trueを設定 - Webhookの場合、
[tunnel]をプロバイダーで設定してください。
Alpine Linux (OpenRC)
OpenRC サービスはシステム全体で実行されます。root としてインストールしてください:
sh
sudo zeroclaw service install
作成します:
/etc/init.d/zeroclaw: init スクリプト/etc/zeroclaw/: 設定ディレクトリ/var/log/zeroclaw/: ログファイル
有効化して開始:
sh
sudo rc-update add zeroclaw default
sudo rc-service zeroclaw start
sudo rc-service zeroclaw status
ログ:
sh
sudo tail -f /var/log/zeroclaw/error.log
OpenRCのノート
- サービスは
zeroclaw:zeroclawとして実行されます(最小権限)。 - システム全体のみ:ユーザーレベルの OpenRC サービスはありません
- すべてのサービス操作には
sudoが必要です。
Telegramのポーリングに関する注意点
Telegram Bot API の getUpdates は、ボットトークンごとに単一のポーラーのみ対応しています。同じトークンで2つのインスタンスを実行することはできず、2つ目は Conflict: terminated by other getUpdates request を受け取ります。
これが見える場合:
-
ps aux | grep zeroclawを実行し、デーモンが1つだけ実行されていることを確認してください。 -
開発セッションから
cargo run --bin zeroclaw -- channel start telegramが残っていないか確認してください。 -
期限切れの場合、Telegramの投票セッションをリセットします:
sh
curl -X POST "https://api.telegram.org/bot$TOKEN/close"
Webhookを安全に公開する
公開可能なWebhookURLは攻撃対象となります。少なくとも:
- HMAC署名検証: 各Webhookチャネルに設定された
secret - ソースIPのホワイトリスト:サービスが固定の送信元IPを使用している場合(GitHub、AWS SNSなど)
- レート制限: webhook チャネル設定の
rate_limit_per_sec
詳細な設定については、チャンネル → ウェブフック を参照してください。
関連項目も参照してください
- Setup → Container: Docker固有のネットワーク設定
- セットアップ → サービス管理: プラットフォームサービスの統合
- 操作 → 概要
- セキュリティ → 概要