AINET Language / ID / Object ワイヤ仕様1
参照:RFC 8949、RFC 8032、SHA-256。 MUST相当の規則を以下に定める。製品0.1とワイヤ形式1は異なるバージョン番号である。
CBORプロファイル
RFC 8949 §4.2.1 Core Deterministic Encodingを使う。整数・長さは最短表現、mapキーは 符号化したキーのバイト列で辞書順とする。mapキーは非負整数だけを許可する。 整数(64ビット範囲)、byte string、正しいUTF-8 text string、array、map、boolean、nullを許可し、 浮動小数点・tag・未確定長・重複キー・未知のフィールドを拒否する。 nullはローカル管理APIの未使用値など、スキーマが許す場合のみ。公開Objectの配列は空arrayを使う。 最大ネスト16、array最大256要素、map最大64項目。個別スキーマの制限も適用する。 自然言語の正規化は行わない。異なる改行やUnicode列は異なるデータである。
すべての構造は整数キーmapである。下表でoptionalとした項目は、空なら送信しない。 必須項目の欠落、optional項目の不要な空値表現も拒否する。 正準な構造を復号・再符号化したバイト列が元と一致することを確認する。
ID
PUBは生のEd25519公開鍵32バイト、HはSHA-256、hexは小文字・区切りなしとする。
AINET ID = "ainet:node:1:" || hex(H(ASCII("AINET-ID-v1") || 00 || PUB))
ID文字列はUTF-8 text stringで伝送する(0.1では短縮バイナリIDにしない)。 秘密鍵をネットワークに送ってはならない。
libp2p側では同じ公開鍵を標準Ed25519公開鍵protobuf(08 01 12 20 || PUB)にし、
identity multihash(00 24 || protobuf)から標準Peer
IDを導出する。 接続登録時にPeer IDから公開鍵を取り出し、AINET
IDとの対応を検証する。
Noiseが認証した実際の接続鍵でも同じ検証を行う。名乗りだけで認証しない。
鍵変更は別AINET IDになる。鍵移行・失効・人や企業への身元証明は0.1の対象外。
Manifest
| キー | 型 | 意味・制限 |
|---|---|---|
| 0 | uint | 形式バージョン。1 |
| 1 | bytes32 | Content Hash = SHA-256(元のファイル全体) |
| 2 | uint | Content Type:1=UTF-8文章、2=PNG、3=JPEG |
| 3 | uint | 本体の正確なバイト数。種類ごとの上限を適用 |
| 4 | text | Author ID |
| 5 | bytes32 | Author Public Key。キー4を導出できること |
| 6 | uint | Created At。正のUnixミリ秒。発行者の申告であり認証された時刻ではない |
| 7 | array(text) | Provenance。最大16件、各2048バイト。有効なUTF-8。主張として保存 |
| 8 | array(text) | References。最大64件の正しいObject ID |
| 9 | uint | Access Policy:0=Private(配布不可)、1=Public(明示公開時に配布・再配布可) |
全項目必須。Object IDと署名はManifestに含めない。
M = CoreDetCBOR(Manifest)
Object ID = "ainet:object:1:" || hex(H(ASCII("AINET-OBJECT-ID-v1") || 00 || M))
Signature input = ASCII("AINET-OBJECT-SIGN-v1") || 00 || ASCII(Object ID)
Signature = Ed25519.Sign(author_private_key, Signature input)
標準Ed25519の署名であり、Ed25519phではない。固定接頭辞は用途の混同を避けるためのもの。
Recordは{0: Manifest, 1: Object ID(text), 2: Signature(bytes64)}。
受信側は作者IDと鍵の対応、IDの再計算、署名、本体のサイズ・ハッシュ・形式をすべて検証する。
署名が証明するのは鍵の主体と改ざん有無であり、出典の事実性や著作権ではない。
本文の上限と境界
| 種類 | 最大バイト数(上限を含む) | 追加条件 |
|---|---|---|
| UTF-8文章 | 1,048,576(1 MiB) | 有効なUTF-8、NULを含まない。空の文章を許可 |
| PNG | 16,777,216(16 MiB) | 正の幅・高さ、幅×高さ ≤ 16,000,000画素、PNGとして復号可能 |
| JPEG | 16,777,216(16 MiB) | 正の幅・高さ、幅×高さ ≤ 16,000,000画素、JPEGとして復号可能 |
例:1,048,576バイトの有効な文章はサイズ条件を満たし、1,048,577バイトは拒否する。
4000×4000画素は画素条件を満たし、4000×4001画素は拒否する。
これらは受理に必要な境界条件であり、署名・ハッシュ・形式の検証も通過する必要がある。
internal/object/object_test.goでは本文変異、文章上限超過、途中で切れたPNG/JPEG、
画素数を偽ったPNGを試験する。上記のすべての境界値を実デコード試験済みとは扱わない。
フレームとEnvelope
libp2pのプロトコル名は/ainet/0.1.0。TCP+Noise+Yamuxを使用する。
制御フレームはuint32 big-endian長 || CBOR Envelope。長さは1〜65536バイト。
長さを確認してからメモリ確保する。OBJECTの直後にだけ、署名されたSizeちょうどの生バイナリが続く。
本体をCBORやBase64で包まない。制御メッセージにJSONは使用しない。
| Envelopeキー | 型 | 規則 |
|---|---|---|
| 0 | uint | Version=1 |
| 1 | text | Message ID。暗号乱数16バイトを小文字hex32文字で表示 |
| 2 | text | From。認証済み接続相手のAINET IDと一致 |
| 3 | uint | 操作コード |
| 4 | text、optional | Reply To。応答対象のMessage ID |
| 5 | map | 下表の型付きBody |
各操作コードは独立の名前空間を持ち、意味を変更・再利用しない。
| 操作コード | 名前 | Body |
|---|---|---|
| 1 | HELLO | {0: 公開鍵bytes32} |
| 2 | GET_OBJECT | {0: Object ID(text)} |
| 3 | OBJECT | Recordそのもの。その後にSizeバイトの本体 |
| 4 | RECEIPT | {0: 保存済みObject IDまたはMessage ID(text)} |
| 5 | MESSAGE | Meaning、下記 |
| 127 | ERROR | {0: エラーコード(uint)} |
OBJECT・RECEIPTを独立した操作にし、要求・送信・保存済みを明確に区別する。 各応答のReply Toは、その応答を生じさせた直前の要求・送信のIDである。
意味、認証、権限のコード
Meaningは{0: Intent(uint), 1: References(array(text)), 2: In Reply To(text, optional)}。
Referencesは1〜16件のObject
ID。送信前に、すべてそのノードで明示公開済みであることを確認する。
受信した参照は自動取得しない。参照先の存在や内容を受領通知で保証しない。
| Intent | 意味 | 解釈 |
|---|---|---|
| 1 | NOTIFY | 指定Objectを知らせる。In Reply Toなし |
| 2 | QUESTION | 先頭の参照Objectが質問本文、残りは背景情報。In Reply Toなし |
| 3 | ANSWER | 先頭の参照Objectが回答本文、残りは根拠。In Reply Toは質問のMessage IDで必須 |
ANSWERは、その相手との間に記録済みのQUESTIONを指定しなければならない。 人間がCLIで作ることができ、AIによる解釈や回答生成を必要としない。 「情報を参照する」は参照配列、「権限を確認する」は署名付きPolicyとローカル公開状態・相手の許可の 同時確認として定義する。機密Objectへの独立した存在照会・権限照会APIは作らない。
認証状態コードはローカル状態として0=未認証、1=鍵とIDの対応確認済み、2=手動登録済み参加者とする。 これはアプリケーションが遷移させる状態であり、受信側に信用させる自己申告フィールドではない。 身元提示はHELLO、鍵の保有証明はNoise、IDの対応・許可確認は通信アダプターが担当する。
状態遷移
- 双方で相手を手動登録。Noise相互認証とID対応検証。許可されない接続はここで拒否。
- 新しいAINETストリームごとにHELLO要求とHELLO応答。応答は要求IDを参照。
- GET_OBJECTまたはMESSAGEを一つ送る。他の順序は拒否。
- GET_OBJECTなら、公開・権限を確認してOBJECTと本体を送る。
- 取得側が検証・正式保存し、OBJECTのMessage IDをReply ToにしたRECEIPTを返す。
- MESSAGEなら、構造・質問回答の対応・重複IDを検査して保存し、MESSAGEへのRECEIPTを返す。
- その要求用ストリームを閉じる。新しい要求は新しいストリームでHELLOから行う。
メッセージの認証・暗号化はNoise接続で保証する。制御Envelopeに独立した署名は付けない。 保存・転送後にも検証するObjectは独立署名を持つ。履歴やRECEIPTは第三者向けの署名付き証明ではない。
受領通知・再送・保存の範囲
RECEIPTは、受信側が検証とローカル保存処理を完了した後に送る。Objectは一時ファイルを 検証し、ファイルのsync、本文のrenameとディレクトリsync、Recordの原子的な書き込みと ディレクトリsync、検証ログのsyncを行う。MESSAGEは記録ファイルのsync・rename・ ディレクトリsync後に応答する。OS・ファイルシステム・記憶装置がこれらの操作を正しく 提供することを前提とする。受領通知を受け取れない場合でも保存済みの可能性がある。
MESSAGEの重複判定キーは「認証した送信者のAINET ID+Message ID」。同じ宛先と同一の Meaningを同じIDで再送した場合、保存件数を増やさずRECEIPTを返す。同じキーで宛先や Meaningが異なる場合はINVALIDとする。Envelopeの応答対応は要求ごとに検証する。 履歴には自動期限切れ・自動削除を設けず、保存領域が維持される限り再起動後も判定する。 新しい履歴は最大1024件。既存の同一内容への再送は件数上限到達後も扱う。管理者が保存領域を 消去・巻き戻しした後の重複排除は保証しない。CLIは送信ごとに新しいIDを生成し、 同じIDを指定する再送APIや自動再試行は現時点では提供しない。
GET_OBJECTの再試行は同じObject IDを指定できる。検証済みの同一Objectは同じ識別子で保存し、 別の論理Objectを追加しないが、本文や検証ログへの再書き込みは起こり得る。 同じObject IDに対する異なるRecord・本文も通常のID・署名・本文検証を必須とする。 受領通知がないことを理由に取得済みのObjectを自動再公開しない。
確認済みの障害範囲は単一Linuxでのプロセス再起動・SIGKILLであり、電源断やOS停止時の 実機耐久試験は未実施。本文の配置後・Record保存前の停止では参照されない本文が残り得る。 分散複製完了、永久保存、厳密に一度限りの配送、受信側の善意をRECEIPTは保証しない。
0.1参照実装の資源制限(セキュリティ修正後)
ノード全体の処理枠は4。同じ認証済み相手から受け入れる処理は同時2件までとする。 受信本文を待つ間は保存領域全体のロックを保持せず、容量を事前予約して検証後に確定する。 保存容量は1 GiB、Objectは4096件、MESSAGE履歴は1024件。容量予約は一時ファイル分を 保守的に数えるため、物理上限に達する前に拒否する場合がある。 HELLOには5秒、後続操作には30秒の期限を設け、Go APIの呼び出し側が指定した短い期限と キャンセルは通信I/Oにも反映する。CLI切断によるキャンセル伝播やCPU処理の割り込みとは別である。
この相手別上限は1台による処理枠の独占を制限する。複数の許可済み相手による共謀、 一般的な公平性、無制限の接続元からのDoSを解決する方式ではない。待機キューと BUSYに基づく再試行は未実装。ワイヤの符号化とID計算は修正前後で変更していない。
エラーと互換性
| エラー | 意味 |
|---|---|
| 1 | NOT_AVAILABLE。存在しない・非公開・停止済み・提供時検証失敗を区別しない |
| 2 | INVALID。形式、参照、順序、保存失敗など。内部事情は相手に返さない |
| 3 | UNSUPPORTED。非対応バージョン・操作コード |
| 4 | BUSY。将来の明示応答用に予約。現実装の資源超過はストリームを拒否する |
HELLOを解釈できない場合は切断し、後続要求の不正では可能ならERRORを返して閉じる。 フレーム破損・長さ超過・時間超過では、ERRORまたは切断による拒否を許す。 認証前に機密情報や詳細な内部エラーを返してはならない。 形式1は厳密な互換性を優先し、未知のフィールド・操作・バージョンを無視しない。 将来版は別のプロトコル識別子と能力交渉を設計する。形式1の意味を変更しない。
設計のみの拡張
| 予約操作 | 将来のBody案・意味 |
|---|---|
| 16 DISCOVER | 種別、要求ID、上限。許可済みのノード・公開Objectの候補を返す |
| 17 QUERY | 検索プロファイル番号、型付き条件、上限。結果はObject参照と根拠 |
| 18 SHARE | 公開Object ID、サイズ、形式を提示。受信側が明示的にGETする |
| 19 VERIFY | 対象Object ID、検証種別。将来は検証者署名付きの別Objectを返す |
上記は予約・設計段階であり、形式1の実装はUNSUPPORTEDを返す。発見・検索・共有・検証の 自動処理を実装済みとして広告しない。入力制限・権限・検証範囲は将来の仕様確定が必要。
独立した試験例
test-vectors.jsonはPythonの小さな別CBOR実装と標準Ed25519ライブラリで生成した。
Go実装と、公開鍵・ID・正準バイト列・署名・本文の一致をテストする。
tests/vectors.pyで再生成できる。これは全面的な第2互換実装ではなく、符号化・暗号の試験例である。