---
title: "Hermes Relay"
description: "プラットフォームの資格情報をコネクター側が持つ形で Hermes をメッセージング基盤につなぐ — 登録、できること、設定、困ったときの対処"
upstream_path: user-guide/messaging/relay.md
upstream_blob: d12ae3edc5669f9d4a3461979620a7d2b461162f
sources:
  - https://hermes-agent.nousresearch.com/docs/user-guide/messaging/relay
---

# Hermes Relay（コネクター） {#hermes-relay-connector}

:::warning 実験的
Relay は**実験的な機能**です。通信の取り決め、認証の方式、設定項目は、
検証が続いている間、段階的な廃止の手順を踏まずに変わることがあります。
:::

Hermes Relay それ自体はチャットのプラットフォームではありません。**コネクターの仕組み**であり、
ゲートウェイが実在するメッセージング基盤（Discord、
Telegram、Slack、WhatsApp など）を **プラットフォームの資格情報を一切持たずに**代理で扱えるようにします。
資格情報は *コネクター* と呼ばれる別のサービスが持ち、ボットのトークンとソケットを管理します。
ゲートウェイはコネクターに向けて**外向き**に 1 本の認証済み
WebSocket をつなぎ、接続時に「何ができるか」を記した情報を受け取り、
そのソケットの上で正規化されたメッセージのイベント（受信）と操作（送信）をやり取りします。

主な性質は次のとおりです。

- **通信は外向きだけ。** ゲートウェイは受信用のポートを一切開きません。
  受信したメッセージは、ゲートウェイ側からつないだ同じ WebSocket を通って戻ってきます。そのため
  relay は NAT の内側でも、公開 IP を持たないホストでも動きます。
- **プラットフォームの秘密情報がゲートウェイに置かれない。** ボットのトークンはコネクターにあります。
  認証が必要なプラットフォーム上のメディアの URL はコネクター側で置き直されるので、プラットフォームの
  資格情報が通信路に流れることはありません。
- **プラットフォームに依存しない。** ゲートウェイは、代理で扱うプラットフォームに何ができるか
  （メッセージの長さの上限、markdown の方言、編集・スレッド・逐次表示への対応、そして
  対応する操作の正確な一覧）を、ハードコードされた個別の処理ではなく接続時の情報から学びます。

ゲートウェイとコネクターの間の正式な取り決めは、リポジトリの
`docs/relay-connector-contract.md` にあります。

## Relay を使う場面 {#when-to-use-relay}

Relay は、ホスティングされた、あるいは共有のコネクターのサービスがプラットフォーム側を
まとめて受け持つ構成のためのものです。たとえば 1 つの共有ボットが多数の利用者のエージェントを
代理する複数テナントのホスティングや、ゲートウェイのマシンにボットのトークンを置きたくない構成が該当します。
自分でボットを直接動かすなら、各プラットフォーム向けのアダプター（[Telegram](/hermes/docs/user-guide/messaging/telegram/)、
[Discord](/hermes/docs/user-guide/messaging/discord/) など）を使ってください。

## 登録 {#enrollment}

自分で運用するゲートウェイは、ゲートウェイごとの秘密の値でコネクターに認証します。
`hermes gateway enroll` は、**一度だけ使える登録用トークン**
（テナントの経路が用意されるときにコネクターが発行し、ゲートウェイの設定と一緒に渡されます）を
その秘密の値と引き換えます。

```bash
hermes gateway enroll \
  --token <enrollment-token> \
  --connector-url wss://connector.example.com/relay
```

このコマンドは次のことを行います。

1. すでにログインしている情報（`~/.hermes/auth.json`）から Nous Portal のアクセストークンを
   取り直します。これによって、どの Nous の組織（テナント）に属しているかが証明されます。
   `gateway.idp.token_url` が設定されている場合は、代わりに自前の IdP が使われます（外部と切り離した構成や
   IdP を自前で持つ場合の経路で、Nous Portal は関与しません）。
   `client_id` と `client_secret` が設定されていれば一般的な OAuth2 の
   クライアントクレデンシャルによる認可を行い、どちらも設定されていなければ、その URL は
   周囲から自動で取得できるトークンの提供元として扱われます（単純な GET の応答本文がトークンそのものになる、
   メタデータサーバー方式です。たとえば Domino の `$DOMINO_API_PROXY/access-token`）。
   2 つの資格情報のうち片方だけを設定するとエラーになります。
2. 登録用トークンとゲートウェイの id を、TLS 経由でコネクターの
   `/relay/enroll` エンドポイントに POST します。
3. コネクターがトークンを検証し（署名、一度きりであること、テナントの一致）、
   ゲートウェイごとの秘密の値とテナントごとの配信キーを発行して、一度だけ返します。
