Live Runtime Rust Strangler Architecture¶
Purpose¶
この文書は、Phase 5 の LT Rust 移行方針を同居型から独立コンテナ型へ全面的に置き換えるための設計正本である。
背景には、既存 Python PT コンテナ aegis-wft に Rust バイナリを同居させたことで Docker build が失敗し、PT・token keeper・関連 runtime が連鎖停止した本番事故がある。
この事故をもって、以下を正式方針とする。
- 既存 PT コンテナに Rust を同居させない
- Rust LT は
aegis-lt-rustとして独立コンテナで稼働させる - Python PT は現行本番を維持し、Rust LT は shadow mode で並行観測する
- cutover は shadow 検証完了後にのみ行う
Executive Summary¶
正しい移行パターンは Strangler Fig である。
- 現行本番:
aegis-wftが注文と live runtime を担当する - 新規 shadow:
aegis-lt-rustが replay artifacts を読み、Rust 側の判断結果を出力する - 両者は
/data/tokens/data/replay/data/stateを介して疎結合に連携する - Python PT の Dockerfile / entrypoint / deploy pipeline は変更しない
Decision¶
Retired Design¶
以下の設計は廃止する。
pt-docker/Dockerfileに Rust multi-stage build を追加するaegis-wftコンテナにlt-shadow/lt-saxo-session/lt-saxo-streamを同居させるwft-entrypoint.shや supervisord / cron に Rust lane を追加するdeploy-pt.ymlから Rust lane を同時にデプロイする
Accepted Design¶
以下の設計を採用する。
aegis_v3/lt-rust-docker/を新設するaegis-lt-rustを独立コンテナとして build / deploy / restart するdeploy-lt-rust.ymlを既存 PT pipeline と完全分離して新設する- Rust LT は shadow mode に限定し、注文を出さない
Core Principles¶
-
Failure isolation Python PT と Rust LT は build failure, dependency failure, process crash を相互に伝播させない。
-
WebSocket-first Saxo balance / positions / orders は最終的に WebSocket streaming を正系とする。 ただし shadow mode の最小稼働は replay 読み取りのみで成立させる。
-
Read-only coupling Rust LT が Python PT の live path に依存するときは、原則として read-only volume 経由に限定する。
-
Strangler rollout Python PT を先に止めて切り替えない。shadow で十分な一致を確認してから cutover する。
-
PT immutability
aegis-wftを動かしている Dockerfile / entrypoint / deploy workflow は、Rust LT 導入のためには変更しない。
Target Architecture¶
flowchart LR
saxo["Saxo LIVE API"]
python["aegis-wft\nPython PT\n(unchanged)"]
rust["aegis-lt-rust\nRust LT\n(shadow mode)"]
tokens["/data/tokens\nshared read-only"]
replay["/data/replay\nPython writes\nRust reads"]
state["/data/state\nshared artifacts"]
saxo --> python
python --> replay
tokens --> rust
replay --> rust
rust --> state
Container Responsibilities¶
aegis-wft¶
役割:
- live PT execution
- Saxo / Polygon 連携
- replay artifact 生成
- 現行の注文・約定・監視フローを維持
制約:
- Rust バイナリを同居させない
- Rust LT の build dependency を持ち込まない
- Rust LT の起動責務を持たない
aegis-lt-rust¶
役割:
lt-shadowによる replay 読み取り- Rust side shadow summary の出力
- 必要に応じて
lt-saxo-session/lt-saxo-streamを独立診断として実行
制約:
- 注文を出さない
- Python PT の process lifecycle を制御しない
- shared volumes 以外で Python PT に依存しない
Shared Volumes Contract¶
/data/tokens¶
用途:
- Saxo token cache の共有
マウント方針:
- Python PT: read-write
- Rust LT: read-only
理由:
- token keeper の正系は既存 Python lane にあるため
/data/replay¶
用途:
- Python PT が生成した
pt_replay_log/YYYY-MM-DD/scan_*.jsonl
マウント方針:
- Python PT: write
- Rust LT: read-only
理由:
- shadow mode は replay consumer であり producer ではないため
/data/state¶
用途:
- Rust LT の status / summary / bootstrap artifact 出力
マウント方針:
- shared volume だが、Rust LT は
lt_rust/サブディレクトリ配下のみを使用する
理由:
- PT の state と artifact root を物理的には共有しつつ、論理的には namespace を分離するため
Filesystem Layout¶
想定 host path:
/volume1/aegis/
├── tokens/
│ └── saxobank_tokens_live.json
├── wft_state/
│ ├── pt_replay_log/
│ │ └── YYYY-MM-DD/scan_000001.jsonl
│ └── lt_rust/
│ ├── runtime_status.json
│ ├── lt_shadow_summary.json
│ ├── saxo_session_bootstrap.json
│ └── saxo_stream_status.json
Runtime Flow¶
Phase 5 Shadow Flow¶
aegis-wftが通常通り live PT を実行する- Python PT が replay artifacts を
/data/replayに書く aegis-lt-rustが replay root を監視するlt-shadowが Rust 側 summary を/data/state/lt_rust/lt_shadow_summary.jsonに出力する- 必要に応じて差分比較ツールが Python / Rust の一致を評価する
Optional Diagnostics¶
aegis-lt-rust には以下の補助バイナリを含めるが、初期 shadow phase では必須ではない。
lt-saxo-sessionlt-saxo-stream
これらは replay summary を止めずに個別に有効化できるよう、entrypoint または手動 docker exec から独立実行する。
Deployment Model¶
New Build Surface¶
新規ディレクトリ:
New Workflow¶
新規 workflow:
トリガー:
aegis_v3/lt-rust-docker/**aegis_v3/aegis-bt-rs/**.github/workflows/deploy-lt-rust.yml
Deployment Rules¶
deploy-pt.ymlから Rust LT を扱わないdeploy-lt-rust.ymlはaegis-lt-rustだけを build / recreate する- verify step では
aegis-wftが継続稼働していることも確認する
Why the Old Approach Failed¶
| 同居アプローチ | 独立コンテナ |
|---|---|
| PT Dockerfile 変更が PT build failure に直結 | Rust image failure が PT に波及しない |
| entrypoint / supervisord / cron が複雑化する | Rust entrypoint を独立管理できる |
| OpenSSL / build deps が PT image に混入する | Rust image 側だけで依存を解決できる |
| 1コンテナ障害で token keeper / PT / shadow が連鎖停止する | Rust 側が落ちても PT は継続する |
Operational Guardrails¶
Hard No¶
以下は禁止する。
pt-docker/を Rust LT 導入のために変更するwft-entrypoint.shに Rust lane を追加するdeploy-pt.ymlに Rust lane を混ぜるaegis-wftの Docker image に Rust build dependency を追加する
Hard Yes¶
以下を必須とする。
- Rust LT は独立コンテナにする
- shared volume は read-only / namespace 分離を守る
- verify step で
aegis-wft非影響を確認する - cutover 前に shadow parity gate を通す
Cutover Criteria¶
Python PT から Rust LT への本番切り替えは、少なくとも以下を満たした後に限る。
- replay summary が継続して生成される
- Python / Rust の差分レポートが許容範囲に収まる
- Rust side Saxo session / stream diagnostics が安定する
aegis-lt-rustの再起動や build failure がaegis-wftに影響しないことを確認済み
Implementation Sequence¶
Step 1¶
aegis_v3/lt-rust-docker/ を新設し、lt-shadow / lt-saxo-session / lt-saxo-stream を build できる Dockerfile を作る。
Step 2¶
独立 compose で aegis-lt-rust を起動し、/data/replay と /data/state/lt_rust の入出力を確認する。
Step 3¶
deploy-lt-rust.yml を追加し、Synology 上で独立デプロイできるようにする。
Step 4¶
shadow parity の日次確認を運用フローへ追加する。
Step 5¶
cutover criteria を満たした後にのみ、本番切り替え計画を別文書で定義する。
Non-Goals¶
この文書では以下を扱わない。
- Rust 注文執行の本番 cutover 詳細
- Saxo order placement の Rust 実装詳細
- WebSocket reconnect policy の低レベル実装
- 既存 PT container の修復手順
Status¶
現在の設計状態:
- 同居型は廃止
- 独立コンテナ型を正とする
- 実装は
aegis-lt-rust新設を起点に進める
Current Status (2026-04-09)¶
Completed¶
- Steps 1-4: independent container, compose, deploy workflow, daily parity observation
- PT Dockerfile decoupled from Rust
- Both deploy lanes green
- Parity progress visibility in artifacts
Next¶
- First deploy:
gh workflow run deploy-lt-rust.yml --ref main -f force=true - 5 business day parity observation accumulation
- Cutover requires
ready_for_cutover=trueplus manual approval