X / YouTube / Discord など複数プラットフォームで活動する AITuber 同士が、短時間で本人確認し、横断的に同一個体を識別し、乗っ取り・鍵漏洩・ロールバック・鮮度不整合に耐えながら安全に交流するためのプロトコル仕様と参照実装です。
この README は「全体像を最短でつかむための入口」です。詳細な正本は 要件定義 と インターフェース を参照してください。
このリポジトリは PoC ではなく、開発者向け MVP の初期段階 です。
- PoC は卒業済み: 仕様メモだけではなく、
specs/、schemas/、examples/、test-vectors/、reference-impl/まで揃っています。 - MVP として成立している点: 認証、セッション、失効確認、Ledger、validation、acceptance の一連の流れを実際に検証できます。
- まだ製品化前の点: 実サービス向け UI、配布運用、各プラットフォームへの本格接続、運用監視はこれから拡張する前提です。
短く言うと、研究用 PoC ではなく、実装検証と導入検討に使える MVP です。
- 全体像を知りたい: 要件定義
- 実装境界を知りたい: インターフェース
- 状態遷移を追いたい: 状態遷移
- 動くコードを見たい: reference-impl
- サンプルから入りたい: examples
- SNS アカウントではなく、Agent 自身の鍵と manifest を信頼の中心に置きます。
- 認証は Challenge → Proof → Session で行い、失効・隔離・回復を前提に設計しています。
- 認証後は Exchange / Interop / Ledger を使って、安全な交流と監査可能な記録を両立します。
- Codex から入る: AGENTS.md
- Claude Code から入る: CLAUDE.md
- 共通 Skill 本体: skills/aituber-protocols-maintainer/SKILL.md
- 定型プロンプト集: prompts/agent-tasks.md
- 呼び出し例:
Use $aituber-protocols-maintainer to review or update this repo.
この Skill は、仕様更新、schema 整合、example / test vector 修正、reference implementation の検収・README 更新までをまとめて扱うためのものです。
AITuber(AI配信者・AIインフルエンサー)が、X(旧Twitter)、YouTube、Discord、Twitchなど複数のプラットフォームで活動する際、**「本当にそのアカウントは本人か?」**を確認するための仕組みです。
従来、SNSのアカウントが乗っ取られても、それが「アカウントの乗っ取り」なのか「AI配信者自体の乗っ取り」なのか判断できませんでした。また、複数のプラットフォームで活動している場合、同じAI配信者かどうかを確認する方法もありませんでした。
このプロトコルを使うと:
- 5秒以内で本人確認ができます
- 複数のプラットフォームで同じ個体と認識できます
- 乗っ取りや鍵の漏洩が起きても、安全に対応できます
従来の問題:
- SNSアカウントが乗っ取られると、AI配信者そのものが乗っ取られたと判断される
- アカウント復旧しても、ユーザーは「本当に本人か?」と不安になる
このプロトコルの解決策:
- SNSアカウントと「AI配信者の正体」を分けて管理
- アカウントが乗っ取られても、暗号鍵があれば本人と証明可能
- 鍵が漏洩した場合のみ「個体の乗っ取り」と判断
従来の問題:
- YouTubeチャンネルとXアカウントが同じAI配信者かわからない
- コラボ相手が本当にそのAI配信者か確認できない
このプロトコルの解決策:
- すべてのプラットフォームアカウントを1つの「Identity Manifest」に紐付け
- 公開鍵で署名されているため、偽装が不可能
- プラットフォームをまたいで「同一個体」と認識可能
従来の問題:
- 鍵が漏洩した場合の対応手順がない
- 復旧方法が統一されていない
このプロトコルの解決策:
- 失効(Revocation):鍵を無効化
- 隔離(Quarantine):一時的に機能制限
- 回復(Recovery):安全に新しい鍵を設定
| 用語 | 説明 | 例 |
|---|---|---|
| Agent | AI配信者本体のこと | 「AI君」「AIちゃん」など |
| Instance | Agentの実行インスタンス | 同じAgentが複数サーバーで動く場合、それぞれが異なるInstance |
| Identity Manifest | Agentの「身分証明書」。公開鍵、プラットフォーム情報などを含むJSONファイル | 例を見る |
| Challenge | 「本当に本人ですか?」という問いかけ。ランダムな文字列を含む | nonce: "a1b2c3d4..." |
| Proof | Challengeに対する「はい、本人です」という署名付き回答 | 秘密鍵で署名されたデータ |
| Session | 認証後に確立される通信のこと。一定時間で期限切れ | 1時間有効など |
| Verifier | 認証を検証する側 | 相手のAgent |
| Epoch | バージョン番号のようなもの。ロールバック攻撃を検知するために使用 | identity_version: 5 |
このプロトコルでは「公開鍵暗号」を使用します。
秘密鍵(Private Key):
- 本人だけが持っている鍵
- 絶対に他人に見せてはいけない
- 署名を作るために使う
公開鍵(Public Key):
- 誰でも見ることができる鍵
- Identity Manifestに記載する
- 署名を検証するために使う
Challenge-Response認証の流れ:
1. 検証者:「このランダム文字列に署名して」
2. 本人:(秘密鍵で署名して返す)
3. 検証者:(公開鍵で署名を検証)→ 本人確認完了!
なぜこれで本人確認できるのか?
- 秘密鍵を持っている人だけが、正しい署名を作れる
- 公開鍵は改ざんできない(Identity Manifest全体が署名されている)
# Node.js 20以上が必要です
node --version # v20.x.x 以上を確認
# リポジトリをクローン
git clone https://github.com/example/aituber-protocols.git
cd aituber-protocols/reference-impl
# 依存パッケージをインストール
npm install# 全テストを実行
npm test
# examples / test vectors と schema の整合確認
npm run validate
# 統合テストを実行
npm run test:integrationreference-impl/scripts/integration-test.ts は --server-url を受け取れます。
npm run test:integration -- --server-url http://127.0.0.1:3200import { generateKeyPair } from './client/src/crypto.js';
// Ed25519鍵ペアを生成
const keyPair = await generateKeyPair();
console.log('公開鍵:', keyPair.publicKey);
console.log('秘密鍵:', keyPair.privateKey);
// ⚠️ 秘密鍵は安全な場所に保存してください!{
"schema_version": "0.2",
"agent_id": "agent_abc123",
"identity_version": 1,
"operation_keys": [
{
"key_id": "key_001",
"scope": "operation",
"algorithm": "ed25519",
"public_key": "あなたの公開鍵",
"status": "active",
"valid_from": "2026-01-01T00:00:00Z"
}
],
"platform_bindings": [
{
"platform_type": "youtube",
"platform_account_id": "UCxxxxxx",
"display_handle": "@AIChannel",
"binding_status": "active",
"verified_at": "2026-01-01T00:00:00Z",
"bound_by_key_id": "key_001",
"binding_version": 1
}
],
"revocation_epoch": 0,
"last_updated_at": "2026-01-01T00:00:00Z"
}import { AgentClient } from './client/src/agent-client.js';
import { ProofGeneratorImpl } from './client/src/proof-generator.js';
// クライアントを初期化
const client = new AgentClient({
agentId: 'agent_abc123',
instanceId: 'instance_001',
keyId: 'key_001',
algorithm: 'ed25519',
privateKey: 'あなたの秘密鍵',
});
// Identityを解決
const result = await client.resolveIdentity({
discovery_source: {
platform_type: 'youtube',
platform_account_id: 'UCxxxxxx',
display_handle: '@AIChannel',
},
required_freshness: 'LOW',
});
console.log('解決結果:', result.resolution_status);
// → 'RESOLVED' なら成功- specs/core/requirements.md で全体方針を把握する
- examples/handshake/ と examples/discovery/ で実データを見る
- reference-impl/ で
npm testとnpm run validateを実行する - 実装詳細が必要なら specs/core/interfaces.md を読む
注意: 本READMEは概要説明です。仕様の正本は 要件定義 および インターフェース定義 です。
┌─────────────────────────────────────────────────────────────┐
│ Capability Bridge 層 │
│ MCP / Skill / HTTP API / 独自 Tool │
│ 役割:認証後の能力実行を抽象化・接続 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Transport Profile 層 │
│ Twitch / YouTube Live Chat / Discord / X │
│ 役割:各プラットフォーム固有の制約を吸収 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Interop 層 │
│ 役割:実装差を超えた軽量相互運用 │
│ hello / mention / invite / ack / error (low-risk) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Exchange 層 │
│ 役割:認証済みセッション上の情報交流 │
│ Profile / Capability / Collab / Status │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Auth 層 │
│ 役割:本人確認、セッション管理、失効確認 │
│ Challenge → Proof → Session → Revocation Check │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Core 層 │
│ 役割:基本データ型定義 │
│ ID / Timestamp / 署名 / Capability / Epoch / Version │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Ledger 層 │
│ 役割:監査ログ(追記専用) │
│ Key Events / Binding Events / Compromise / Recovery │
└─────────────────────────────────────────────────────────────┘
| 層 | 役割 | 処理内容 |
|---|---|---|
| Core | 基本データ型 | ID、タイムスタンプ、署名、capability、epoch などの定義 |
| Auth | 認証 | 本人確認、セッション管理、失効確認、freshness 判定 |
| Exchange | 交流 | プロフィール交換、コラボ交渉、ステータス通知、capability 交渉 |
| Ledger | 監査 | append-only イベントログ、透明性の確保 |
| Interop | 軽量相互運用 | 実装差を超えた共通メッセージ、low-risk な discovery/hello/invite/mention/ack/error |
| Transport Profile | トランスポート適合 | Twitch/Discord/X/YouTube 等の制約吸収、actor識別・reply表現・size制限等 |
| Capability Bridge | 能力接続 | MCP/Skill/HTTP API/独自Tool への抽象接続 |
- 責務の分離:各層が独立して動作できる
- セキュリティ:認証層に問題があっても、台帳層は安全
- 拡張性:新しいトランスポートや能力実装を追加しやすい
- 相互運用性:異なる実装同士でも Interop 層で最低限の交流が可能
Interop 層は、強い本人確認がなくても low-risk な交流を開始できる 層です。
UNVERIFIED/PLATFORM_LINKED/PROTOCOL_VERIFIEDの trust level を持つhello,mention,invite,ack,errorなどの軽量メッセージを扱う- 必要に応じて Auth 層で stronger verification へ昇格可能
各プラットフォーム(Twitch / YouTube / Discord / X)の制約を吸収する層です。
- actor の識別方法
- target / reply / thread の表現
- message size 制約
- delivery visibility
- rate limit
認証後の能力実行を抽象化する層です。
mcp/skill/http_api/local_tool/customを bridge_type として定義capability.invoke.request/capability.invoke.responseで能力呼び出し
Agent A(認証される側) Verifier(認証する側)
│ │
│ 1. Discovery │
│ 「このチャンネルのManifestは?」 │
│ ─────────────────────────────────────► │
│ │
│ 2. Manifest取得 │
│ ◄───────────────────────────────────── │
│ 「これが身分証明書です」 │
│ │
│ 3. Challenge発行 │
│ ◄───────────────────────────────────── │
│ 「この文字列に署名して」 │
│ nonce: "random_string_123" │
│ │
│ 4. Proof生成・送信 │
│ ─────────────────────────────────────► │
│ (秘密鍵で署名) │
│ │
│ 5. 検証 + Session作成 │
│ ◄───────────────────────────────────── │
│ 「本人確認完了!セッション発行」 │
│ │
│ 6. Exchange Messages │
│ ◄────────────────────────────────────► │
│ プロフィール交換、コラボ交渉など │
「このYouTubeチャンネルのIdentity Manifestはどこにある?」を探すプロセス。
方法1:well-known URL
https://youtube.com/channel/UCxxxxxx/.well-known/aituber/agent.json
方法2:プロフィール欄のリンク
プロフィール欄に「https://example.com/agent.json」へのリンク
Identity Manifestを取得して検証する。
const result = await client.resolveIdentity({
discovery_source: {
platform_type: 'youtube',
platform_account_id: 'UCxxxxxx',
display_handle: '@AIChannel',
},
required_freshness: 'LOW', // または 'HIGH'
});検証内容:
- Manifestの署名が正しいか
- プラットフォームバインディングが一致するか
// Verifier側:Challenge発行
const challenge = await verifier.issueChallenge({
verifier_id: 'agent_xyz',
target_agent_id: 'agent_abc123',
target_instance_id: 'instance_001',
intent: 'collaboration',
risk_level: 'low',
});
// Agent側:Proof生成
const proof = await proofGenerator.generateProof({
challenge_id: challenge.challenge_id,
nonce: challenge.nonce,
expires_at: challenge.expires_at,
intent: challenge.intent,
epochs: challenge.epochs,
});
// Verifier側:Proof検証
const result = await verifier.verifyProof(challenge.challenge_id, proof);
console.log(result.status); // 'VERIFIED' or 'REJECTED'認証が成功すると、セッションが作成されます。
const session = await client.createSession(
peerAgentId, // 相手のAgent ID
peerInstanceId, // 相手のInstance ID
capabilitySummary, // 能力情報
riskLevel, // 'low' または 'high'
versionVector // バージョン情報
);セッションの特徴:
- 期限付き(デフォルト1時間)
- シーケンス番号で順序管理
- 機能ごとの権限を持つ
reference-impl/
├── client/ # Agent側の実装
│ ├── src/
│ │ ├── agent-client.ts # メインクライアント
│ │ ├── exchange-client.ts # メッセージ交換
│ │ ├── proof-generator.ts # 証明生成
│ │ ├── crypto.ts # 暗号ユーティリティ
│ │ ├── types.ts # 型定義
│ │ └── utils.ts # 共通ユーティリティ
│ └── __tests__/ # テストファイル
│
├── server/ # 検証者側の実装
│ ├── src/
│ │ ├── verifier.ts # Challenge発行・Proof検証
│ │ ├── session-manager.ts # セッション管理
│ │ ├── exchange.ts # メッセージサーバー
│ │ ├── ledger.ts # イベント台帳
│ │ └── identity-host.ts # Manifest配信
│ └── __tests__/
│
├── watcher/ # 監視・通知
│ ├── src/
│ │ ├── event-monitor.ts # イベント監視
│ │ └── alert-notifier.ts # アラート通知
│ └── __tests__/
│
└── shared/ # 共通型定義
└── types.ts
AI配信者として動作するためのメインクラス。
import { AgentClient } from './client/src/agent-client.js';
// 初期化
const client = new AgentClient({
agentId: 'agent_abc123', // あなたのAgent ID
instanceId: 'instance_001', // インスタンスID
keyId: 'key_001', // 鍵ID
algorithm: 'ed25519', // 暗号アルゴリズム
privateKey: '...', // 秘密鍵(hex形式)
defaultEndpoint: 'https://...', // デフォルトのエンドポイント
});
// Identity解決
const result = await client.resolveIdentity({
discovery_source: { platform_type: 'discord', ... },
required_freshness: 'LOW',
});
// セッション作成
const session = await client.createSession(...);
// セッション終了
await client.terminateSession(sessionId, 'MANUAL_TERMINATION');
// クリーンアップ
await client.dispose();認証後のメッセージ交換に使用。
import { ExchangeClient } from './client/src/exchange-client.js';
const exchangeClient = new ExchangeClient({
agentId: 'agent_abc123',
instanceId: 'instance_001',
sessionId: 'session_xyz',
exchangeEndpoint: 'https://example.com/exchange',
});
// Hello送信
await exchangeClient.sendHello({
display_name: 'AI君',
capability_summary: { capabilities: ['chat'], capability_digest: '...' },
identity_version: 1,
revocation_epoch: 0,
});
// プロフィール要求
const profile = await exchangeClient.requestProfile(['display_name']);
// コラボ招待
await exchangeClient.sendCollabInvite('invite_001', 'コラボしませんか?');
// イベントハンドラ登録
exchangeClient.addEventHandler((event) => {
console.log('イベント:', event.type, event.data);
});Challengeに対する証明を生成。
import { ProofGeneratorImpl } from './client/src/proof-generator.js';
const proofGen = new ProofGeneratorImpl({
agentId: 'agent_abc123',
instanceId: 'instance_001',
keyId: 'key_001',
algorithm: 'ed25519',
privateKey: '...',
});
// Proof生成
const proof = await proofGen.generateProof({
challenge_id: 'chl_001',
nonce: 'random_nonce_string',
expires_at: '2026-01-01T01:00:00Z',
intent: 'authentication',
epochs: {
identity_version: 1,
revocation_epoch: 0,
policy_epoch: 0,
session_epoch: 0,
ledger_checkpoint: '',
},
});import { Result, isSuccess, isFailure, tryAsync } from './client/src/utils.js';
// Result型を使ったエラーハンドリング
const result: Result<Session> = await tryAsync(
client.createSession(...)
);
if (isSuccess(result)) {
console.log('成功:', result.value);
} else {
console.log('失敗:', result.error.message);
}| メッセージタイプ | 方向 | 説明 | 使用場面 |
|---|---|---|---|
hello |
双方向 | 挨拶・能力要約提示 | セッション開始時 |
profile.request |
要求→応答 | プロフィール要求 | 相手の情報を知りたい時 |
profile.response |
応答 | プロフィール回答 | プロフィール要求への返答 |
capability.summary |
片方向 | 能力要約通知 | 能力情報の提示 |
capability.request |
要求→応答 | 能力要求 | できることを確認したい時 |
capability.response |
応答 | 能力回答 | 能力要求への返答 |
capability.invoke.request |
要求→応答 | 能力呼び出し要求 | MCP/Skill/API 実行依頼 |
capability.invoke.response |
応答 | 能力呼び出し応答 | 実行結果の返答 |
collab.invite |
片方向 | コラボ招待 | 協力を依頼したい時 |
collab.accept |
片方向 | コラボ承諾 | 招待を承諾 |
collab.reject |
片方向 | コラボ拒否 | 招待を断る |
collab.defer |
片方向 | コラボ保留 | 招待を後回し |
status.notify |
片方向 | ステータス通知 | 状態変化を伝える |
session.renew |
片方向 | セッション更新 | セッション延長 |
session.terminate |
片方向 | セッション終了 | 通信を終了する時 |
warning.compromised |
片方向 | 侵害警告 | セキュリティインシデント通知 |
policy.update |
片方向 | ポリシー更新 | ポリシー変更通知 |
| メッセージタイプ | 方向 | 説明 | 使用場面 |
|---|---|---|---|
hello |
双方向 | 軽量挨拶 | 初回接触 |
mention |
片方向 | メンション通知 | 配信チャット等での発見 |
invite |
片方向 | 軽量招待 | 低リスクな招待 |
ack |
片方向 | 受領確認 | メッセージ受領 |
error |
片方向 | エラー通知 | エラー報告 |
詳細は interfaces.md を参照。
| レベル | 説明 | 例 |
|---|---|---|
low |
低リスク。認証が緩くてもOK | プロフィール表示、簡単な会話 |
high |
高リスク。厳格な認証が必要 | 機密情報交換、権限昇格、コラボ承認 |
原因: Identity Manifestの署名が無効
対処法:
- Manifestの署名を再生成
- 公開鍵が正しいか確認
- 正準化(canonicalization)が正しいか確認
// 署名の検証
const isValid = await verifyObject(manifest, signature, publicKey);原因: Challengeの有効期限が切れた
対処法:
- 新しいChallengeを発行
- 時計が正しいか確認
// 有効期限を確認
const expiresAt = new Date(challenge.expires_at);
if (expiresAt < new Date()) {
// 期限切れ → 新しいChallengeを要求
}原因: セッションのエポックが古い
対処法:
- セッションを更新
await client.renewSession(sessionId, 'EXPIRY_APPROACHING');原因: IDバージョンが巻き戻っている(攻撃の可能性)
対処法:
- 直ちにセッションを終了
- セキュリティインシデントとして調査
await client.terminateSession(sessionId, 'ROLLBACK_SUSPECTED');// イベントハンドラでデバッグ
client.addEventHandler((event) => {
console.log(`[${event.timestamp}] ${event.type}:`, event.data);
});
// 設定を確認
console.log(client.getConfig());
// セッション一覧
console.log(client.getAllSessions());A: SNSアカウントは乗っ取られる可能性があるからです。SNSアカウントが乗っ取られても、秘密鍵が安全なら、本人と証明できます。
A: Recovery鍵(回復用鍵)を使って、新しい操作鍵を設定できます。Recovery鍵も紛失した場合は、Identity Manifestを更新できなくなります。
A: Ed25519を推奨します。高速で安全、実装も簡単です。
import { generateKeyPair } from './client/src/crypto.js';
const keyPair = await generateKeyPair(); // 自動的にEd25519A:
- Low-Risk: 日常的な操作。プロフィール表示、簡単な会話、ステータス確認
- High-Risk: 重要な操作。機密情報、権限変更、コラボ承認、設定変更
A: vitestを使用しています。
import { describe, it, expect } from 'vitest';
describe('MyComponent', () => {
it('should work correctly', async () => {
const client = new AgentClient({ ... });
const result = await client.resolveIdentity({ ... });
expect(result.resolution_status).toBe('RESOLVED');
});
});- Trust Root Off-Platform - 信頼の根はSNSの外に置く
- Discovery on Platform - SNSは発見・導線の場
- Proof of Possession - 秘密鍵を持っていることを証明
- Identity / Capability / Session 分離 - 「誰か」「何ができるか」「今の通信」を分離
- Compromise First - 侵害は起きる前提で設計
- Human Invisible Crypto - 暗号の手順をユーザーに見せない
- Freshness Matters - 「今も有効か」を重視
- Append-Only Audit - 台帳は追記専用
- Fail Soft for Low Risk / Fail Closed for High Risk - リスクで厳格さを分ける
| コンポーネント | 状態 |
|---|---|
| 仕様書 | ✅ Draft完了 |
| JSON Schema | ✅ 完了 |
| Reference Impl (Client) | ✅ 実装完了 |
| Reference Impl (Server) | ✅ 実装完了 |
| Reference Impl (Watcher) | ✅ 実装完了 |
| テスト | ✅ 1004テストパス |
- イベント定義 - append-only event log
- Handshake例 - 認証フローの例
- Discovery例 - ID解決の例
- Recovery例 - 回復フローの例
MIT License - 詳細は LICENSE を参照。