コンテンツにスキップ

運用上の上限

英語版の参考訳です(08/11/2026 更新)。内容が異なる場合は英語版が優先されます。識別子、コマンド、コードブロックは翻訳せずにそのまま掲載しています。

TypeScript、Python、Go、Rust の SDK と Express、FastAPI のアダプターについて、入力、信頼と失効のソース、並行性、クライアントの切断、判定キャッシュを何が制限するのか、そしてオペレーターであるあなたが何を設定する必要があるのかをまとめます。このリポジトリのソースを説明しています。公開済みのパッケージに含まれるものは、そのバージョンによります(公開済みのバージョンを参照)。

このページは、ワイヤーフォーマット、dcp-jcs-v1、プロファイル、署名のいずれも変更しません。上限を決めるのは 4 つの層で、それぞれを分けて扱います。

  • プロトコルは、フィールドごとの長さと strict-v1 の鮮度の期間を定めます。
  • SDK は検証、受け入れ、合成を行います。これらの API は期限もキャンセルのシグナルも受け取りません。Python の拒否リストのヘルパーには独自のダウンロード期限がありますが、暗号処理とは別のものです。
  • アダプターは、予算、期限、容量、キャンセルを追加します。いずれもオプトインです。
  • あなたが、それらの値、テナント、レート制限、前段のインフラを選びます。

最後にあるサンプル構成は出発点の一つであり、デフォルトでも規則でもありません。

何を誰が決めるか

上限 決めるもの
strict-v1 のセッションの長さと失効スナップショットの経過時間:最大 300 秒 受け入れプロファイル(規範)
sig_b64 は最大 66 476 文字、公開鍵は最大 3456 文字、intended_verifier は最大 256 文字 V2 スキーマ(規範、フィールドごと)
監査エントリ、PQ チェックポイント、鍵、信頼するプリンシパル、拒否リストのエントリ、チェーン内のマンデート、委任者のバンドルの数 スキーマにも SDK にも上限はなく、あなたが受け入れる範囲だけで制限されます。検証のコストはこれらとともに増えます
ネストの深さ 共通の予算はありません。各ランタイムのパーサーとスタックがそれぞれの深さで拒否します。オプトインの admitSignedBundleV2FromBytes は 64 段を超えると拒否します
リクエストボディ Express:ボディパーサーの limit。FastAPI:max_body_bytes(オプトイン、デフォルトは未設定)。リファレンスサーバー:1 MiB
ヘッダー内のバンドル サーバーと各プロキシのヘッダー上限(Node のデフォルトは 16 KiB)。max_body_bytes はヘッダーを対象にしません
拒否リストのダウンロード 拒否リストのヘルパー:最大 16 MiB、リダイレクトなし、期限はあなたが設定
ソースの期限と容量 ソースの監督(オプトイン、デフォルトの容量なし)
Express の判定キャッシュ デフォルトでは無効(cacheTtlSeconds 0)。有効にすると最大 cacheMaxEntries(デフォルト 1000)
テナント あなた自身の認証。クライアントが選ぶヘッダーでは決して決めない
リクエストのレート あなた(Express の例は express-rate-limit を使用)

入力

  • バンドルはボディで送ってください。 ボディの予算は、パースの前に確認されるものにします。ハイブリッド署名を持つ典型的な V2 バンドルは、Node のデフォルトのヘッダー上限より大きくなります。
  • ボディの予算は、あなたが設定する入力の予算です。 署名、監査エントリ、PQ チェックポイントはそれぞれ処理を増やしますが、スキーマはその数を制限しません。あなたが受け入れる件数、検証ポリシー、アダプターの外で許す CPU と並行性と合わせて、1 つのリクエストが持ち込める処理量を制限します。CPU の期限ではなく、HTTP の外で組み立てたオブジェクトであなたのコードが呼び出す API も制限しません。最大のものではなく、想定するバンドルに合わせて決めてください。
  • 深さ。 現在のコードとメンテナーのローカルのサンプルでは、ランタイムが扱えるより深くネストしたボディは受け入れられずに拒否されましたが、拒否された場所はアダプターごとに異なりました。Express は検証の失敗を報告し(Malformed signed bundle: Maximum call stack size exceeded)、FastAPI はボディを JSON として読めなかったため 403 Missing DCP signed bundle を返しました。深さの上限はランタイムごとに異なり、プロトコルの一部ではありません。

信頼と失効のソース

ソースが遅い、または応答しないときに取りうる動作は 4 つあり、互いに別のものです。アダプターができるのは最初の 3 つで、4 つ目はソース自身にしかできません。

  1. 待つのをやめる。 リクエストには失敗の応答が返ります。
  2. 遅れた結果を拒む。 期限の後に届いた値は決して使われません。
  3. キャンセルを求める。 AbortSignal を中断する(Express)か、タスクをキャンセルします(FastAPI)。
  4. 処理を止める。 協調するソースだけがこれを行います。

