Chapter 1 / 3 ― 入門編
最小組込 ― WASM ロードと最初の write/read
SlimeTree-RLM の WASM を静的ホストから読込み、write() / read() / verify_audit_chain() を 1 つの HTML ファイルで動かすのがゴール。SharedArrayBuffer なし、single-thread。Chapter 2 で SAB を入れる前の足場固め。
1.1 配布形態 ― どこから WASM を入手するか
SlimeTree-RLM の WASM ポートは 3 つの配布形態で提供されます (ライセンス契約後)。
| 静的ファイル一式 | slimetree_rlm.js + slimetree_rlm_bg.wasm の 2 ファイル。任意の HTTPS 静的ホストに置けば動作。最小入門に推奨 |
|---|---|
| npm パッケージ | @javatel/slimetree-rlm (限定配布)。bundler (Vite/webpack/esbuild) 統合向け。import init from '@javatel/slimetree-rlm'; |
| Rust crate | slimetree-rlm ソース crate (NDA + ライセンス)。自前で WASM をビルドする中級編 (Chapter 2) で使用 |
1.2 ファイル配置と HTTP server 要件
以下のディレクトリ構成を想定。HTTPS 必須は Chapter 2 以降 (SAB) ですが、本章でも localhost または HTTPS を強く推奨 (ブラウザ拡張機能との互換性のため)。
my-rlm-app/
├── index.html
├── slimetree_rlm.js # 配布ファイル (ES module loader)
└── slimetree_rlm_bg.wasm # 配布ファイル (WASM binary)
最小 HTTP server (Python):
cd my-rlm-app/
python3 -m http.server 8000
# → http://localhost:8000/
Node.js なら:
npx http-server -p 8000 --cors
1.3 最小サンプル ― 1 つの HTML で動かす
以下を index.html として保存し、上記 server で開く。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>SlimeTree-RLM 入門</title>
<style>
body { font-family: system-ui; max-width: 720px; margin: 2rem auto; padding: 0 1rem; }
pre { background: #0d0d0d; color: #e0e0e0; padding: 0.8rem; border-radius: 6px; }
button { padding: 0.6em 1.2em; margin: 0.3em; cursor: pointer; }
</style>
</head>
<body>
<h1>SlimeTree-RLM 最小組込</h1>
<p><button id="write">1. write 5 records</button>
<button id="read">2. read all</button>
<button id="verify">3. verify audit chain</button></p>
<pre id="log">準備中...</pre>
<script type="module">
import init, { SlimeTreeRLM } from './slimetree_rlm.js';
const log = document.getElementById('log');
const print = (msg) => { log.textContent += '\n' + msg; };
// 1. WASM モジュールの初期化 (非同期)
await init();
print('[ok] WASM 初期化完了');
// 2. RLM インスタンスを生成 (single-thread, no-SAB)
// capacity = 1 MiB の linear memory を確保
const rlm = new SlimeTreeRLM({
capacity: 1024 * 1024,
audit: true, // SHA-256 audit chain を有効化
mode: 'in-memory', // 永続化なし
});
print('[ok] RLM インスタンス生成 (capacity=1MiB)');
// --- write ---
document.getElementById('write').onclick = () => {
const samples = [
{ key: 'fact:tokyo:capital_of_japan', payload: 'Tokyo is the capital of Japan' },
{ key: 'fact:fuji:height_m', payload: '3776' },
{ key: 'fact:yen:currency_of_japan', payload: 'JPY' },
{ key: 'fact:tokyo:population_2024', payload: '13929280' },
{ key: 'fact:osaka:population_2024', payload: '2752412' },
];
for (const s of samples) {
const id = rlm.write({
semantic_key: s.key,
payload: s.payload,
source: 'tutorial_ch1_' + new Date().toISOString(),
});
print('[write] id=' + id + ' key=' + s.key);
}
};
// --- read ---
document.getElementById('read').onclick = () => {
const keys = [
'fact:tokyo:capital_of_japan',
'fact:fuji:height_m',
'fact:yen:currency_of_japan',
];
for (const k of keys) {
const rec = rlm.read(k);
print('[read] ' + k + ' -> ' + (rec ? rec.payload : '(not found)'));
}
};
// --- audit chain 検証 ---
document.getElementById('verify').onclick = () => {
const result = rlm.verify_audit_chain();
print('[audit] verified=' + result.verified
+ ' records=' + result.record_count
+ ' head_hash=' + result.head_hash.slice(0, 16) + '...');
};
</script>
</body>
</html>
1.4 動作確認 ― 期待される出力
順番に 3 ボタンを押すと、以下のようなログが <pre> 内に出ます。
準備中...
[ok] WASM 初期化完了
[ok] RLM インスタンス生成 (capacity=1MiB)
[write] id=0 key=fact:tokyo:capital_of_japan
[write] id=1 key=fact:fuji:height_m
[write] id=2 key=fact:yen:currency_of_japan
[write] id=3 key=fact:tokyo:population_2024
[write] id=4 key=fact:osaka:population_2024
[read] fact:tokyo:capital_of_japan -> Tokyo is the capital of Japan
[read] fact:fuji:height_m -> 3776
[read] fact:yen:currency_of_japan -> JPY
[audit] verified=true records=5 head_hash=a3f1d2e8b4c97f5e...
verified=true + head_hash がブラウザリロードで毎回同じになることが、SHA-256 chain が決定論的に動作している証拠です (同一入力 → 同一 chain head)。
1.5 API リファレンス (Chapter 1 で使う 3 つ)
new SlimeTreeRLM(options)
capacity | 整数 bytes。linear memory の上限。既定 1024 * 1024 (1 MiB)。超えると RlmCapacityError |
|---|---|
audit | true で SHA-256 audit chain を有効化 (既定 true)。false にすると性能は上がるが Chapter 3 の監査機能が無効に |
mode | 'in-memory' (既定) / 'persistent' (Chapter 3 で扱う) |
rlm.write({ semantic_key, payload, source })
semantic_key | 文字列。意味駆動型の参照キー。fact:tokyo:capital_of_japan のような category:entity:attribute 命名を推奨 |
|---|---|
payload | 文字列 or Uint8Array。値本体 |
source | 文字列。由来 (LLM 出力なら model_id + prompt_hash 等)。audit chain に同時記録 |
| 戻り値 | 整数 record_id。後の read_by_id() 等で使用 |
rlm.read(semantic_key) / rlm.verify_audit_chain()
read 戻り値 | { id, semantic_key, payload, source, ts, prev_hash } または null (未登録時) |
|---|---|
verify_audit_chain 戻り値 | { verified: bool, record_count: int, head_hash: hex_string, broken_at: int|null } |
1.6 ハマる点 ― 入門で詰まる 5 つ
(1) import 文で 404 / MIME type エラー
ローカルファイル (file://) では ES module が動かない。必ず HTTP server (http://localhost:8000) 経由で開く。
(2) await init() を忘れる
init() は WASM bytes の fetch + WebAssembly.instantiate を行う非同期処理。完了前に new SlimeTreeRLM を呼ぶと TypeError: undefined is not a constructor。
(3) capacity 超過時の対応
1 MiB は約 5000〜20000 レコード (payload サイズ依存)。本番想定なら capacity: 64 * 1024 * 1024 (64 MiB) 程度を最初から指定。RlmCapacityError は try/catch で捕捉。
(4) ブラウザリロードで全データ消える
Chapter 1 は mode: 'in-memory' のため、ページ遷移/リロードで状態消失は仕様。永続化は Chapter 3 (OPFS / IndexedDB) で扱う。
(5) verify_audit_chain が verified: false を返す
同一 RLM インスタンス内では発生しないが、永続化済みデータを別バージョンの WASM で読込んだ場合に発生しうる。broken_at フィールドで破壊された record_id を特定可能。
1.7 入門編はここで完了 ― 次章への橋渡し
Chapter 1 で以下が動くようになりました。
- WASM モジュールの非同期初期化 (
init()) - RLM インスタンス生成 (
new SlimeTreeRLM({...})) - 意味キー付き書込 (
write())、参照 (read()) - SHA-256 audit chain の決定論的検証 (
verify_audit_chain())
しかし以下はまだ 不可能 です。
- LLM 推論 Worker と main thread が 同じ RLM linear memory を同時操作
- 監査 Worker が backend で audit chain を 並行検証
- ページリロード後に 同じ audit chain を引き継ぐ
これらは SharedArrayBuffer + cross-origin isolation + shared-memory WASM が前提です。Chapter 2 で順に組み立てます。
