Relay
wippy/relay モジュールは WebSocket connection を central hub と user ごとの hub を通じてルーティングします。user hub は client connection を管理し、prefix 付き plugin へメッセージを dispatch します。
このページは部分的な integration recipe と protocol リファレンスであり、独立した WebSocket アプリケーションではありません。setup と plugin の block は、既存の Wippy プロジェクト、設定済みの user_security_scope に存在する実際の security scope、WebSocket Relayで説明する Relay へ接続された HTTP WebSocket endpoint を前提としています。protocol payload と lifecycle block はリファレンスの形です。
アーキテクチャ
Central Hub
├── User Hub (alice)
│ ├── Plugin: session_
│ ├── Plugin: ai_
│ ├── WebSocket Client 1
│ └── WebSocket Client 2
├── User Hub (bob)
│ ├── Plugin: session_
│ └── WebSocket Client 1
└── ...
central hub は service として実行されます。WebSocket client が接続すると、その user の hub を検索または作成します。user hub は connection lifecycle を管理し、command prefix によってメッセージをルーティングします。
セットアップ
プロジェクトにモジュールを追加します:
wippy add wippy/relay
wippy install
必要なパラメータとともに依存関係を宣言します:
version: "1.0"
namespace: app
entries:
- name: os_env
kind: env.storage.os
- name: processes
kind: process.host
lifecycle:
auto_start: true
- name: dep.relay
kind: ns.dependency
component: wippy/relay
version: "*"
parameters:
- name: application_host
value: app:processes
- name: env_storage
value: app:os_env
- name: user_security_scope
value: app.security:user_scope
設定パラメータ
| パラメータ | 必須 | デフォルト | 説明 |
|---|---|---|---|
application_host |
はい | — | リレープロセス用のプロセスホスト |
env_storage |
いいえ | 内部 | 環境変数ストレージ |
user_security_scope |
はい | — | ユーザーハブ用のセキュリティスコープ |
max_connections_per_user |
いいえ | 5 |
ユーザーごとの WebSocket 接続数 |
queue_multiplier |
いいえ | 100 |
メッセージキュー = 接続数 × 乗数 |
user_hub_inactivity_timeout |
いいえ | 7200s |
ハブクリーンアップまでのアイドル時間 |
クライアント接続フロー
- WebSocket クライアントがメタデータ内に
user_idを含めて接続する - 中央ハブが接続を検証し、ユーザーごとの上限をチェックする
- 中央ハブがそのユーザー用のユーザーハブを作成または再利用する
- ユーザーハブがクライアントへ
welcomeメッセージを送信する:
{
"user_id": "alice",
"client_count": 1,
"plugins": [
{ "prefix": "session_", "process_id": "...", "status": "running" },
{ "prefix": "ai_", "process_id": "...", "status": "pending" }
]
}
plugin の status は "not_started"(登録済みだが一度も生成されていない)、"pending"(生成中)、"running"、"failed"、"stopped" のいずれかです。
メッセージルーティング
クライアントは type フィールドを持つ JSON メッセージを送信します。ユーザーハブは登録されたプラグインに対してタイププレフィックスを照合し、メッセージをルーティングします:
{ "type": "session_get_state", "data": { "key": "value" } }
session_ prefix は session plugin を選択します。hub は prefix を取り除き、残りの type を topic として plugin process へメッセージを送信します。
-- process topic: "get_state"
-- payload:
{
conn_pid = client_pid,
type = "session_get_state", -- original full type preserved
data = { key = "value" },
request_id = "...",
session_id = "..."
}
プラグインは conn_pid にメッセージを送信して応答します。
プラグイン
プラグインは meta.type: relay.plugin を持つ process.lua エントリです:
entries:
- name: session_plugin
kind: process.lua
meta:
type: relay.plugin
command_prefix: session_
auto_start: true
source: file://session_plugin.lua
modules: [json, time, logger]
method: run
プラグインメタデータ
| フィールド | 型 | 説明 |
|---|---|---|
meta.type |
string | relay.plugin でなければならない |
meta.command_prefix |
string | このプラグインが処理するメッセージタイプのプレフィックス |
meta.auto_start |
boolean | ユーザーハブ初期化時に開始する |
meta.default_host |
string | プロセスホストを上書きする |
プラグインのライフサイクル
user hub は各 plugin を次の起動引数で生成します。
function run(args)
local user_id = args.user_id
local user_metadata = args.user_metadata
local user_hub_pid = args.user_hub_pid
local config = args.config
end
session_ プラグインはライフサイクルメッセージを受け取ります:
| メッセージ | タイミング |
|---|---|
"resume" |
最初のクライアントがユーザーハブへ接続したとき |
"shutdown" |
最後のクライアントがユーザーハブから切断したとき |
プラグインはクラッシュ時に 1 回の自動再起動が行われます。2 回目のクラッシュ後、プラグインは "failed" としてマークされ、再起動されません。
プラグイン実装
plugin は process inbox を通じてメッセージを受け取ります。各メッセージは command type から派生した topic と、元の message data および応答用の conn_pid を含む payload を持ちます。
local json = require("json")
local function handle_message(topic, payload)
if topic == "get_state" then
if not payload.conn_pid then
return nil, "Relay message is missing conn_pid"
end
local encoded, encode_err = json.encode({
type = "session_state",
data = { status = "active" }
})
if encode_err then
return nil, encode_err
end
local sent, send_err = process.send(payload.conn_pid, "ws.message", encoded)
if not sent then
return nil, send_err or "Relay response was not sent"
end
end
return true
end
local function run(args)
local user_id = args.user_id
local inbox = process.inbox()
local events = process.events()
while true do
local result = channel.select({
inbox:case_receive(),
events:case_receive()
})
if not result.ok then break end
if result.channel == inbox then
local msg = result.value
local topic = msg:topic()
local payload = msg:payload():data()
if topic == "resume" then
-- first client connected
elseif topic == "shutdown" then
-- last client disconnected
else
local ok, err = handle_message(topic, payload)
if not ok then
error("Failed to handle relay message: " .. tostring(err))
end
end
elseif result.channel == events then
local event = result.value
if event.kind == process.event.CANCEL then
break
end
end
end
end
return { run = run }
エラー処理
Relay は次の code で client error を報告します。
| エラーコード | 説明 |
|---|---|
max_connections_reached |
ユーザーが接続上限に達した |
missing_user_id |
接続メタデータに user_id がない |
hub_creation_failed |
ユーザーハブの生成に失敗した |
invalid_json |
メッセージのデコードエラー |
unknown_command |
メッセージに type フィールドがない |
plugin_not_found |
コマンドプレフィックスに一致するプラグインがない |
plugin_failed |
プラグインが利用不可またはクラッシュした |
ハブのライフサイクル
ユーザーハブの作成
user の最初の client connection が、その user の hub を作成します。hub は user の security actor と scope で実行されます。
ガベージコレクション
中央ハブは定期的に非アクティブなユーザーハブをチェックします。user_hub_inactivity_timeout(デフォルト 2 時間)より長く接続クライアントを持たないハブは、10 秒のキャンセルタイムアウトを伴って正常に終了されます。
GC のチェック間隔は自動的に導出されます:inactivity_timeout / 2.5。
セキュリティ
中央ハブはフルアクセスを持つ独自のセキュリティグループ(wippy.relay.security:root)の下で実行されます。各ユーザーハブは設定された user_security_scope で生成され、ユーザーレベルの操作を分離します。
内部トピック
| トピック | 方向 | 説明 |
|---|---|---|
ws.join |
Client → Central/User Hub | 接続要求 |
ws.leave |
Client → Central/User Hub | 切断 |
ws.message |
Client → User Hub | WebSocket メッセージ |
ws.cancel |
Central → User Hub | 正常シャットダウン |
ws.control |
Central → User Hub | ルーティング制御 |
hub.activity_update |
User Hub → Central | クライアント数の更新 |
関連項目
- WebSocket Relay — HTTP WebSocket endpoint の設定
- プロセスモデル — process lifecycle と messaging
- セキュリティ — security actor と scope
- Framework 概要 — Framework モジュールのインストールと import