ソースの監督がある場合(sourceSupervisor と supervise、または source_supervisor と supervise):

  • 期限内に応答しない信頼のソースは 503、失効のソースは 403 REVOCATION_UNAVAILABLE になります。
  • 遅れた結果は決して使われません。
  • 期限は単調時計で測りますが、イベントループが空いているときにしか発火しません。CPU が忙しい場合や、ソースがループをブロックする場合は、応答が遅れます。
  • 監督は、自分が所有する呼び出しを、その呼び出しが実際に終わるまで数えます。キャンセルを無視するソースは、応答の後も終わるまで枠を保持します。
  • ソースが自分で始めた処理(スレッド、プロセス、返さないタスク)は、アダプターが数えることも制限することもありません。

監督がない場合:

ソース Express FastAPI
信頼 期限なし:リクエストはソースを待ちます 期限なし。同期のソースはイベントループ上で動き、ループをブロックします
失効 revocationTimeoutMs:応答は送られますが、ソースは動き続け、数えられません revocation_timeout は asyncio.wait_for を使い、ソースのキャンセルが終わるのを待ちます。キャンセルを抑え込むソースは応答を遅らせ、その遅れたスナップショットが使われます。同期のソースは自分のスレッドで動き続けます

本番では監督を使ってください。Express の古い checkRevocation オプションには期限もサイズの上限もなく、strict-v1 では拒否され、このガイドの対象外です。

拒否リスト:

  • TypeScript。 Express のヘルパー denyListRevocationSource は、自分の期限が来たとき、または監督のシグナルでダウンロードを中断します。ボディを読んでいる最中も同じです。
  • Python。 ヘルパー deny_list_revocation_source は読み取りの合間に期限を確認するため、DNS の名前解決や、ゆっくり送られるステータス行とヘッダーは制限しません。DenyListWorkers の予算を与えてください。これはダウンロードのスレッドを制限するもので、監督の枠とは別の予約です。DenyListWorkers は fetch が戻るまでダウンロードを数えますが、スレッドはその後も一瞬、終了処理を続けることがあります。
  • Go と Rust には拒否リストのソースがありません。あなたが失効スナップショットを作り、受け入れに渡します。

拒否リストのヘルパーは呼び出されるたびにリストをダウンロードし直します(失効の段階より前に拒否されたリクエストはダウンロードしません)。大きなリストを多くのリクエストが同時にダウンロードすると、それぞれにバッファとオブジェクトを使います。リストは小さく保ってください。独自のキャッシュからスナップショットを提供する場合は、テナントごとに分け、プロファイルの鮮度の期間内に保ってください。受け入れの規則は変わりません。

並行性とテナント

  • 容量はプロセスではなく監督のオブジェクトに属します。 1 つの容量を共有するには、1 つの監督を共有してください。1 つのリクエストの信頼と失効のソースは、同じ監督から枠を取ります。
  • どの段階で先に拒否されるかは、何を監督しているかによります。 監督下の trustedPrincipals 関数がある場合(両方のソースを監督するサンプル構成のように)、リクエストはまず信頼の枠を必要とします。信頼または失効の呼び出しで枠が埋まっている間、新しいリクエストはどのソースも呼ばれる前に信頼の段階で 503 として拒否されます。静的な信頼セットで失効のソースだけを監督している場合は取るべき信頼の枠がなく、空き枠のないリクエストは失効の段階で 403 REVOCATION_UNAVAILABLE として拒否されます。
  • maxInFlightPerTenant / max_in_flight_per_tenant がなければ、テナント間の分離はありません。 1 つのテナントがすべての枠を占有できます。
  • テナントはあなたの認証から決めてください。 クライアントが設定するヘッダーから取らないでください。
  • 検証は CPU を使います。 TypeScript と Python ではイベントループ上で、Go と Rust では呼び出し側のスレッドで動きます。並行性があると、リクエストは検証の順番を待ちます。容量が制限するのはソースであり、CPU や HTTP の並行性ではありません。それらはレート制限、ワーカー、インフラで制限してください。

クライアントの切断

cancelOnDisconnect(Express)または cancel_on_disconnect(FastAPI。max_body_bytes も必要)を使う場合:

  • 何を「去った」とみなすか。 応答が送られる前に閉じられたとき、またはアップロードが途中で切れたとき、クライアントは本当に去ったとみなされます。Express は、ボディを受け取った後のリクエスト自身の close を離脱とはみなしません。
  • 何が起きるか。 そのリクエストの監督下のソース呼び出しだけがキャンセルされます。そのリクエストについては、ハンドラー、キャッシュへの書き込み、onFailure、応答のいずれも行われません。
  • 何をしないか。 すでに動いている検証は中断されません。キャンセルを無視するソースは、終わるまで枠を保持します。

SDK にはキャンセルがない

SDK の検証、受け入れ、合成の API(委任された受け入れとアクティベーションを含む)は、いずれも期限、AbortSignal、context.Context を受け取りません。Python の拒否リストのヘルパーのダウンロード期限は別のもので、暗号処理には適用されません。

  • TypeScript と Python はイベントループ上で検証します。
  • Go と Rust は同期です。
  • WASM は同期です。

