net-kotoba | Part 2 — 大規模クエリ・multihop・書込・CACAO・Signal CACAO × X3DH/Double-Ratchet × Pregel[BSP] × t-of-N custody
EXPLAINER ─ PART 2

複雑/大規模クエリ・multihop・書き込み
CACAO 認可 ・ Signal E2E はどう動くか

Part 1(Datomic × IPFS × Prolly Tree)の続き

大規模query
join / MV
multihop
path / Pregel
書込/認可
CACAO
E2E秘匿
Signal

▶ 自動再生中 … 下のバーで一時停止・シーン移動

① 複雑/大規模クエリ — join と増分MV

BGP は subject 集合の積で結合する

execute_sparql_graph_pattern(quad_store.rs:1673〜)

?s :role "admin" .
?s :score ?n
triple1 → AVET{e3,e7,e9…}
∩
triple2 → AEVT{e1,e3,e9…}
HashSet 積{e3,e9}

各 triple を最適索引で引いて subject 集合を作り、HashSet で O(1) 交差。線形スキャン無し(JOIN 223→73ms / 10K)。

対応する代数(algebra)
BGP 三つ組結合
FILTER 数値/文字列述語
UNION 併合 + dedupe
OPTIONAL 左外部結合
GROUP BY COUNT/SUM/AVG…
DISTINCT (s,p,o) で重複除去
繰り返しクエリは MaterializedView で“作り直さない”
commit Δassert/retract
program.evaluate_deltaDatalog rules
MvRegistry.maintainderived facts ⊕=

commit ごとの差分だけをルールに流し、結果 Arrangement に畳み込む(mv.rs:7-93)。次回読取は再評価ゼロ。

② multihop — property path と N-hop DESCRIBE

<pred>+ は索引上の BFS(最大64ホップ)

eval_property_path / sparql_describe_n_hop(quad_store.rs:1441〜,2200〜)

?x knows+ ?y を frontier 展開で辿る
x
a
b
c
d
e

各ラウンド AEVT スキャンで次フロンティアを得て、(s,p,o) を HashSet で dedupe。

path 演算子
p+ OneOrMore(BFS)
p* ZeroOrMore
^p reverse(VAET 逆参照)
p1/p2 sequence(連結)
MAX_HOPS = 64
N-hop DESCRIBE は層ごとに
try_join_all で並列 fetch(438→149ms)

「友達の友達の…」も「参照を逆に辿る」も、結局 4 索引への BFS に落ちる。

③ multihop の重い計算 — Pregel BSP

頂点プログラム × superstep × メッセージ伝播

kotoba-vm/src/pregel.rs ・ distributed.rs(libp2p gossip)

superstep k 各頂点が inbox を読む
compute() state 更新 → 隣へ Message 送信
barrier 全頂点同期(BSP)
halt active 頂点ゼロで終了

Vertex{id,state,active} / Message{src,dst,payload=CBOR Delta}(pregel.rs:30-72)。PageRank・最短路・連結成分などをグラフ全体で。

分散 Pregel
node A頂点 1..n
gossipmsg route
node B頂点 n+1..

DistributedPregelRunner が node 跨ぎの Message を async channel + KotobaSwarm gossip でルーティング(QUIC/Noise/GossipSub)。

④ 大きいクエリ — federation で graph を跨ぐ

SERVICE <cid:remote> / GRAPH ?g

GraphPattern::Service / ::Graph(quad_store.rs:2062〜)

local graph
hot Arrangement
+ CommitDag head
SERVICE <cid:mb> → multibase 解析 → KotobaCid
DistributedBlockStore → remote IPFS peer から block 取得
GRAPH ?g → all_graph_cids() を fan-out 列挙

content-address された別グラフを、その CID を service IRI にして横断クエリ。silent=true なら未知サービスは無視。

⑤ 書き込み — transact は graph に縛られる

CACAO 検証 → assert_datom → commit

xrpc transact handler → quad_store.rs commit(:3071〜)

① XRPC
datomic.transact
② CACAO検証
datom:transact
③ graph束縛
db/<did>/<name>
④ assert_datom
pending
⑤ commit
CommitDag head
edge が target graph を強制束縛

書込先は呼び手自身の名前空間 kotobase/db/<tenant_did>/<db> に固定。client が graph を指定しても上書き不可 → 自分のDBにしか書けない。