4. 受け取った資格情報を `~/.hermes/.env` に保存します。
   `GATEWAY_RELAY_ID`、`GATEWAY_RELAY_SECRET`、`GATEWAY_RELAY_DELIVERY_KEY`
   （加えて、渡された場合は `GATEWAY_RELAY_URL` と `GATEWAY_RELAY_WAKE_URL`）です。

このあとゲートウェイを再起動すると、新しい環境変数が読み込まれます。

指定できるフラグは次のとおりです。

| フラグ | 説明 |
|------|-------------|
| `--token` | 一度だけ使える登録用トークン。`GATEWAY_RELAY_ENROLL_TOKEN` でも指定できます。 |
| `--connector-url` | コネクターのベース URL、または relay の URL（`wss://…/relay` か `https://…`）。`GATEWAY_RELAY_URL` や `config.yaml` の `gateway.relay_url` でも指定できます。 |
| `--gateway-id` | このゲートウェイのインスタンスを表す変わらない id（緊急停止をどの単位でかけるかに使われます）。既定は `gw-<hostname>` です。 |
| `--wake-url` | 任意。ゲートウェイが休んでいる間に処理待ちのものが届いたとき、コネクターがつつくための到達可能な URL です（本文のない GET）。`GATEWAY_RELAY_WAKE_URL` として保存されます。設定しなくても、次に再接続したときに溜まっていたメッセージは受け取れます。 |

:::note ホスティングされた環境での導入
`hermes gateway enroll` は、ホスティング事業者が管理する環境では実行できません。そうした環境では
ホスティング側が relay の秘密の値をコンテナーの環境に直接用意します。
:::

## 設定 {#configuration}

Relay は、コネクターの relay の URL を設定した時点で有効になります。専用の
オンオフの切り替えはありません。設定していない構成には何の影響もありません。

| 設定 | 場所 | 意味 |
|---------|-------|---------|
| `GATEWAY_RELAY_URL` | 環境変数（`~/.hermes/.env`） | コネクターの relay の WebSocket の URL。設定されていること自体が relay を有効にします。 |
| `gateway.relay_url` | `config.yaml` | 上と同じものを設定ファイルに書く形（環境変数が優先されます）。 |
| `GATEWAY_RELAY_ID` | 環境変数 | このゲートウェイのインスタンスの id（`enroll` が書き込みます）。 |
| `GATEWAY_RELAY_SECRET` | 環境変数 | WebSocket の接続時の認証に使う、ゲートウェイごとの秘密の値（`enroll` が書き込みます）。 |
| `GATEWAY_RELAY_DELIVERY_KEY` | 環境変数 | テナントごとの配信キー（`enroll` が書き込みます。今後の互換性のために保持されます）。 |
| `GATEWAY_RELAY_WAKE_URL` / `gateway.relay_wake_url` | 環境変数 / `config.yaml` | 任意。休止中や一時停止中のゲートウェイをつつく先。 |
| `GATEWAY_RELAY_PLATFORMS` | 環境変数 | このゲートウェイが 1 本の接続で代理するプラットフォームをカンマ区切りで並べたもの（例: `discord,telegram`）。ふつうは配備側や管理の仕組みが設定します。 |
| `GATEWAY_RELAY_BOT_IDS` | 環境変数 | プラットフォームごとのボットの識別情報を表す JSON の対応表。例: `{"discord": {"botId": "…"}}`。`GATEWAY_RELAY_PLATFORMS` と対で使います。 |
| `gateway.idp.token_url` | `config.yaml` | 設定すると、登録や払い出しの認証先が Nous Portal ではなく自前の IdP になります。`gateway.idp.client_id` と `client_secret` も設定されていれば OAuth2 のクライアントクレデンシャル方式、そうでなければ周囲から自動で取得できるトークンの提供元（単純な GET でトークンそのもの、または `{"access_token": …}` が返る形）として扱われます。 |

## 対応していること {#supported-capabilities}

relay の接続で実際に何ができるかは、接続時のやり取りで決まります。コネクターが
`supported_ops` の一覧を提示し、ゲートウェイはコネクターが明示した操作しか使いません
（古いコネクターの場合は、従来の `send`/`edit`/`typing`/`follow_up` の組に落とします）。プラットフォームごとの
できること（編集による逐次表示、スレッド、下書きの逐次表示、markdown の方言、メッセージの
長さの上限）も、接続時に受け取る情報から決まります。そのやり取りの範囲で、relay は次に対応します。

- **テキストのメッセージと逐次表示** — 送信、返信、そして代理するプラットフォームが
  メッセージの編集に対応している場合は編集による逐次表示ができます。
  対応していない場合は、区切りごとに 1 通ずつ送る形になります。
