コンテンツにスキップ

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

  1. Failure isolation Python PT と Rust LT は build failure, dependency failure, process crash を相互に伝播させない。

  2. WebSocket-first Saxo balance / positions / orders は最終的に WebSocket streaming を正系とする。 ただし shadow mode の最小稼働は replay 読み取りのみで成立させる。

  3. Read-only coupling Rust LT が Python PT の live path に依存するときは、原則として read-only volume 経由に限定する。

  4. Strangler rollout Python PT を先に止めて切り替えない。shadow で十分な一致を確認してから cutover する。

  5. 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

  1. aegis-wft が通常通り live PT を実行する
  2. Python PT が replay artifacts を /data/replay に書く
  3. aegis-lt-rust が replay root を監視する
  4. lt-shadow が Rust 側 summary を /data/state/lt_rust/lt_shadow_summary.json に出力する
  5. 必要に応じて差分比較ツールが Python / Rust の一致を評価する

Optional Diagnostics

aegis-lt-rust には以下の補助バイナリを含めるが、初期 shadow phase では必須ではない。

  • lt-saxo-session
  • lt-saxo-stream

これらは replay summary を止めずに個別に有効化できるよう、entrypoint または手動 docker exec から独立実行する。

Deployment Model

New Build Surface

新規ディレクトリ:

aegis_v3/lt-rust-docker/
├── Dockerfile
├── docker-compose.yml
└── entrypoint.sh

New Workflow

新規 workflow:

.github/workflows/deploy-lt-rust.yml

トリガー:

  • 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.ymlaegis-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 への本番切り替えは、少なくとも以下を満たした後に限る。

  1. replay summary が継続して生成される
  2. Python / Rust の差分レポートが許容範囲に収まる
  3. Rust side Saxo session / stream diagnostics が安定する
  4. 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=true plus manual approval