Repository navigation
Operations Backup Multiverse
multiverse 構成(MV3〜MV5)における per-universe の継続バックアップと災害復旧 (DR) の運用ページ。standalone(単一ユーザー)構成のバックアップは Operations — Compact & Backup を参照。 起票: 2026-07-04(MV5 完遂) 関連: Operations — Multiverse Setup(MV3)、Operations — Control Plane(MV4)、Operations — Compact & Backup、Operations — Tuning、Plans — Multiverse Scale-Out §Stage 4、multiverse-implementation-plan.md §MV5、multiverse-mv5-plan.md
MV5 は各宇宙(universe)の gaottt.db を Litestream(SQLite WAL レプリケーション)で継続バックアップし、災害時に SQLite + manifest の 2 点セット から engine レベルの復旧をできるようにする仕組みと手順を提供する。FAISS ファイルは scripts/rebuild_faiss_from_db.py で 同一 embedder artifact + 同一 version + 同一 prefix/normalization が揃えば決定論的に再構築可能(2026-05-31 の FAISS reverse-overwrite incident 復旧で実証済み)なので、バックアップ対象は意図的に最小化されている。コード成果物は Litestream 設定ファイル生成スクリプト・supervisor hook・DR drill の 3 点で、運用手順(本ページ)が成果物の本体。
★ 境界確定(multiverse-mv5-plan.md §0 / §1 D4 反映): MV5 が扱うのは per-universe data recovery のみ。各宇宙の
gaottt.db+manifest.jsonの 2 点セットだけが復旧対象。
本 stage のスコープ外(商用運用の別作業として runbook の人手手順で案内):
| 対象 | 復旧方法 | 理由 |
|---|---|---|
<multiverse_root>/registry.db(supervisor local registry) |
supervisor の reconcile() が on-disk universes/ から再構築。ただし API key hash の連続性・port 割当履歴は保証されない → API key 再発行が必須 |
registry は index であり source-of-truth ではない(Plans — Multiverse Scale-Out §0)。directory が正、registry は突き合わせ |
| MV4 control plane (Postgres) | Postgres 自体の標準バックアップ/復旧運用(別途) | 本 stage は関与しない(設計判断 J5: control plane は aggregator/audit 収集点、運用 deprovisioning 権威なし) |
backend.token |
復旧後の初回 route で supervisor が re-spawn 時に再生成 | 設計どおり(MV3)。旧 token の連続性は保証しない |
owner.lock |
復旧時の初回起動で新 lease が取得される | 旧 lease の owner_id 連続性は保証しない(MV2) |
| ファイル | 役割 | バックアップ経路 |
|---|---|---|
gaottt.db |
全ノードの content / metadata / mass / displacement / velocity / 共起 edge。唯一の source-of-truth | Litestream(SQLite WAL レプリケーション)で継続 replica |
manifest.json |
embedder identity(embedder_id / embedder_version)+ embedding_dim + managed flag。復旧後の verify_embedder_identity 整合ガードの鍵 |
Litestream は対象外(WAL のみ replicate)。file-level snapshot or exec command で別経路必須(下記「Litestream 雛形の使い方」) |
FAISS ファイル(gaottt.faiss / gaottt.virtual.faiss)はデフォルトでバックアップ対象外。理由: (a) SQLite + embedder artifact から決定論再構築可能、(b) WAL stream との衝突回避、(c) RPO/RTO 対象の最小化。大宇宙向けには FAISS snapshot も optional で設定可能(下記)。
★ 「SQLite だけで復元できる」は前提条件つき: FAISS 再構築の決定論は 同一 embedder artifact(model weights)+ 同一 version + 同一 prefix/normalization 実装 が揃って初めて成立する。manifest の
embedder_id/embedder_versionはその照合キー。モデルが HF から消える・依存ライブラリ更新で encode 数値が変わるリスクに備え、embedder artifact の pin / ミラーは 商用運用の必須要件(下記)。
deploy/litestream.yml がテンプレート。dbs: ブロックは scripts/gen_litestream_config.py が <multiverse_root>/universes/*/ を scan して生成する(宇宙の増減で再生成)。
# 手動で設定ファイルを生成(stdout に YAML、diagnostics は stderr)
.venv/bin/python scripts/gen_litestream_config.py --root /var/lib/gaottt-multiverse
# ファイルへ atomic write(supervisor hook が設定時はこれを自動実行)
.venv/bin/python scripts/gen_litestream_config.py \
--root /var/lib/gaottt-multiverse \
--output /etc/litestream/gaottt.yml生成される YAML(litestream v0.3 schema):
dbs:
- path: /var/lib/gaottt-multiverse/universes/<uid>/gaottt.db
replicas:
- type: file
path: /var/lib/litestream/gaottt-multiverse/<uid>manifest のバックアップは litestream と別経路で必須。3 つの選択肢から環境に合うものを選び、runbook に明記すること:
-
(A) filesystem snapshot 推奨 —
<multiverse_root>全体を ZFS / Btrfs / LVM snapshot。SQLite と manifest をほぼ同時点に capture でき最も堅牢。embedder identity は不変(宇宙の生存期間中に変わらない)ので、SQLite と manifest の snapshot が数秒ズレても復旧後の照合は成功する。 -
(B) litestream
execcommand —deploy/litestream.ymlのコメント例を参照。各 WAL sync 後にcp -a universes/<uid>/manifest.jsonを平行ディレクトリへ mirror。 -
(C) cron rsync —
universes/*/manifest.jsonを定期的に object storage へ sync。
supervisor は litestream_config_path knob(Operations — Tuning §MV5)が設定されているとき、POST /admin/universes(作成)と DELETE /admin/universes/{uid}(削除)の 成功経路の最後 で generate_litestream_config 純粋関数を direct import で呼び、atomic write(tmp + fsync + os.replace)で設定ファイルを再生成する。
# supervisor 起動時に litestream 設定ファイルのパスを指定
GAOTTT_LITESTREAM_CONFIG_PATH=/etc/litestream/gaottt.yml \
GAOTTT_MULTIVERSE_ROOT=/var/lib/gaottt-multiverse \
GAOTTT_SUPERVISOR_ADMIN_KEY=... \
.venv/bin/python -m gaottt.multiverse.supervisor設計特性(multiverse-mv5-plan.md §1 D2):
- default 不変: knob 未設定(空文字列)なら hook は一切呼ばれず、MV3/MV4 構成は 1 行も変わらず動く。
- best-effort: hook 内の例外は ERROR log のみ。バックアップ設定ミスで宇宙作成/削除が倒れる事故を構造的に防ぐ。
-
scan + write 直列化: 専用の
_backup_hook_lock(asyncio.Lock)で scan → atomic write を 1 つの臨界区間 で行う。並行 create/delete hook が out-of-order に完了しても、最後の writer の scan 時点の on-disk 状態が最終 YAML になる(stale write の構造的排除、Codex review round-2 B2)。 - import-based(subprocess ではない): 例外隔離・testability・レイテンシの全てで有利。純粋関数は disk IO のみ。
-
spawn env 隔離:
_build_spawn_envはGAOTTT_*を strip して allowlist だけ再注入する設計。litestream / backup 関連 knob は backend に 絶対に漏れない(hook は supervisor プロセス内のみで動く)。
★ 既知の制約(multiverse-mv5-plan.md §5 R2/R4): create/delete の hook は設定ファイルを 次の操作または定期再生成で反映 するが、新規宇宙の
gaottt.dbが litestream config に現れるまでには構造的なラグがある。
ラグの原因: POST /admin/universes(create)は宇宙ディレクトリ + manifest.json + registry 行を作った 直後 に backup hook を発火する。しかしこの時点ではまだ backend が起動しておらず、gaottt.db がディスク上に存在しない。generate_litestream_config は db 無しの宇宙を WARN skip する(WP-1 scan rule)ので、新規宇宙はこの時点の YAML に含まれない。gaottt.db が作られるのは 最初の /route で backend が spawn され、engine が SqliteStore を初期化したとき である(開発機構成では embedder もこの /route の前段で lazy spawn される — Tuning §MV3 follow-on、/route 1 回で embedder + backend の 2 段 spawn が直列で走る)。
v1 の構造的緩和: backend が readiness を確認した直後の _ensure_locked 成功経路でも hook を発火するよう実装した(supervisor.py _ensure_locked)。これにより、新規宇宙の 最初の route で db 作成 → 即座に rescan → YAML に現れる、という経路が閉じる。ただし「最初の route が来る前」の窓(create 直後〜最初の route)は残る。
定期再生成 cron による決定的な閉包(推奨): 上記の窓を確実に埋めるため、litestream config の定期再生成 cron を併用すること。create/delete の hook と _ensure_locked の hook に加え、定期的に全宇宙を rescan すれば、どの経路でも取りこぼされた宇宙が遅くとも次の cron 周期で config に現れる:
# 毎時 0 分に litestream config を再生成(周期は環境に合わせて調整)
0 * * * * /opt/gaottt/.venv/bin/python /opt/gaottt/scripts/gen_litestream_config.py \
--root /var/lib/gaottt-multiverse \
--output /etc/litestream/gaottt.ymlcron と supervisor hook は同じ
_backup_hook_lock(同一プロセス内)または atomic write(プロセス間)で直列化されるため、並行実行しても YAML が torn になることはない。
★ 既知の制約: supervisor の
_build_spawn_envはGAOTTT_*変数のみを strip し、それ以外の環境変数(AWS_SECRET_ACCESS_KEY/LITESTREAM_*/ object-store 認証 等)は spawned backend に そのまま継承 される設計。allowlist 化(structural hardening)は別 stage の課題。
supervisor プロセスの環境に litestream / object-store の credential が含まれていると、各宇宙の backend プロセスにもそれらが漏れ渡る。credential は supervisor とは別の、litestream daemon 専用の環境から供給すること:
- litestream daemon は 独立した systemd unit(
litestream.service)で起動し、/etc/litestream/env等の専用 env file からのみ credential を読み込む(deploy/litestream.ymlの冒頭コメントに最小 unit 例を記載)。 - supervisor の systemd unit(または起動 shell)には litestream / object-store 認証を 置かない。
- これにより「supervisor と litestream が同一ホストで動く構成」でも、backend が object-store への書込権限を持つ事故を防ぐ。
災害発生時(ディスク障害・誤削除・宇宙ディレクトリ破損 等)の標準復旧手順。各宇宙単位で独立に適用 する。
# 0. 復旧対象の宇宙 uid を特定。multiverse_root を環境変数で。
ROOT=/var/lib/gaottt-multiverse
UID=abc123
# 1. 全プロセス停止(supervisor + 全 backend) — write-behind 上書き罠の防御
systemctl stop gaottt-supervisor
ps -ef | grep 'gaottt.server.mcp_server' | grep -v grep # 残りがいないか確認
# owner lease が release されたことも確認(lease 保持中の強制移動は NG)
cat $ROOT/universes/$UID/owner.lock 2>/dev/null || echo "no lease file (OK)"
# 2. SQLite + manifest を restore(2 点セット)
mkdir -p $ROOT/universes/$UID
# Litestream から SQLite を restore(litestream binary 必須)
litestream restore -o $ROOT/universes/$UID/gaottt.db \
-replica /var/lib/litestream/gaottt-multiverse/$UID
# manifest は別経路(filesystem snapshot / exec mirror / rsync)から restore
cp -a /backup/gaottt-multiverse-manifests/$UID/manifest.json $ROOT/universes/$UID/
# 3. embedder artifact が利用可能ことを確認(manifest.embedder_id + version に対応する model)
# → 詳細は下記「embedder artifact pinning」
.venv/bin/python -c "from gaottt.embedding.ruri import RuriEmbedder; RuriEmbedder('cl-nagoya/ruri-v3-310m').dimension"
# 4. FAISS 再構築(SQLite から決定論的に)
GAOTTT_DATA_DIR=$ROOT/universes/$UID .venv/bin/python scripts/rebuild_faiss_from_db.py --apply
GAOTTT_DATA_DIR=$ROOT/universes/$UID .venv/bin/python scripts/rebuild_faiss_from_db.py --check
# 5. 起動時診断(Tier A FAISS integrity / Tier B size consistency)が ERROR 0 件であることを確認
# → supervisor 再起動時の lifespan で自動実行。ERROR があれば手順 1〜4 を見直す。
# 6. supervisor 再起動
systemctl start gaottt-supervisor
# supervisor の reconcile() が registry を再構築。API で universe 一覧を確認。managed 宇宙は manifest.managed=true で owner lease が強制される。復旧時の初回起動で新 lease が取得されるが、以下は人手で対応する:
-
API key 再発行: registry.db は
reconcile()で再構築できるが、API key hash の連続性は保証されない(仮定 6)。旧 key は無効 → ユーザーへ新 key を再発行(POST /admin/universesではなく、registry への直接 key 再発行手順、または該当宇宙の削除→再作成)。 -
backend token 再生成: 復旧後の初回 route で supervisor が再 spawn し、新 token が
backend.tokenに書かれる。旧 token を使っていた shim は再 route で新 token を取得する。 - control plane 再同期(MV4 使用時): supervisor の定期 sync が復旧後の universe 状態を control plane へ反映する。手動で即時 sync したい場合は supervisor 再起動。API key 再発行の事実は audit log に記録する。
★ 商用運用の 必須要件。「SQLite だけで復元できる」は同一 embedder artifact が入手できる前提で成り立つ。
manifest の embedder_id(例 cl-nagoya/ruri-v3-310m)+ embedder_version(HF snapshot commit hash)に対応する model weights を、復旧時に確実に入手できる状態にしておくこと。HF から model が消える・依存ライブラリ(transformers / sentence-transformers / torch)の更新で encode 数値が微妙に変わるリスクに備える。
推奨手法(環境に応じて選択):
-
HF cache の tar 退避: 運用中の HF cache(
~/.cache/huggingface/hub/models--cl-nagoya--ruri-v3-310m/snapshots/<commit>/)を丸ごと tar でバックアップディレクトリへ退避。復旧時に展開するだけで決定論が保証される。 - 社内ミラー: 社内の model registry / S3 ミラーへ snapshot を保持。HF Hub が落ちていても復旧可能。
-
version pin:
requirements.txt/uv.lockでtransformers==X.Y.Z/torch==A.B.Cを pin。embedder_versionは HF revision を記録するが、依存ライブラリの version も encode 数値に影響しうるので、embedder_version+ 依存 lock file の両方を保存する。
scripts/dr_drill.py の drill は stub embedder でこの前提を検証する(下記)。商用投入前は 本番の embedder artifact で同様の pin 手順 を別途試験すること(商用導入前チェックリスト)。
FAISS はデフォルトでバックアップ対象外だが、大宇宙(数万ノード以上)では FAISS 再構築の RTO が無視できなくなる(再 embed の時間)。RTO 短縮のため、FAISS ファイル自体も replicate 対象にする設定例:
- filesystem snapshot(ZFS / Btrfs / LVM)なら FAISS も同時に capture されるので、手順 (A) を採用していれば自動対応。
- litestream 経由ではないので、
gaottt.faiss/gaottt.virtual.faissは別途 rsync / object storage sync で定期的に退避。復旧時は SQLite restore → FAISS restore →rebuild_faiss_from_db.py --checkで整合確認(--applyは不要、FAISS が揃っていれば skip される)。
scripts/dr_drill.py は standalone(managed=False)宇宙で バックアップ → 破壊 → 復元 → FAISS rebuild → 起動時診断 green → top-1 determinism 保持 の完全な DR 経路を自動検証する。litestream binary は不要(default は生ファイルコピー、--with-litestream で binary 経路も検証)。
# 四半期に 1 回、本番とは別の tmp root で実行(exit 0 = 成功)
.venv/bin/python scripts/dr_drill.py
.venv/bin/python scripts/dr_drill.py --with-litestream # litestream binary がある環境で何を証明するか: 「standalone 宇宙について、gaottt.db + manifest.json + embedder artifact が揃えば、FAISS rebuild → engine.startup → 起動時診断 green → 固定 query の top-1 が復元前後で一致する engine レベル復旧ができる」(仮定 2: StubEmbedder の決定論性)。
何を証明しないか(runbook の人手作業):
- managed 宇宙の完全復旧(lease / backend token / registry / control plane 再同期)— 上記の人手手順
- litestream binary を実際に立ち上げての WAL restore e2e — 外部依存、商用導入前チェックリストで別途試験
- MV4 control plane (Postgres) の復旧 — 本 stage のスコープ外(Postgres は別運用)
商用環境へ MV5 を導入する前に、以下を 本番環境と同じ構成で 別途試験すること(dr_drill では検証できない外部依存を含む):
- litestream binary 実機試験: 実際の litestream binary で WAL replica →
litestream restoreの e2e を、本番サイズの dummy 宇宙で実施。雛形deploy/litestream.ymlのdbs:/replicas:schema が実機で受理されるか確認。 - litestream 厳格検証(手動):
dr_drill.py --with-litestreamは best-effort(binary 不在・subprocess 失敗を ERROR log するが drill は落とさない)。本番投入前は 実際の litestream binary で snapshot →litestream restoreの完全な WAL e2e を手動で 1 回実施し、replica から復元した SQLite でrebuild_faiss_from_db.py --checkが通ることを確認すること。 - litestream config の新規宇宙取りこぼし確認: 新規宇宙を作成し、定期再生成 cron 1 周期(または最初の route)待った後に生成された
/etc/litestream/gaottt.ymlにその宇宙のgaottt.dbが含まれることを確認(上記のラグ対策の確認)。取りこぼしがあれば cron 周期を短くするか、定期再生成 cron の導入を見直す。 - litestream / object-store credential 分離の確認: supervisor プロセス環境に
AWS_SECRET_ACCESS_KEY/LITESTREAM_*等の認証が含まれていないこと、litestream daemon が独立 systemd unit + 専用 env file から起動していることを確認(上記)。 - manifest バックアップ経路の選定と試験: filesystem snapshot /
execmirror / rsync のいずれかを選び、復旧時に manifest が揃うことを実機確認(manifest 欠落はverify_embedder_identityで検出されるが、商用では未然に防ぐ)。 - managed 宇宙 DR drill(人手): standalone の
dr_drill.pyに加え、managed 宇宙で「supervisor 停止 → 各宇宙 restore →reconcile()→ API key 再発行 → control plane 再同期」の人手手順を 1 回リハーサル。 - registry 復旧手順の確認:
registry.db破損時にreconcile()が on-diskuniverses/から再構築すること、API key は再発行が必要なことを関係者へ周知。 - embedder artifact pin の実機確認: 本番 embedder で HF cache tar 退避 or 社内ミラーが機能し、復旧時に同一数値の encode が得られることを確認(dr_drill は stub embedder で検証するため、本番 embedder では別途)。
- Postgres 復旧(MV4 使用時、別運用): control plane の Postgres バックアップ/復旧は標準的な商用 DB 運手順で別途設定。本 stage は関与しない。
(formerly GER-RAG)
- 1. Welcome
- 2. Install GaOTTT
- 3. Connect Your Client
- 4. First Conversation
- 5. Everyday Use
- 6. When Stuck
- As Long-Term Memory
- As Task Manager
- As Persona Base
- As Narrative Engine
- Multi-Agent Setup
- Per-Project DBs
- Multiverse (1人1宇宙)
- Ambient Recall
- Visualization
- Server Setup
- Multiverse 運用プレイブック(統合)
- Multiverse クイック健康チェック(パニック時)
- Multiverse Setup (MV3)
- Multiverse Import Universe
- Control Plane (MV4)
- Backup & DR (MV5)
- Resource Requirements (VRAM/RAM/Disk)
- Tuning
- Ingestion (files & chat history)
- Migration (upgrade)
- Compact & Backup
- Isolated Benchmark
- Performance Testing (7-tier)
- Troubleshooting
- Roadmap
- Backend Phase A/B/C
- Phase D — Persona & Tasks
- Phase G — Memory Genesis
- Phase H — Wave Seed Redesign
- Phase I — Free Star Movement
- Phase J — Persona-Anchored Retrieval
- Phase K — Stellar Supernova Cohort
- Phase L — Hybrid Retrieval
- Phase M — Mass Conservation
- Phase N candidate β — Mass Evaporation
- Phase O — TTT Observability
- Phase P — Pressure Terms (Λ + Langevin)
- Phase Q — Orbital Mechanics (Rosette Orbits)
- Phase Q2 — Gravitational Scale Matching
- Phase T — Semantic Requalification
- Phase U — Review Hardening
- Observation Apparatus Refinement
- Observation Apparatus Round 2 (dogfooding 2026-06-12 起点)
- Hardening — Concurrency & Persistence
- Ambient Recall Enrichment
- Ambient Recall Refinement (Phase A 由来)
- Ambient Recall Lateral Association (〇〇といえば〜)
- Accretion Recall (降着想起・〇〇ってなんだっけ)
- Synaptic Pruning (連想の忘却・edge decay)
- Save Candidates Hook (ambient-recall の write-side 対称)
- Lens Hygiene (ambient block の漏れ防止)
- Instruction Surface Hygiene (指示表層と hook harness)
- Query as Mass Distribution
- Multiverse Scale-Out (大規模化・1人1宇宙)
- SKILL.md Improvement
- Embedder Comparison (RURI vs RikkaBotan)