DCP のリファレンスゲートウェイ¶
英語版の参考訳です(26/10/2026 更新)。内容が異なる場合は英語版が優先されます。識別子、コマンド、コードブロックは翻訳せずにそのまま掲載しています。
Digital Citizenship Protocol の V1、V2、フェーズ 3(PQ 優先)を実装する HTTP の検証サービスです。メモリ上のストレージ:外部のデータベースは不要で、再起動の後には何も残りません(永続化を参照)。どの第三者でも運用できるように設計されており、プロトコルは中央のサーバーを必要としません。
クイックスタート¶
チェックアウトのルートから実行します。@dcp-ai/sdk 2.2.0 は npm で公開済みですが、ルートはそのソースをテストするため意図的にワークスペース SDK に依存しています。先にビルドしてください。
(cd sdks/typescript && npm install && npm run build)
npm install
node server/index.js
# or: npm run server
既定のポート:3000。PORT=4000 node server/index.js で変更できます。管理用のエンドポイントを使い、新しいエージェントを登録するには OPERATOR_TOKEN を設定してください。
エンドポイント¶
コア¶
| メソッド | パス | 説明 |
|---|---|---|
| GET | / , /health |
ヘルスチェック:{ ok, service, supported_versions, registered_agents, registered_keys } |
| GET | /.well-known/dcp-capabilities.json |
プロトコルのケイパビリティの発見(アルゴリズム、機能、ワイヤ形式)。サーバーが宣伝している内容であり、確認済みの保証ではありません。hsm_provider: true と application/cbor を挙げていますが、HSM はなく、ボディは JSON としてしか読みません |
| POST | /verify |
署名付きバンドルを検証する(V1 か V2 かを自動判別し、V2 は VERIFIER_POLICY に照らして確認) |
| POST | /v2/admit |
オペレーターが信頼するプリンシパルに対するアドミッション、設定されていれば strict-v1 も(docs/ADMISSION.md)。ボディからは signed_bundle だけを読みます。未設定なら 501 |
| POST | /anchor |
スタブ:501 を返す(オペレーターごとに実装) |
V2:身元と意図¶
| メソッド | パス | 説明 |
|---|---|---|
| POST | /v2/passport/register |
自己署名のエージェントのパスポートを登録する。新しい agent_id にはオペレーターのトークンが必要(信頼モデルを参照) |
| POST | /v2/intent/declare |
登録済みのエージェントの意図を宣言し、サーバーの(署名のない)ポリシー判断を受け取る。意図の composite_sig は検証されません |
| POST | /v2/audit/append |
エージェントのチェーンに監査イベントを追加する |
| POST | /v2/audit/compact |
マークルの要約で監査チェーンを圧縮する |
| POST | /v2/bundle/verify |
V2 のバンドルの完全な検証:スキーマ、マニフェスト、Ed25519 + ML-DSA-65 の複合署名、セッションの結び付け、監査チェーン(lib/verify.js と @dcp-ai/sdk による)を VERIFIER_POLICY に照らして確認。V1 のバンドルには 400 |
V2:鍵の管理¶
| メソッド | パス | 説明 |
|---|---|---|
| GET | /v2/keys/:kid |
決定的な鍵 ID で鍵を検索する |
| POST | /v2/keys/rotate |
鍵のローテーション:新しい鍵による保有の証明と、古い鍵による認可 |
| POST | /v2/emergency-revoke |
緊急失効(パニックボタン) |
V2:マルチパーティとガバナンス¶
| メソッド | パス | 説明 |
|---|---|---|
| POST | /v2/multi-party/authorize |
M-of-N のマルチパーティ承認。署名は登録済みの当事者の鍵で確認されます |
| POST | /v2/governance/register |
ガバナンスの鍵セットを登録する(オペレーター) |
| GET | /.well-known/governance-keys.json |
公開されたガバナンスの鍵 |
フェーズ 3:ポリシーとアドバイザリー¶
| メソッド | パス | 説明 |
|---|---|---|
| GET | /v2/policy |
現在の検証器のポリシー(モード、アルゴリズム、制約) |
| POST | /v2/policy/mode |
検証器のモードを切り替える(classical_only、hybrid_preferred、hybrid_required、pq_only)(オペレーター) |
| GET | /.well-known/algorithm-advisories.json |
公開されたアルゴリズムのアドバイザリー |
| POST | /v2/advisory/publish |
スキーマで検証したアルゴリズムのアドバイザリーを公開する。最大 1000 件(オペレーター) |
| GET | /v2/advisory/check |
特定のアルゴリズムが影響を受けるかを確認する |
| POST | /v2/advisory/auto-apply |
アドバイザリーを検証器のポリシーに自動適用する(オペレーター) |
オペレーターのエンドポイントは、検証器の信頼の設定を変更します。Authorization: Bearer <OPERATOR_TOKEN> が必要で、
そうでなければ 401 を返します。OPERATOR_TOKEN が設定されていなければロックされたままです。
DCP-05 から DCP-09¶
ライフサイクル、承継、紛争、権利、委任の記録のための 31 のエンドポイントで、docs/OPERATOR_GUIDE.md に一覧があります。各記録をそのスキーマで検証しますが、署名は一切検証せず、トークンも不要で、OPERATOR_TOKEN でも保護されません。サーバーに到達できる人なら誰でも、エージェントを廃止または再稼働させ、取り消し可能なマンデートを取り消し、紛争を解決済みにできます。信頼できないクライアントに公開しないでください。
環境変数¶
| 変数 | 既定値 | 説明 |
|---|---|---|
PORT |
3000 |
待ち受ける HTTP のポート |
OPERATOR_TOKEN |
— | オペレーターのエンドポイントと、新しい agent_id の登録のための Bearer トークン。未設定なら両方ともロックされます |
VERIFIER_MODE |
hybrid_required |
起動時の検証モード:classical_only、hybrid_preferred、hybrid_required、pq_only のいずれか。無効な値ではサーバーが停止します |
ALLOW_V1_BUNDLES |
true |
false にすると POST /verify が V1 のバンドルを拒否します(pq_only では常に拒否) |
TRUST_PROXY |
— | クライアントの IP を X-Forwarded-For に付け加えるプロキシ(Fly、nginx)の背後でだけ true。そのときレート制限はその最後のホップを基準にし、そうでなければソケットのアドレスを基準にします。クライアントはヘッダーを偽造できるからです |
REGISTRATIONS_PER_HOUR |
20 |
クライアントごと 1 時間あたりの POST /v2/passport/register の呼び出し回数。超えると 429 |
MAX_REGISTERED_KEYS |
10000 |
レジストリの鍵の数。それ以降の新しいエージェントとローテーションには 507 が返ります(何も追い出されません) |
DCP_TRUSTED_PRINCIPALS_FILE |
— | POST /v2/admit の信頼セット(schemas/v2/principal_trust_set.schema.json)。アドミッションのたびに読み、内容が変わると読み直します。無効なファイルでは起動時にサーバーが停止し、後で壊れた場合はアドミッションを拒否します(503)。アドミッションの変数を空の値で設定すると、サーバーが停止します |
DCP_ADMISSION_PROFILE_FILE |
— | strict-v1 のプロファイル(schemas/v2/admission_profile.schema.json)。前後の 2 つの変数が必要で、矛盾があるとサーバーが停止します |
DCP_REVOCATION_LIST_URL |
— | strict-v1 のための、失効サービスの /.well-known/dcp-revocations.json 形式の完全な拒否リスト:https(平文の http はループバックのホストだけ)、リダイレクトなし、最大 16 MiB。DCP_REVOCATION_LIST_AUTHORIZATION で Authorization ヘッダーを設定し、DCP_REVOCATION_TIMEOUT_MS(既定 5000)でリクエストの時間を制限します |
MAX_KEYS_PER_AGENT |
64 |
エージェントが生涯で登録できる鍵の数。それ以降のローテーションには 409 が返ります。頻繁にローテーションするエージェントでは引き上げてください(引退した鍵ごとに約 100 バイトの墓標がかかります) |
REQUIRE_SESSION_BINDING、REQUIRE_COMPOSITE_BINDING、MAX_KEY_AGE_DAYS、PQ_CHECKPOINT_INTERVAL、
ADVISORY_POLL_INTERVAL_MS は Docker と Fly の設定に出てきますが、まだ読まれていません。組み込みのポリシー
(server/index.js の VERIFIER_POLICY:セッションの結び付けと複合バインディングが必須、鍵の経過日数の閾値は 365 日)が適用され、
GET /v2/policy で確認できます。
信頼モデル¶
- パスポートの登録では、パスポートの複合の自己署名(
DCP-AI.v2.AgentPassport、Ed25519 + ML-DSA-65。クラシカルだけの 署名はclassical_only/hybrid_preferredのモードでのみ受け付けます)を、それが列挙する鍵で検証します。その署名が それを作った鍵(クラシカルな鍵と、ある場合は PQ 鍵)の保有を証明し、登録されるのはそれらの鍵だけです。他人の公開鍵を列挙しても何も登録されません。鍵と署名は、 そのアルゴリズムのサイズちょうどの正規の base64 でなければなりません。 - 新しい
agent_idの登録にはAuthorization: Bearer <OPERATOR_TOKEN>が必要です(そうでなければ 401)。パスポートは 自己署名にすぎず、プリンシパルの RPR も同じなので、信頼のアンカーがなければ、誰でも被害者のagent_idを先に名乗り、 緊急失効させることができてしまいます。そこで、新しいエージェントについてはオペレーターが保証します。それを再登録する (たとえば緊急失効トークンを差し替える)には、署名済みのパスポートは公開されており古いものを再送できるため、VERIFIER_MODEが新しいエージェントに何を許していても、created_atが登録済みのものより新しく、かつエージェントの有効な鍵(そのエージェントに 登録され、失効しておらず、期限切れでないもの)だけで署名されたパスポートが必要です。そうでなければ 409 です。登録済みの鍵の エントリはそのまま保持されるため、新しいパスポートで鍵のexpires_atを延ばすことはできず、トークンのないパスポートでは 登録済みのトークンが残ります。 - 登録した系統の有効な鍵をすべて失った(たとえば ML-DSA-65 の鍵が期限切れになった)エージェントは、再登録できません。残った
鍵だけでトークンを差し替えてはならないからです。復旧は、緊急失効トークン(失効させてから新しい
agent_idを登録する)か、 オペレーターを通じて行いますが、オペレーター向けの API はまだありません。ほかのエージェントに登録された kid も 409 となり、 緊急失効したエージェントは再登録できません。 - 鍵のローテーションは、
@dcp-ai/sdkのcreateKeyRotationが作るKeyRotationRecordを受け取ります。新しい鍵が{old_kid, new_kid, timestamp}にDCP-AI.v2.KeyRotationのもとで署名し(保有の証明)、古い鍵が連署します。古い鍵は有効 (失効しておらず、期限切れでない)でなければならず、その後は失効したものとして記録されます。 - マルチパーティ承認:各当事者は
{operation, operation_payload, party_id, role}にDCP-AI.v2.MultiPartyAuthのもとで 署名します(sdks/typescript/src/core/multi-party-auth.tsのcreatePartyAuthorization。@dcp-ai/sdk2.2.0 はエクスポート していません)。当事者は/v2/passport/registerで登録した身元であり、その署名の kid は、そのparty_idに登録された有効な (失効しておらず、期限切れでない)鍵でなければなりません。各当事者は、どの役割で署名しても、required_partiesに対して 1 回だけ数えられます。役割は署名者が主張するもので、どこかで照会されるわけではありません。 - オペレーターのエンドポイントは
OPERATOR_TOKENだけで保護されています。アドバイザリーのcomposite_sigは構造だけを 確認します。@dcp-ai/sdkにはまだAlgorithmAdvisoryのコンテキストタグがないため、アドバイザリーはオペレーターが保証します。 - 鍵のレジストリは、
MAX_REGISTERED_KEYS(10,000)個の鍵を保持すると、古い鍵を追い出す代わりに新しいエージェントと ローテーションを拒否します(507)。追い出されたagent_idは、それを守る履歴を失ってしまうからです(再登録には引き続きオペレーターのトークンが必要ですが、鍵は何でもよくなります)。1 つのエージェントが レジストリを埋め尽くさないよう、ローテーションではそのエージェントの引退した鍵のうち直近の 4 つだけを保持し、上限の確認の 前に最も古いものを外すため、満杯のレジストリでもローテーションは収まります。外された鍵は墓標として残り、GET /v2/keys/:kidはkey: nullとともにrevoked: true, retired: trueを返し、その鍵は誰も二度と登録できません。エージェントが生涯で登録 できる鍵は最大MAX_KEYS_PER_AGENT(64)個なので、その墓標の数にも上限があり、それを超えるとローテーションには 409 が 返ります。登録はクライアントごとにレート制限され(REGISTRATIONS_PER_HOUR)、登録されたパスポートからは署名済みの パスポート全体ではなくcreated_atだけを保持します。
使用例¶
ヘルスチェック、モードの切り替え、ケイパビリティの発見を除き、以下のリクエストはテンプレートです。バンドル、署名、鍵は
プレースホルダー(...、<64 hex>)なので、そのままでは動かず、作り出した値で埋めてはなりません。
ヘルスチェック¶
V1 のバンドルの検証¶
curl -s -X POST http://localhost:3000/verify \
-H "Content-Type: application/json" \
-d '{
"signed_bundle": {
"bundle": { ... },
"signature": { "alg": "ed25519", "sig_b64": "...", "signer": { "public_key_b64": "..." } }
}
}'
V2 のパスポートの登録¶
ボディは AgentPassportV2 の SignedPayload で、それが列挙する鍵によって
compositeSign(registry, DCP_CONTEXTS.AgentPassport, canonicalBytes, keys) で署名されたものです(SDK で作ったバンドルの
agent_passport がこの形です)。
curl -s -X POST http://localhost:3000/v2/passport/register \
-H "Authorization: Bearer $OPERATOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"signed_passport": {
"payload": {
"dcp_version": "2.0",
"agent_id": "agent:research-bot-001",
"session_nonce": "<64 hex>",
"keys": [
{ "kid": "<32 hex>", "alg": "ed25519", "public_key_b64": "...", "created_at": "...", "expires_at": null, "status": "active" },
{ "kid": "<32 hex>", "alg": "ml-dsa-65", "public_key_b64": "...", "created_at": "...", "expires_at": null, "status": "active" }
],
"principal_binding_reference": "human:alice-001",
"capabilities": ["browse"],
"risk_tier": "low",
"created_at": "...",
"status": "active",
"emergency_revocation_token": "sha256:<64 hex>"
},
"payload_hash": "sha256:...",
"composite_sig": {
"classical": { "alg": "ed25519", "kid": "<32 hex>", "sig_b64": "..." },
"pq": { "alg": "ml-dsa-65", "kid": "<32 hex>", "sig_b64": "..." },
"binding": "pq_over_classical"
}
}
}'
レスポンス:201 { ok, agent_id, registered_kids }、400(スキーマや kid の不一致、または正規の base64 でない鍵)、
401(オペレーターのトークンのない新しい agent_id)、403(署名が無効、またはエージェントが失効済み)、409(エージェントが
ほかの鍵で登録済み、パスポートが登録済みのものより新しくない、エージェントに登録した系統の有効な鍵がない、または kid が
引退済みかほかのエージェントのもの)、429(レート制限)、507(レジストリが満杯)。
鍵のローテーション¶
# body = createKeyRotation(registry, { oldKid, oldAlg, oldSecretKeyB64, newKid, newAlg,
# newSecretKeyB64, newPublicKeyB64, timestamp, expiresAt })
curl -s -X POST http://localhost:3000/v2/keys/rotate \
-H "Content-Type: application/json" \
-d '{
"old_kid": "<32 hex>",
"new_key": { "kid": "<32 hex>", "alg": "ed25519", "public_key_b64": "...", "created_at": "...", "expires_at": null, "status": "active" },
"timestamp": "2026-03-01T00:00:00Z",
"proof_of_possession": { "alg": "ed25519", "kid": "<new kid>", "sig_b64": "..." },
"authorization_sig": { "alg": "ed25519", "kid": "<old kid>", "sig_b64": "..." }
}'
検証器のモードの切り替え(オペレーター)¶
curl -s -X POST http://localhost:3000/v2/policy/mode \
-H "Authorization: Bearer $OPERATOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "mode": "pq_only" }'
V2 の意図の宣言¶
ボディは登録済みのエージェントの IntentV2 の SignedPayload です。その composite_sig は必要ですが、検証されません。サーバーは自ら意図を採点し、{ ok, policy_decision, security_tier, verification_mode, checkpoint_interval } を返します。ポリシー判断には署名がありません(リスク 800 から block、500 から escalate)。
以下のリクエストは形を示すだけです。送る前に、省略記号とプレースホルダーをあなたの署名付きペイロードに置き換えてください。文字どおりの { ... } の断片は有効な JSON ではありません。
curl -s -X POST http://localhost:3000/v2/intent/declare \
-H "Content-Type: application/json" \
-d '{
"signed_intent": {
"payload": {
"dcp_version": "2.0",
"intent_id": "intent:...",
"session_nonce": "<64 hex>",
"agent_id": "agent:research-bot-001",
"human_id": "human:alice-001",
"timestamp": "...",
"action_type": "api_call",
"target": { "channel": "api" },
"data_classes": ["pii"],
"estimated_impact": "medium",
"requires_consent": false
},
"payload_hash": "sha256:...",
"composite_sig": { "classical": { ... }, "pq": { ... }, "binding": "pq_over_classical" }
}
}'
V2 のバンドルの検証¶
curl -s -X POST http://localhost:3000/v2/bundle/verify \
-H "Content-Type: application/json" \
-d '{
"signed_bundle": {
"bundle": {
"dcp_bundle_version": "2.0",
"manifest": { "session_nonce": "...", "rpr_hash": "sha256:...", ... },
"responsible_principal_record": { "payload": { ... }, "payload_hash": "sha256:...", "composite_sig": { ... } },
"agent_passport": { ... },
"intent": { ... },
"policy_decision": { ... },
"audit_entries": [ ... ]
},
"signature": {
"hash_alg": "sha256",
"created_at": "...",
"signer": { "type": "human", "id": "...", "kids": ["..."] },
"manifest_hash": "sha256:...",
"composite_sig": {
"classical": { "alg": "ed25519", "kid": "...", "sig_b64": "..." },
"pq": { "alg": "ml-dsa-65", "kid": "...", "sig_b64": "..." },
"binding": "pq_over_classical"
}
}
},
"public_key_b64": "..."
}'
形式は schemas/v2/signed_bundle.schema.json です。@dcp-ai/sdk の BundleBuilderV2 + signBundleV2、または dcp sign-bundle --composite で作ってください。public_key_b64 は任意で、クラシカルな署名者を固定します。規範以前のエンベロープ(最上位の dcp_version / bundle_manifest)は拒否されます。
緊急失効¶
秘密値は、パスポートとともに登録された emergency_revocation_token の原像です。クライアントの IP ごとに 1 分あたり 5 回までに
制限されています。
curl -s -X POST http://localhost:3000/v2/emergency-revoke \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent:research-bot-001",
"revocation_secret": "<64 hex>"
}'
ケイパビリティの発見¶
アーキテクチャ¶
┌──────────────────────────┐
│ DCP Reference Gateway │
├──────────────────────────┤
│ V1 Verification │
│ V2 Composite Verification│
│ Key Registry (in-memory) │
│ Audit Chains (in-memory) │
│ Policy Engine (Phase 3) │
│ Advisory System │
│ Governance Keys │
└──────────────────────────┘
制限¶
- メモリ上のストレージ:すべての状態(パスポート、鍵、監査チェーン、アドバイザリー)は再起動で失われます。永続化を参照してください。
- バンドルの検証以外での署名:バンドルの検証、パスポートの登録、鍵のローテーション、マルチパーティ承認は、本物の Ed25519 と ML-DSA-65 / SLH-DSA の署名を確認します。
/v2/advisory/publishと/v2/governance/registerは確認せず(オペレーターのトークンが唯一の保護です)、/v2/intent/declare、/v2/audit/*、DCP-05 から DCP-09 のエンドポイントも確認しません。これらはトークンも不要です(DCP-05 から DCP-09 を参照)。 - アンカリング:
POST /anchorは 501 を返します。あなたのブロックチェーン(Ethereum、Bitcoin)または透明性ログを使って実装してください。
永続化¶
ディスクには何も書き込みません。再起動、デプロイ、マシンの停止で、パスポートと鍵のレジストリは消去されます。そのため、
消去の後は登録の履歴がなくなり、オペレーターのトークンを持つ人なら、どの agent_id もほかの鍵で再登録でき、緊急失効も忘れられます。そこで deploy/fly/verification.toml は、ゼロまで
縮小する代わりに 1 台のマシンを動かし続けます(min_machines_running = 1)が、デプロイと再起動では引き続き空の状態から
始まります。本番のデプロイには永続的なストアが必要です。
デプロイ¶
- 本番では TLS を備えたリバースプロキシ(nginx、Caddy)の背後で実行し、それがクライアントの IP を
X-Forwarded-Forに付け加える場合にだけTRUST_PROXY=trueを設定してください。 - 自動再起動にはプロセスマネージャー(systemd、PM2)を使ってください。
- コンテナでのデプロイについては Docker のセットアップを参照してください。
参考¶
- spec/VERIFICATION.md — 規範的な検証のチェックリスト
- spec/DCP-AI-v2.0.md — V2 のプロトコル仕様
- docs/OPERATOR_GUIDE.md — オペレーターガイド