- **メディアの送受信** — 送信する画像、音声、オーディオ、動画、
  文書は、コネクターにアップロードするか公開 URL で参照する形で、各プラットフォーム本来の
  アップロード経路を通り、説明文も一緒に届けられます。
  受信した添付はエージェント向けにファイルとして手元に取り込まれます。認証が必要な
  プラットフォームの URL はコネクター側で置き直されるので、プラットフォームの資格情報が
  ゲートウェイに届くことはありません。置き直せるメディアは 25 MB までで、およそ 1 時間で期限切れになります。
- **プラットフォーム本来の対話的な確認画面** — コマンド実行の承認、確認、内容を明確にするための
  質問が、番号を振ったテキストの代わりに**そのプラットフォーム本来の操作部品**（Discord のボタン、
  Telegram のインラインキーボード、Slack の Block Kit のアクション、WhatsApp のボタンやリストの
  メッセージ）で表示されます。ボタンを押した結果は、実際に押した利用者からの認証済みの応答として
  返るので、ゲートウェイの認可の判定は、打ち込まれた返信とまったく同じように働きます。応答の
  期限切れはゲートウェイ側で管理されます。
- **リアクションによる受付の表し方** — 処理の状態を表すボットのリアクション
  （作業中は 👀、完了時は ✅ か ❌）が relay 越しでも働きます。リアクションは
  できる範囲での対応で、失敗しても処理そのものは失敗しません。
- **スレッドの扱い** — 引き継ぎ用のスレッドの作成と、スレッド名の変更
  （LLM が内容から付け直す名前も含みます）を、プラットフォームに依存しない
  `thread_create` / `thread_rename` の操作で行えます。人が手で付けた名前を上書きしない
  仕組みも入っています。利用できるかどうかはプラットフォーム次第です（Slack のスレッドは
  名前を変えられませんし、WhatsApp にはスレッドがありません）。
- **入力中の表示** — ゲートウェイは処理の間、入力中（およびその解除）をコネクター経由で
  送り出します。
- **チャットの情報** — `get_chat_info` の問い合わせは、コネクターが対応を示していれば
  そちらに取り次がれます。
- **溜め置きの配信と起こす仕組み** — ゲートウェイが休止したり切断したりしている間、
  コネクターは受信したメッセージを確実に溜めておき、再接続時に順序どおりに流し直します（受け取りの確認を
  伴うので、取りこぼしも重複も起きません）。起こすための URL が登録されていれば、
  眠っているゲートウェイ宛ての処理待ちが届いたときにコネクターがそこをつつきます。

複数のプラットフォームの代理にも対応しています。1 つのゲートウェイが複数のプラットフォーム
（Discord *と* Telegram など）を 1 本の relay の接続で代理でき、送信するメッセージには
それぞれ宛先のプラットフォームの印が付きます。

## 困ったときは {#troubleshooting}

**登録が 401 で失敗する** — コネクターが本人確認のトークンを検証できませんでした。
`hermes auth add nous`（または `hermes setup`）でログインし直してから、もう一度試してください。

**登録が 403 で失敗する** — 登録用トークンが無効か、期限切れか、
すでに使用済みか、別のテナントのものです。登録用トークンは
一度きりです。テナントの経路を用意した相手に、新しいものを発行してもらってください。

**「Could not reach the connector」と出る** — コネクターの URL を確認してください。`wss://…/relay` の
接続用 URL でも `https://…` のベース URL でも貼り付けられます。CLI が
自動で読み替えます。

**`enroll` が実行を拒む** — ホスティング事業者が管理する環境にいます。そこでは
relay の秘密の値はホスティング側が用意します。自分で登録できるのは
自分で運用するゲートウェイだけです。

**動いていた relay が無効と表示されるようになった** — 接続が成立した*あとで*
WebSocket が 4401 で閉じられた場合、そのゲートウェイの秘密の値が失効しています
（インスタンスが撤去されたときなど）。ゲートウェイは意図的に再接続をやめ、
やり直すのではなく relay を無効として報告します。接続が一度も成立していない状態での 4401 は、
まだ準備が整っていないだけの一時的な行き違いとみなして、通常どおり再試行します。

**登録したのに何も変わらない** — ゲートウェイは `GATEWAY_RELAY_*` を
起動時に読み込みます。再起動してください（`hermes gateway restart`）。

**ボタン・メディア・スレッドなどが黙って素のテキストに落ちる** — その
プラットフォームのコネクターが、接続時の `supported_ops` にその操作を示していません。
ゲートウェイは、コネクターが扱えない操作を送るのではなく、意図的にテキストでの動きに
落とすようになっています。
