プラグイン設定 API リファレンス
1. 目的と位置づけ
- 本ページは fandhe-backend の各プラグイン(
crates/plugin-*)の feature 名・登録方法・Config 型・既定値・注意点を一覧化するリファレンスである - 一次情報源は各クレートの rustdoc(
crates/plugin-*/src/lib.rsほかの doc comment・doc test)であり、本ページは横断比較と契約の要約を担う - 登録先
Serverの全体像は サーバ API、配線パターンの設計判断はdocs/design/plugin-boundary.md(<https://github.com/Fandhe-AI/fandhe-backend/blob/main/docs/design/plugin-boundary.md>)を参照 - curl 例を含む使い方の詳細は feature 構成別サンプル を参照し、本ページでは重複させない
全プラグイン対応早見表
| プラグイン | feature 名 | 登録メソッド | 配線パターン種別 |
|---|---|---|---|
| plugin-websocket | websocket | Server::websocket(WebSocketConfig) | Upgrade 型(UpgradeHandler 拡張点) |
| plugin-graphql | graphql | Server::graphql(GraphQlConfig) | パスインターセプト型 |
| plugin-openapi | openapi | Server::openapi() / Server::openapi_with(OpenApiDoc) | 設定登録型(静的サービング) |
| plugin-cors | cors | Server::cors(CorsConfig)(+ Router::options_fallback 配線) | レスポンス後処理型(finalize_response シーム) |
| plugin-compression | compression | Server::compression(CompressionConfig) | レスポンス後処理型(CORS の後に逐次適用) |
| plugin-static | static | Server::static_files(StaticFilesConfig) | パスインターセプト型 + spawn_blocking 変種 |
| plugin-tracing | tracing | Server::tracing(TracingConfig) | Middleware 型(Middleware 拡張点) |
| plugin-webrtc-proxy | webrtc-proxy | Server::webrtc_proxy(ProxyConfig) | パスインターセプト型(別プロセス委譲) |
| plugin-webrtc | webrtc | Server::webrtc(WebRtcConfig) | パスインターセプト型(in-process) |
| plugin-hub-wiring | なし(依存逆転型) | Server::gate(TenantGate::new(config)) | Gate 型(RequestGate 拡張点) |
- feature はすべて
fandhe-backend-coreの Cargo feature(hub-wiringを除く)。無効時は依存・コード・バイナリ増がゼロになる(pay-for-what-you-use) - いずれも登録時のみ動作する opt-in。feature 有効でも未登録ならフォールスルーし、既定挙動を変えない
2. プラグイン別リファレンス
2.1 plugin-websocket(websocket)
RFC 6455 ハンドシェイク検証・101 応答・tokio-tungstenite へのフレーミング委譲。
| 項目 | 内容 |
|---|---|
| Config 型 | WebSocketConfig(Default 実装あり) |
| builder メソッド | with_path / with_max_message_size / with_max_frame_size / with_idle_timeout / without_idle_timeout / with_handler / with_close_grace / with_outbound_capacity / with_ping_interval / without_ping_interval / with_handshake_check / without_handshake_check |
| 既定値 | path = "/ws"、max_message_size = 1 MiB、max_frame_size = 256 KiB、idle_timeout = Some(60 秒)、close_grace = 10 秒、outbound_capacity = 8、ping_interval = None(Ping keepalive は無効) |
| メッセージハンドラ | with_handler(impl WsMessageHandler) で差し替え。既定は EchoHandler(後方互換) |
ハンドラの戻り値型 BoxFuture | WsMessageHandler::on_message / on_message_with_ctx の戻り値型 BoxFuture<'a, T>(実体は Pin<Box<dyn Future<Output = T> + Send + 'a>>)はクレートルートで再公開されており、fandhe_backend_plugin_websocket::BoxFuture(コア経由では fandhe_backend_core::plugin_websocket::BoxFuture)として参照できる。futures_util::future::BoxFuture と完全に同一の型(別の型ではない)なので、futures-util を直接の依存に加えなくてもハンドラを実装でき、既存の futures_util::future::BoxFuture で書いたハンドラも無変更で動作する(非破壊追加、v0.4.2) |
| 接続コンテキスト | WsMessageHandler::on_message_with_ctx(provided、既定は既存の on_message へ委譲)で WsConnContext(conn_id() / sender() / param() / params() / peer_addr() / host() / origin() / user_agent() / query())を参照可能。WsOpenContext(on_open 引数)にも同じ peer_addr()/host()/origin()/user_agent()/query()/conn_id() があり、同一接続では常に同じ値になる(docs/design/ws-connection-context-and-close.md 参照) |
| 接続元アドレス | WsOpenContext::peer_addr() / WsConnContext::peer_addr()(両方に対応)で接続元の実 peer address(Option<std::net::SocketAddr>)を参照可能。コア経由(Server::bind/BoundServer::run_until 等の実ソケット経路)では Some、tokio::io::duplex 等の非ソケット経路・peer_addr を渡さない旧 API handle_upgrade(5 引数)からの呼び出しでは常に None(GateContext::peer_addr と同型のフェイルクローズ契約)。Debug 出力には含まれない(docs/design/ws-connection-context-and-close.md 参照) |
| 主要リクエストヘッダ・query | WsOpenContext/WsConnContext いずれも host() / origin() / user_agent() / query() で Host/Origin/User-Agent ヘッダ(大文字小文字無視・重複時は先頭値)と query 文字列(RequestHead::query と同一の非デコード契約)を参照可能。許可リスト方式で固定 5 項目(上記 peer_addr を含む)のみを保持し、任意ヘッダを取得する汎用アクセサは無い。ヘッダ値は 1024 バイト・query は 2048 バイトが上限で、超過時は切り詰めず None(フェイルクローズ、部分一致バイパス防止)。クライアントの申告値(peer_addr を除く)であり認可判定の唯一の根拠にしないこと。Debug 出力には含まれない(docs/design/ws-connection-context-and-close.md 参照) |
| 終了理由 | handler::CloseReason / handler::FailureKind(いずれも #[non_exhaustive])でセッションの終了経路を種別化。WsMessageHandler::on_close(&self, ctx: &WsConnContext, reason: CloseReason)(既定 no-op)で on_open が呼ばれた接続についてのみ終了経路を問わずちょうど 1 回通知される(ハンドシェイク検証失敗・101 送出前キャンセルでは呼ばれない、on_open と対称のフェイルクローズ契約。docs/design/ws-connection-context-and-close.md 4 節参照)。CloseReason::PongTimeout は Ping keepalive の Pong 期限切れによる切断を表す(下記参照) |
| サーバー起点 Ping keepalive | WebSocketConfig::with_ping_interval(interval, pong_timeout) で有効化(既定は無効、interval/pong_timeout に Duration::ZERO は PingIntervalError で構築時に拒否)。interval ごとにサーバーから Message::Ping を送出し、送出時点から pong_timeout 以内にクライアントの Pong が届かなければ CloseReason::PongTimeout で切断する(idle_timeout とは独立した死活監視で、サーバー起点 push を受けているだけの受信専用クライアントにも効く。Ping の送出自体は idle_timeout をリセットしない、Pong の受信は他の全フレーム種別と同じく idle_timeout もリセットする、既存挙動)。期限切れは受信待ちで読めるフレームがなくなった時点で判定し、それまでに届いているフレームは先に読んで処理する(Pong を返さない対向も、フレームが途切れず届いている間は切断しない)。1 回の送出(Ping・Reply・outbound push 等)がその開始時刻から pong_timeout を超えてブロックした場合も同じ PongTimeout で終了する。without_ping_interval() で明示的に無効へ戻す |
| 送信ハンドル(切断待ち) | WsSender::closed().await で切断まで待つ(受信側 Receiver drop 時に完了。cancel-safe)、WsSender::is_closed() で同期判定(send の Err が正の判定基準、こちらは参考値)。close() 呼び出し後、または受信側 drop 後のいずれか早い方で true になる(clone 間で同じ時点を観測。close() 確定時点も対象) |
| サーバー起点の Close | WsSender::close(code, reason) で on_message の外(on_open 等から spawn したタスク)からも任意タイミングで Close ハンドシェイクを開始できる。呼び出し前に send が Ok を返した push は Close より前に送出される(順序保証)。close() を呼んだら、要求の観測から close_grace 以内に Close ハンドシェイクを終えるか接続を打ち切る(close_grace 超過・世代キャンセル・idle timeout・送出失敗で打ち切られた場合は残りの push と Close を送らない)。code は RFC 6455 7.4 節・IANA 登録に基づき 1000..=1003・1007..=1009・1011..=1014・3000..=4999 のみ許可(1010 はクライアント専用のため拒否)、reason は 123 バイト以内でなければ WsCloseError(InvalidCode/ReasonTooLong/Closed、#[non_exhaustive])を返す。close 後の send/2 回目以降の close は Err になる。終了理由は CloseReason::SenderClose(docs/design/ws-connection-context-and-close.md 参照) |
| 送信キュー容量・非ブロッキング送信 | WebSocketConfig::with_outbound_capacity(n) で WsSender の送信キュー容量を指定(既定 8、上限 MAX_OUTBOUND_CAPACITY = 4096。0・上限超は OutboundCapacityError)。WsSender::try_send(msg) は send と異なり待たず、満杯(WsTrySendError::Full(msg))とセッション終了・close 確定済み(WsTrySendError::Closed(msg))を区別して即座に返す(送れなかった WsMessage を保持し再試行・破棄を選べる)。順序保証・push の消化契約は send と同一 |
| ハンドシェイクの受理判定フック | WebSocketConfig::with_handshake_check(check)(既定は無効・後方互換、without_handshake_check() で解除)。check: WsHandshakeCheck(Fn(&WsHandshakeContext<'_>) -> Result<(), Response> への blanket impl あり、クロージャ登録可)は RFC 6455 検証を通過した要求について 101 応答の送出前に一度だけ同期で評価される。WsHandshakeContext から {name} パスパラメータ(param/params)・リクエストヘッダ(header/head)・接続元アドレス(peer_addr)を参照できる。Err(response) を返すと(1xx/2xx・304・0..=99/600 以上のステータスは 400 Bad Request へ正規化した上で)response を送出し upgrade しない(conn_id・WsSender の生成、on_open/on_close はいずれも呼ばれない)。戻り値は拒否時も Ok(())(WsError に専用 variant は追加しない、docs/design/ws-connection-context-and-close.md 16 節参照) |
- 型エイリアス:
fandhe_backend_plugin_websocket::BoxFuture<'a, T>はfutures_util::future::BoxFutureと同一の型。利用者はfutures-utilを直接依存に加えずにon_message等の戻り値型を書ける - 注意: 受信メッセージ/フレームが
max_message_size/max_frame_sizeを超えた場合は、Close フレーム(close code 1009 Message Too Big)を送出してから閉じる。frame 上限超過では送信途中の巨大 payload により Close が RST で失われないよう、生ストリームの半閉鎖 + 有界な読み捨て(無通信 2 秒の打ち切りと、トリクラー対策の総上限 3 秒またはclose_grace残り期限の短い方)を行う。戻り値は従来どおりErr(WsError::Protocol(Capacity)) - 注意: ハンドラが
WsOutcome::Closeを返すと送信キューを封鎖し、キュー済み push を Close より先に送出する(以後のsendはErr。flush と Close は全体でclose_graceが上限で、超過・世代キャンセル・送出失敗では残りと Close を送らない)。ハンドラがErrを返した場合も、封鎖してからclose_graceを上限に排出して終了し、封鎖前に確定したWsSender::closeがあればon_closeの理由はSenderCloseになる。WsOutcome::Reply後の排出は容量回までに制限される - 注意: サイズ上限はメモリ枯渇 DoS 対策。アイドルタイムアウトは既定で有効(fail-safe)であり、無効化は
without_idle_timeoutの明示操作でのみ可能 - 注意:
close_grace(with_close_grace)はコアの世代キャンセル(最終 graceful shutdown・rebind 世代 drain)発火時の Close ハンドシェイク猶予。fandhe_backend_plugin_websocket::handle_upgradeの第 5 引数(キャンセルFuture)が発火すると close code 1001 Going Away を送出し、close_graceを上限にクライアント応答を有界に待つ(v0.3.0 での BREAKING CHANGE)。接続元アドレスを渡したい呼び出し元は、同じ 5 引数 +peer_addr(6 番目)を取るhandle_upgrade_with_peer_addrを使う(既存handle_upgradeはpeer_addr: Noneで本関数へ委譲する後方互換の薄いラッパー)。WsMessageHandler::on_message/on_message_with_ctxが返すFutureは任意のawait点で drop されうる契約(docs/design/ws-cancellation-propagation.md) - 注意:
WsConnIdはサーバー側の単調カウンタ発行(unsafe不使用・クライアント入力から独立)でプロセス内一意。値は推測可能なため認可トークン・セッション秘密として使ってはならない(識別子であって資格情報ではない) - 注意:
on_message_with_ctx(on_messageにも適用)実行中もWsSender::sendの送信キュー(設定された容量、既定 8)を cancel(最優先)→ (ハンドラ完了 | outbound 到着) の順で消化するため、ハンドラ内(ctx.sender()経由)から容量を超える回数send(...).awaitしてもデッドロックしない。排出ステップ開始時点で既に到着済みの push は、cancel・送出失敗・close 要求後のclose_grace超過で打ち切られない限り、そのハンドラが返すWsOutcome::Reply/Closeより先に送出される(それ以外の push との相対順序は不定)。詳細契約はcrates/plugin-websocket/src/session.rsモジュール doc「ハンドラ実行中の送信キュー消化」節・docs/design/ ws-connection-context-and-close.md6 節を参照 - 注意(デッドロック):
WsSender::closed()をon_message/on_message_with_ctxの実装内でインラインawaitしてはならない。ハンドラ実行中はセッションループがクライアントの受信ストリームを読まないため、受信側Receiverはハンドラが返るまで drop されず自己デッドロックになる(世代キャンセル発火時のみ解除される)。on_open等からtokio::spawnした別タスクでのみ使うこと - 注意(
idle_timeoutと Ping keepalive の併用。設計判断はdocs/design/ws-connection-context-and-close.md14 節参照):idle_timeoutはクライアントからのフレーム受信でのみリセットされ、WsSender::send/try_sendによる push・with_ping_intervalによるサーバー起点 Ping 送出では延長されない。push だけを受けている受信専用クライアントも死活監視したい場合はwith_ping_intervalを併用し、interval + pong_timeoutをidle_timeoutより小さく設定することを推奨する(例: 既定 60 秒に対しwith_ping_interval(30s, 10s))。Pong の受信は他の全フレーム種別と同じくidle_timeoutもリセットするため、生存クライアントは Ping への Pong で切断されず、Pong を返さない対向はidle_timeoutより先にCloseReason::PongTimeoutで切断される(早い方のタイマーが勝つ契約)。逆にintervalをidle_timeout以上にすると、最初の Ping が送られる前にidle_timeoutが発火してしまう誤設定になるため避ける。検証はcrates/plugin-websocket/tests/idle_keepalive_e2e.rs
2.2 plugin-graphql(graphql)
POST /graphql(GRAPHQL_PATH 定数)のパスインターセプト。async-graphql による実クエリ実行。
| 項目 | 内容 |
|---|---|
| Config 型 | GraphQlConfig |
| 構築 | GraphQlConfig::new(executor)(executor: Executor + Clone + Send + Sync + 'static。async_graphql::Schema が満たす) |
| 既定値 | なし(スキーマ必須。深さ・複雑度制限の既定値も提供しない) |
- 注意: クエリ深さ・複雑度制限(
Schema::limit_depth/limit_complexity)と introspection 無効化(Schema::disable_introspection)はスキーマ登録者の責務 - 不正な body は 400 + 固定エラー body(リクエスト由来の値を一切エコーしない)。未登録時は feature 有効でもフォールスルー
2.3 plugin-openapi(openapi)
GET /openapi.json / GET /openapi.yaml の静的サービング(明示登録時のみ)。
| 項目 | 内容 |
|---|---|
| 登録(組み込みスキーマ) | Server::openapi()(ApiDoc 由来の埋め込み OPENAPI_JSON / OPENAPI_YAML を配信) |
| 登録(独自スキーマ) | Server::openapi_with(OpenApiDoc) |
| Config 型 | OpenApiDoc |
| 構築 | OpenApiDoc::from_json(json)(構築時 JSON 検証、Result<_, OpenApiDocError>)+ with_yaml(yaml)(任意) |
- 注意:
openapi()とopenapi_with()は排他ではなく後勝ち(最後に呼んだ方の登録が残る)。YAML 変換依存は開発用gen-clifeature に閉じ、サーバ経路には現れない
2.4 plugin-cors(cors)
実リクエスト応答への CORS ヘッダ付与(レスポンス後処理型)+ プリフライト応答関数の 2 点構成。外部依存ゼロ。
| 項目 | 内容 |
|---|---|
| Config 型 | CorsConfig(CorsConfig::builder() 経由でのみ構築) |
| builder メソッド | allow_origin(完全一致・複数回可) / allow_any_origin / allow_methods / allow_headers / allow_credentials / max_age / expose_headers / build |
| 既定値 | オリジン空リスト(何も許可しない)、methods = None(プリフライトは対象パスの実登録メソッドを反映)、credentials 無効、max_age なし |
| 構築時検証 | build() は Result<CorsConfig, CorsConfigError>。allow_any_origin() と allow_credentials(true) の併用は AnyOriginWithCredentials で拒否(トークン窃取経路を型レベルで排除) |
| プリフライト | preflight_response を利用者が Router::options_fallback へ直接配線する(is_preflight で判定可能)。配線例は feature 構成別サンプル |
- 注意: オリジン照合はバイト完全一致でありワイルドカード部分一致はない。ヘッダ付与に失敗した場合は当該ヘッダを付与しない側へ倒す(フェイルクローズ)
2.5 plugin-compression(compression)
条件充足レスポンスの gzip 圧縮(レスポンス後処理型の第 2 インスタンス、CORS の後に逐次適用)。外部依存は flate2(純 Rust の rust_backend)のみ。
| 項目 | 内容 |
|---|---|
| Config 型 | CompressionConfig(CompressionConfig::builder() 経由) |
| builder メソッド | min_size / compressible_types(丸ごと差し替え) / add_compressible_type / build(失敗しない) |
| 既定値 | min_size = 1024 バイト、圧縮対象 Content-Type は text/(プレフィックス)・application/json・application/javascript・application/xml・application/xhtml+xml・image/svg+xml |
| 圧縮判定 | ステータス・Content-Type・body サイズ・Accept-Encoding(q 値解釈込み)をすべて満たす場合のみ圧縮。解釈不能・条件未充足は無圧縮のまま返す(フェイルセーフ) |
- 注意: gzip のみ(br はスコープ外)。秘密情報とリクエスト反映値を同一圧縮 body に同居させると BREACH 類似の情報漏洩リスクがある(rustdoc に明記)
2.6 plugin-static(static)
GET の静的ファイル配信(パスインターセプト型 + spawn_blocking 変種)。外部依存ゼロ(fandhe-backend-http + tokio の rt feature のみ)。
| 項目 | 内容 |
|---|---|
| Config 型 | StaticFilesConfig(StaticFilesConfig::builder(mount, root) 経由でのみ構築) |
| builder メソッド | max_file_bytes / mime(ext, content_type)(内蔵 MIME テーブルより優先する拡張マッピング) / build |
| 既定値 | max_file_bytes = DEFAULT_MAX_FILE_BYTES(8 MiB。1 リクエストあたりのメモリ使用上限そのもの)。mime 未登録時は内蔵テーブル(.webmanifest 等を含む)+ 既定 application/octet-stream |
| 構築時検証 | build() は Result<_, StaticConfigError>。mount の形式不正(InvalidMount)・root の canonicalize 失敗(RootNotAccessible)・非ディレクトリ(RootNotADirectory)・mime マッピングの拡張子/Content-Type 不正(InvalidMimeMapping、CR/LF 等のヘッダインジェクション対策込み)を起動前に検出 |
- 注意: 二層防御(I/O 前の字句検証 + canonicalize 後の root 配下検証)でパストラバーサル・シンボリックリンク脱出を拒否。先頭ドットセグメント(
.env・.git/config等)も配信拒否。未検出・検証失敗・サイズ超過は一律 404(存在秘匿)
2.7 plugin-tracing(tracing)
サンプリング付きトレーシング(Middleware 型の第 1 号)。決定的カウンタ方式 + 非同期・バッファ済み I/O。
| 項目 | 内容 |
|---|---|
| Config 型 | TracingConfig(公開フィールド sample_interval: NonZeroU64 / exclude_paths: Vec<String>) |
| 構築・builder | TracingConfig::default() / TracingConfig::new(sample_interval) / exclude_path(path)(チェーン可能) |
| 既定値 | sample_interval = 100(100 リクエストに 1 回記録)、exclude_paths 空 |
| 出力初期化 | init_tracing(TracingOutput) -> WorkerGuard(tracing-appender の non_blocking writer。WorkerGuard は drop までログをフラッシュし続けるため保持必須) |
- 注意:
exclude_pathsはクエリ除去後パスとの完全一致のみ(プレフィックス・glob 非対応。ログ抑制範囲の意図しない拡大を防ぐ安全側の設計)。除外パスはサンプラーのカウンタも消費しない
2.8 plugin-webrtc-proxy(webrtc-proxy)
WebRTC シグナリングの別プロセス切り出し型プロキシ。POST /rtc/offer(OFFER_PATH 定数)を upstream へ委譲する。攻撃表面をプロセス境界で分離でき、MVP 推奨。
| 項目 | 内容 |
|---|---|
| Config 型 | ProxyConfig |
| 構築・builder | ProxyConfig::new(upstream_addr) + with_upstream_path / with_connect_timeout / with_request_timeout / with_max_offer_bytes / with_max_answer_bytes |
| 既定値 | upstream_path = "/rtc/offer"、connect_timeout = 3 秒、request_timeout = 5 秒、max_offer_bytes = max_answer_bytes = 64 KiB |
- 注意: offer / answer 双方向にサイズ上限を持つ(upstream 応答の肥大もフェイルクローズで遮断)。
webrtcfeature と同時有効時はwebrtc-proxyが優先評価される
2.9 plugin-webrtc(webrtc)
in-process WebRTC(webrtc-rs 直接依存)。POST /rtc/offer を同一プロセス内で処理する。
| 項目 | 内容 |
|---|---|
| Config 型 | WebRtcConfig |
| 構築・builder | WebRtcConfig::new() + with_max_offer_bytes / with_max_peer_connections / with_signaling_timeout(getter: max_offer_bytes / max_peer_connections / signaling_timeout) |
| 既定値 | max_offer_bytes = 64 KiB、max_peer_connections = 64、signaling_timeout = 10 秒 |
| drain API | close_active_peers(&config, per_close_timeout) / drain_for_shutdown(&config, per_close_timeout)(drain モジュール) |
- 注意:
webrtc-rsの依存ツリーが大きく攻撃表面が広いため、クレート境界で完全分離されている。まずplugin-webrtc-proxyの採用を検討すること - 注意:
close_active_peers/drain_for_shutdownはいずれもWebRtcConfig::registry上のアクティブなRTCPeerConnectionを 1 接続あたりper_close_timeoutの有界タイムアウトで並行に明示 close する。drain_for_shutdownのみWebRtcConfig::begin_terminal_drainで以降の新規登録を拒否するフェイルクローズ判定を伴う(close_active_peersは rebind 用途を想定し新規登録は拒否しない)。コアのSessionDrain(webrtcfeature ゲート、独立シーム)が最終 graceful shutdown・rebind の両経路から自動でこれらを呼ぶため、通常は利用側が直接呼ぶ必要はない(docs/design/ws-cancellation-propagation.md10 節を参照)
2.10 plugin-hub-wiring(feature なし・依存逆転型)
hub 共通配線(JWT RS256 / JWKS / テナント境界強制・同意ゲート・outbox・監査)。他プラグインと異なりコア側 feature を持たず、利用側サービスが本クレートを依存に追加してコアの RequestGate 拡張点へ登録する(プラグイン → コアの一方向依存)。
| 項目 | 内容 |
|---|---|
| 登録 | Server::gate(TenantGate::new(TenantGateConfig::from_jwks_json(jwks_json)?)) |
TenantGate | RequestGate 実装。Bearer JWT の RS256 検証とテナント境界強制を同期・I/O なしで行う |
TenantGateConfig | new(SharedJwks) / from_jwks_json(&str) で構築。authenticator() で Authenticator(検証結果キャッシュ。ゲート通過時に温まり、ハンドラ側の再検証で RS256 署名検証を再実行しない)を取得 |
SharedJwks | 再起動なしの鍵ローテーション対応 JWKS 保持(snapshot / set)。JWKS の HTTP 取得・自動リフレッシュは利用側の責務 |
ConsentStore / OutboxStore | ゲート通過後にハンドラ層から呼ぶストレージ抽象 trait。同梱はテスト用インメモリ実装(InMemoryConsentStore / InMemoryOutboxStore)のみで、DB 実装は利用側が提供 |
AuditSink | 越境アクセス監査ログの出力先 trait(MemoryAuditSink 同梱)。外部応答は正当な 404 と越境 404 で完全同一のまま、監査ログのみで区別する |
- 注意: 検証失敗はキャッシュしない(キャッシュ汚染防止)。各型の詳細契約は rustdoc(
crates/plugin-hub-wiring/src/{gate,auth,jwks,consent,outbox,audit}.rs)を参照
3. 契約・不変条件(横断)
- opt-in 契約: すべてのプラグインは feature 有効化 +
Server::xxx(...)登録の 2 段階が揃って初めて動作する。未登録時のリクエスト挙動は feature 無効時と同一 - 構築時検証(フェイルクローズ): 不正設定は実行時ではなく構築時に
Resultで拒否する(CorsConfigError/StaticConfigError/OpenApiDocError)。実行時に初めて失敗する経路を作らない - DoS 上限内蔵: サイズ・接続数・タイムアウトの上限は既定で有効。無効化・緩和は明示操作のみ
- レスポンス後処理の適用順:
finalize_responseシームで CORS → compression の順に逐次適用される
4. セキュリティ観点
| プラグイン | 主な観点 |
|---|---|
| websocket | メッセージ / フレームサイズ上限・アイドルタイムアウト既定有効(メモリ枯渇・接続占有 DoS 対策) |
| graphql | 深さ・複雑度制限と introspection 無効化は登録者責務(既定値を提供しないことを明示)。エラー応答にリクエスト由来値を含めない |
| cors | Any オリジン + credentials の最悪構成を構築時拒否。オリジンはバイト完全一致のみ |
| compression | BREACH 類似リスク(秘密 + 反映値の同居 body)を rustdoc に明記。判定不能は無圧縮側へ倒す |
| static | 二層防御によるパストラバーサル・シンボリックリンク脱出拒否、機密ファイル(先頭ドット)遮断、一律 404 による存在秘匿 |
| tracing | 除外は完全一致のみ(可観測性の穴の拡大防止)。ログに機密を出さない運用前提 |
| webrtc / webrtc-proxy | ペイロードサイズ・接続数・タイムアウト上限。攻撃表面の観点で proxy 型を推奨 |
| hub-wiring | RS256 + JWKS・鍵ローテーション・検証失敗の非キャッシュ・越境アクセスのフェイルクローズ遮断と監査 |
5. スコープ外・関連ドキュメント
- スコープ外: 各プラグインのプロトコル仕様詳細(RFC 6455 / GraphQL over HTTP / SDP 等)、curl での動作確認手順(feature 構成別サンプル)、hub-wiring の JWT / JWKS / outbox の詳細契約(rustdoc)、TLS 終端・multipart(v1 スコープ外。
docs/design/v1-scope-tls-multipart.md) Server本体の設定(接続数・body 上限・タイムアウト・graceful shutdown): サーバ API、graceful shutdown ガイド- ルーティングとの接続(
options_fallback/ パスインターセプトの前提): ルーティング API - 拡張点の契約(
Middleware/UpgradeHandler/RequestGate): 拡張 API、拡張点ガイド - 配線パターンの設計判断・境界検証:
docs/design/plugin-boundary.md(<https://github.com/Fandhe-AI/fandhe-backend/blob/main/docs/design/plugin-boundary.md>) - 入門: Getting Started、チュートリアル