始まった検証は最後まで実行されます。そのコストは入力の予算で、並行性は SDK の外で制限してください。

判定キャッシュ(Express)

  • デフォルトでは無効です。 あなた自身の計測で正当化できない限り、無効のままにしてください。
  • 受け入れの経路(trustedPrincipals、strict-v1 の有無を問わない)で保持するもの: 暗号検証の結果だけを、ハッシュのキーで保持します。バンドル、スナップショット、どんな認可も保持しません。プリンシパルの信頼は、キャッシュの有無にかかわらずリクエストごとに評価され、プロファイルの確認と失効も、設定されていれば同様です。プロファイルのない信頼セットに、strict-v1 の失効の確認が加わることはありません。
  • 検証だけの経路(trustedPrincipals なし)では、抽出したエージェントとその鍵の ID も保持します。信頼とプロファイルの確認には受け入れの経路が必要で、その場合は検証だけの経路ではなくなります。checkRevocation を設定している場合は、リクエストごとに評価されます。
  • キー。 正規化されたバンドル、検証ポリシー、テナントを対象にします。
  • 期限切れと追い出し。 エントリは TTL、バンドルのセッション、またはその鍵とともに期限切れになります。キャッシュが満杯になると、最も長く使われていないエントリが追い出されます。
  • メモリ。 プロセスのメモリはキャッシュだけではなく、バンドルの大きさがそのままエントリの大きさになるわけでもありません。
  • ほかのアダプターと SDK。 FastAPI にはキャッシュがありません。TypeScript SDK の VerificationCache は別のユーティリティで、アダプターは使いません。

アダプターにおける委任

  • 委任された受け入れがある場所。 TypeScript、Python、Go、Rust の SDK は、委任された受け入れ(admitDelegatedBundleV2 とその対応 API)、アクティベーション(evaluateDelegationActivationV2)、バイトからの受け入れ(admitSignedBundleV2FromBytes)を提供します。
  • ない場所。 WASM はこれらを公開しておらず、どちらのアダプターも呼び出しません。
  • mandateId と require_dcp_mandated。 Express の req.dcpAgent.mandateId はバンドルのマニフェストからコピーされたもので、マンデートのチェーンを検証しません。FastAPI の require_dcp_mandated は、ミドルウェアが V2 のバンドルについて一度も設定しない mandate_id を確認するだけで、やはりチェーンを検証しません。
  • あなたのアプリケーションが行うこと。 SDK の API を自分で呼び出し、受け入れる証拠(チェーンの長さ、委任者のバンドルの数、バイト数)を制限し、認可とリプレイを判断します。

そのほかの対象

対象 上限
OpenClaw(@dcp-ai/openclaw) Express と同じ期限、容量、拒否リストのヘルパー。入力はホストがすでにパースしたオブジェクトで、独自の予算はなく、cancelOnDisconnect もありません
リファレンスサーバー(/v2/admit) ボディは最大 1 MiB。ソケットが 30 秒無活動だと 408 を返しますが、処理はキャンセルしません。失効はデフォルトで 5000 ミリ秒以内で、ダウンロードを中断します。信頼のファイルは受け入れのたびに読み込みます。状態はメモリ上、インスタンスは 1 つ。再現用であり、本番用ではありません
CLI(dcp-ai) 1 プロセスにつき 1 回の呼び出しで、独自の期限も予算もありません。呼び出し側が課す必要があります

サンプル構成

アダプターを試すために使った、パイロットの出発点の一つです。デフォルトでも、あらゆるデプロイへの推奨でも、プロトコルの一部でもありません。 それぞれの値は、あなたのバンドル、ソース、ハードウェアに合わせて決めてください。

設定 サンプルの値
ボディの予算 64 KiB(Express ではパーサーの limit、FastAPI では max_body_bytes=65536)
転送 ヘッダーではなくボディ
監督 同時に 32 呼び出し、テナントごとに 8
信頼の期限 Express:supervise.trustTimeoutMs: 2000(ミリ秒)。FastAPI:supervise={"trust_timeout": 2.0}(秒)
失効の期限(監督あり) Express:revocationTimeoutMs: 2000 と supervise.revocation: true。FastAPI:revocation_timeout=2.0 と supervise={"revocation": True}
拒否リストのダウンロード Express のヘルパー:timeoutMs: 1500。Python のヘルパー:timeout=1.5。失効の期限の内側にあり、ダウンロードを早めに終えるよう求めますが、その処理が終わることは保証しません
Python の同時ダウンロード DenyListWorkers(max_workers=8, max_workers_per_tenant=2):数えるのは実行中のダウンロードで、スレッドの正確な数ではありません
クライアントの切断 有効
Express の判定キャッシュ 無効
委任の証拠(あなたのアプリケーション) チェーン内のマンデートは最大 4、各バンドルはボディの予算内

このページが主張しないこと:

  • あなたのデプロイにおける容量、レイテンシー、メモリの数値。
  • リークがないこと。
  • 各 API が文書化している範囲を超えて証拠を受け入れること。

あなた自身の環境で計測してください。