第 4 章 論理フレーム構造
4.1 全体構造
LogicalFrame は 2 つの部分から構成されなければならない:
+----------------+
| Header | (平文)
+----------------+
| Payload | (暗号化)
+----------------+
LogicalFrame の形式的定義:
interface LogicalFrame {
header: FrameHeader;
payload: Uint8Array; // 暗号化済み
}
4.2 フレームヘッダー(FrameHeader)
フレームヘッダーは以下のフィールドを含まなければならず、表中の順序が規範的順序である:
| フィールド | 型 | 必須 | 暗号化 | 説明 |
|---|---|---|---|---|
protocolVersion | ProtocolVersion | 必須 | 否 | プロトコルバージョン |
frameType | FrameType | 必須 | 否 | フレームタイプ |
fragmentId | FragmentID | 必須 | 否 | Fragment の一意識別子 |
agreementId | AgreementID | null | 必須 | 否 | Agreement ID(null 可) |
originTimestamp | OriginTimestamp | 必須 | 否 | オリジナルタイムスタンプ(UTC ms) |
dagDependencies | DAGEdge[] | 必須 | 否 | DAG 依存リスト(空配列可) |
encryptionMetadata | EncryptionMetadata | 必須 | 否 | 暗号化メタデータ |
sequenceNumber | SequenceNumber | 必須 | 否 | 伝送シーケンス番号 |
フレームヘッダーは暗号化してはならない。すべてのフィールドは平文で伝送しなければならない。
4.3 フレームタイプ(FrameType)
FrameType は以下の 4 つの列挙値のいずれかでなければならない:
| 値 | 用途 | Payload の内容 |
|---|---|---|
"data" | 実際の Fragment データを搬送 | Fragment の data フィールド |
"request" | データリクエストの発行または伝送 Agreement の調整 | RequestFrame のシリアライズ内容 |
"response" | データリクエストへの応答 | ResponseFrame のシリアライズ内容 |
"control" | エラー通知、Agreement 終了などの制御情報を伝達 | ControlFrame のシリアライズ内容 |
実装は列挙されていないフレームタイプを導入してはならない。
4.4 プロトコルバージョン(ProtocolVersion)
ProtocolVersion は 2 つの非負整数で構成されなければならない:
interface ProtocolVersion {
major: number;
minor: number;
}
バージョン番号の意味論と変更判定規則は第 1 章 §1.7.2 で定義される規則に従わなければならない(権威ある情報源)。
4.5 Agreement_ID 圧縮
agreementId フィールドは null でよく、圧縮伝送のために用いられる。圧縮規則は以下の要件に従わなければならない:
- 同一 Agreement に属する一連の連続した Fragment において、最初の Fragment のフレームヘッダーのみが完全な Agreement_ID を携帯しなければならない。
- 後続の Fragment の
agreementIdフィールドはnullに設定してもよく、これは直前の non-null の Agreement_ID を継承することを示す。 - 受信側は「現在のコンテキスト Agreement_ID」を維持しなければならず、以下の規則で解釈しなければならない:
agreementIdが non-null の Fragment を受信した場合、その値を現在のコンテキスト Agreement_ID として更新しなければならない。agreementIdが null の Fragment を受信した場合、現在のコンテキスト Agreement_ID に紐付けなければならない。
- 受信側が
agreementIdが null かつ現在のコンテキスト Agreement_ID も null の Fragment を受信した場合、その Fragment を破棄しAGREEMENT_NOT_FOUNDエラー(3001)を返さなければならない。 - 受信側が未知の Agreement_ID を参照する Fragment を受信した場合、その Fragment を破棄し
AGREEMENT_NOT_FOUNDエラー(3001)を返さなければならない。
受信側の現在のコンテキスト Agreement_ID は伝送方向ごとに独立して維持しなければならない。
4.6 オリジナルタイムスタンプ(Origin_Timestamp)
originTimestamp は以下を満たさなければならない:
- UTC タイムゾーンを使用しなければならない。
- ミリ秒精度を持たなければならない。
- 非負整数(Unix タイムスタンプのミリ秒数)でなければならない。
- データが発生源で実際に生成された時刻を記録しなければならず、伝送時刻であってはならない。
- 完全な伝送経路(シリアライズ → 暗号化 → 伝送 → 復号 → デシリアライズ)を経た後、送信前と完全に一致しなければならない。
- 受信側は受信した Origin_Timestamp を変更してはならない。
4.7 DAG 依存(DAGEdge)
DAGEdge は以下のフィールドを含まなければならない:
interface DAGEdge {
targetFragmentId: FragmentID;
relationType: DAGRelationType;
}
DAGRelationType は以下の 3 つの列挙値のいずれかでなければならない:
| 値 | 意味 |
|---|---|
"derived_from" | この Fragment は対象 Fragment から派生した |
"annotates" | この Fragment は対象 Fragment を注釈/解説する |
"supersedes" | この Fragment は対象 Fragment を置き換える |
dagDependencies 配列は空でよい(つまり Fragment が依存関係を持たない)。
4.8 暗号化メタデータ(EncryptionMetadata)
EncryptionMetadata は以下のフィールドを含まなければならない:
interface EncryptionMetadata {
algorithm: string;
keyVersion: number;
}
| フィールド | 規範的要件 |
|---|---|
algorithm | 暗号化アルゴリズムの識別文字列でなければならない(例:"AES-256-GCM")。IANA に登録されたアルゴリズム名を使用すべきである |
keyVersion | 非負整数でなければならない。鍵ローテーションのサポートに用いる |
暗号化メタデータ自体は暗号化してはならず、フレームヘッダー内に平文の形式で含めなければならない。
4.9 シーケンス番号(Sequence_Number)
sequenceNumber は以下を満たさなければならない:
- 非負整数でなければならない。
- 1 つのセッション内で単調増加でなければならない。
- 各伝送方向(データ収集、データ注入)ごとに独立して維持しなければならない。
- シーケンス番号空間は 2 つの方向間で共有してはならない。
- 0 または 1 から開始すべきである。
- シーケンス番号がオーバーフローした場合(実装定義の最大値)、実装は新しいセッションを作成して対処しなければならず、ラップアラウンドしてはならない。
4.10 Fragment 構造
Fragment は以下のフィールドを含まなければならない:
interface Fragment {
fragmentId: FragmentID;
agreementId: AgreementID;
originTimestamp: OriginTimestamp;
contextMetadata: ContextMetadata;
dagDependencies: DAGEdge[];
data: Uint8Array;
}
注:Fragment 中の agreementId フィールドは LogicalFrame にシリアライズされる際、圧縮規則により LogicalFrame ヘッダー上では null として表現される場合があるが(第 4.5 節を参照)、Fragment 自体の論理的な agreementId は常に non-null でなければならない。
4.11 コンテキストメタデータ(ContextMetadata)
ContextMetadata は以下のフィールドを含まなければならない:
interface ContextMetadata {
dataType: string;
source: DataSource;
customFields: Record<string, unknown>;
}
DataSource は以下の 2 つの構造のいずれかでなければならない(kind フィールドで判別される discriminated union):
4.11.1 HardwareSource
データがハードウェアセンサ由来である場合、source は以下でなければならない:
interface HardwareSource {
kind: "hardware";
sensorType: string;
precision: string;
samplingRate: number;
}
| フィールド | 規範的要件 |
|---|---|
kind | リテラル文字列 "hardware" でなければならない |
sensorType | 空でない文字列でなければならない。標準化された命名(例:"accelerometer"、"heart_rate_monitor")を使用すべきである |
precision | 空でない文字列でなければならず、センサ精度を記述する(例:"±0.1°C") |
samplingRate | 正の数でなければならず、単位は Hz |
4.11.2 SoftwareSource
データがソフトウェア共有由来である場合、source は以下でなければならない:
interface SoftwareSource {
kind: "software";
appIdentifier: string;
sharingMethod: string;
}
| フィールド | 規範的要件 |
|---|---|
kind | リテラル文字列 "software" でなければならない |
appIdentifier | 空でない文字列でなければならない。逆ドメイン名形式(例:"com.example.app")を使用すべきである |
sharingMethod | 空でない文字列でなければならず、共有方式を記述する(例:"api_push"、"clipboard_capture") |
4.11.3 カスタムフィールド
customFields は文字列から任意の値へのマッピングでなければならず、空オブジェクト {} でよい。実装は customFields 内に dataType または source 内に既に存在する情報を重複させてはならない。
4.12 シリアライズ要件
LogicalFrame のシリアライズは以下の規範的要件を満たさなければならない:
- 決定性:同一の LogicalFrame オブジェクトは同一のバイナリ出力を生成しなければならない。
- ラウンドトリップ整合性:任意の有効な LogicalFrame について、シリアライズ後再度デシリアライズすると元のオブジェクトと等価な LogicalFrame を生成しなければならない。
- 完全性:シリアライズ出力はフレームヘッダーのすべてのフィールドを含まなければならない。
- ペイロード暗号化:シリアライズの前に、Payload は EncryptionMetadata で指定されたアルゴリズムで暗号化済みでなければならない。
シリアライズの具体的なバイナリレイアウト(バイト順、フィールドエンコード方法)は後続のドラフトで明記する。CBOR(RFC 8949)または Protocol Buffers などの成熟したバイナリ形式を優先すべきである。
4.13 物理フラグメンテーション
下位伝送がフラグメンテーションを必要とする場合(例:BLE の MTU 制限):
- フラグメンテーション操作は Transport_Adapter が担当しなければならない。
- LogicalFrame は DTP_Engine 層で完全性を保持しなければならない。
- 受信側の Transport_Adapter は、DTP_Engine に渡す前に完全な LogicalFrame を再構築しなければならない。