commit パイプライン
pending 収集 → 4索引 ProllyTree 並列ビルド → tx_cid=blake3(datoms) → DistributedDatomCommit{index_roots, prev, author_sig} → CAR 1束 PUT → CommitDag.add → head-ref 原子更新

許可された operator DID だけは任意 graph に書ける。pod は JWT 署名を検証せず(edge が信頼境界)、CACAO は暗号検証する。

⑥ CACAO 認証/認可 — 主権鍵のケイパビリティ

Cacao{h,p,s} を5段で検証する

kotoba-auth: cacao.rs / delegation.rs(verify)

1時間: iat/exp(exp無しは 最大7日)
2能力: datom:read / datom:transact / kotobase:pin
3graph scope: kotoba://graph/<cid> に含まれるか(multi-graph 可)
4署名: Ed25519(did:key) または EIP-191(secp256k1)
5audience: aud == node operator DID(使い回し防止)+ nonce
depth-2 委譲チェーン
root grantdelegator → service
leaf.iss == root.aud で橋渡し ↓
leaf invocationservice → agent

減衰のみ: leaf 能力 ⊆ root 能力、graph ⊆ root graph。depth-3+ は拒否。

Private
既定。CACAO + graph 一致が必須
Authenticated
Bearer 必要・scope は不問
Public
認証不要で誰でも読める
⑦ Signal E2E — X3DH → Double Ratchet

鍵共有 → メッセージごとに鍵が前進

kotoba-signal: x3dh.rs / ratchet.rs ・ kotoba-crypto: AES-256-GCM

X3DH 初期鍵共有
DH1=ECDH(IK_A, SPK_B)
DH2=ECDH(EK_A, IK_B)
DH3=ECDH(EK_A, SPK_B)
DH4=ECDH(EK_A, OPK_B) 任意
SS = HKDF(DH1‖DH2‖DH3‖DH4)

送受信の双方が同じ shared secret を導出。SPK 署名は IK で検証、OPK は使い捨て。

Double Ratchet(前方秘匿)
root keyDH rotation
chain keyKDF 連鎖
message key毎回使い捨て

各メッセージ mk で AES-256-GCM 暗号化。使用後 mk は zero化、順序ズレは MKSKIPPED にキャッシュ(MAX_SKIP=1000)。鍵漏洩しても過去/未来は守られる。

暗号化 datom(Enveloped value) 値を "signal:v1:{base64url}" で包んで格納(keycodec の Encrypted/Enveloped tag)。暗号文のまま複製、復号は subject==self か許可された agent だけ。
⑧ X-Road 流アカウンタビリティ — 説明可能な秘匿

暗号文複製 + レシート + t-of-N 鍵分散

docs/SECURITY-ARCHITECTURE.md(R0–R3)・kotoba-custody

R0
暗号文のみ複製
sealed_cid 再計算可
R1
全読取で access datom
(who/purpose/ts)
R2
Base L2 anchor
署名 CommitDag
R3
Shamir t-of-N
custody
鍵リリースは「レシートを書いてから」
agentkey.requestShare
custodian 検証CACAO+purpose+nonce
レシート記帳→HPKE share 解放
clientt 個で復元

⚠レシート無しの解放は検知され、custodian は証明可能に有責(slashable)。epoch/deal_id 混在の定足数は拒否。

まとめ — Part 2

重いことは全部「索引 / BSP / CID / 鍵」に落ちる

①複雑query: BGP は subject 集合の HashSet 積、FILTER/UNION/OPTIONAL/GROUP。繰返しは MaterializedView が Δ 増分維持。
②multihop: property path p+ / p* / ^p は索引上 BFS(≤64hop)、N-hop DESCRIBE は層ごと並列 fetch。
③重い計算: Pregel BSP(superstep+message+barrier)、分散は libp2p gossip でルーティング。
④federation: SERVICE <cid:remote> / GRAPH ?g で content-address グラフを横断。
⑤書込: edge が db/<did>/<name> に束縛 → 自分のDBのみ。commit→CommitDag head 原子更新。
⑥CACAO: 時間/能力/scope/署名/audience の5段、depth-2 減衰委譲、Private/Authenticated/Public。
⑦⑧Signal: X3DH→Double Ratchet で毎メッセージ鍵前進・AES-256-GCM。暗号文複製+レシート+t-of-N custody で“説明可能な秘匿”。

主要コード: quad_store.rs / mv.rs / pregel.rs / delegation.rs / x3dh.rs / ratchet.rs / shares.rs

net-kotobase/docs/explainer
…
1.0× 0 / 0