# NixOS Deployment / Scheduler Roadmap (2026-03-20)

## 背景

このリポジトリにはすでに次の材料がある。

- `NixOS` モジュール群: 各サービスの systemd 化とテストクラスタ構成
- `deployer`: bare metal/bootstrap 用の phone-home と node inventory
- `deployer-ctl`: ChainFire 上の cluster desired state を apply する CLI
- `fleet-scheduler`: native service の配置決定
- `node-agent`: 各ノードで process/container を reconcile
- `plasmavmc` / `k8shost`: VM と Pod の個別スケジューラ

ただし、これらがまだ 1 本の「Nix から始まるデプロイ経路」になっていない。

現状は、

- `Nix` で host configuration を作る経路
- `deployer-ctl` の YAML/JSON で cluster state を入れる経路
- `deployer` の phone-home で node を登録する経路
- `fleet-scheduler` / `node-agent` で native service を動かす経路

が並立していて、単一正本と責務境界がまだ弱い。

## いま見えている重要な不足

### 1. `Nix` が単一正本になっていない

- `ultracloud-cluster.nix` / `nix-nos` / `deployer-ctl` の `ClusterStateSpec` が並立している
- static topology をどこで持つかが定まっていない
- node class / pool / enrollment rule / service schedule が Nix から一気通貫で生成されていない

### 2. bootstrap はあるが「NixOS を適用する agent」がない

- `deployer` は `nix_profile` を返せる
- しかし bootstrap ISO は `node-config.json` の `hostname` / `ip` しか実質使っていない
- `node-agent` は process/container 用であり、NixOS generation を apply しない

つまり、`NixOS deployment` と `runtime scheduling` が別々で、間をつなぐ node-side reconciler が存在しない。

### 3. ISO / netboot が generic bootstrap になり切っていない

- ISO は `node01|node02|node03` と `nix/nodes/vm-cluster/$NODE_ID` に寄っている
- real bare metal 用の node class / profile / disk layout 選択器としてはまだ固定的
- `cloud-init` endpoint はあるが、本流はまだ「Nix native bootstrap API」ではない

### 4. `first-boot-automation` は設計途中で本流に乗っていない

- モジュールはあるが実利用配線が薄い
- ChainFire / FlareDB / IAM bootstrap の責務が中途半端
- 現状の main path は `initialPeers` と固定 node 定義で成立しており、ここは未整理

### 5. scheduler の層分離がまだ曖昧

- `fleet-scheduler`: native service 配置
- `plasmavmc`: VM 配置
- `k8shost`: Pod 配置

この 3 つは全部「scheduler」だが、対象が違う。
それぞれの責務を意図的に分離しないと、NixOS 配備責務まで混ざる。

### 6. MaaS 代替としては inventory / commissioning がまだ弱い

- machine-id と enrollment rule はある
- しかし hardware facts, NIC facts, disk facts, BMC/Redfish, power cycle, reprovision, rescue の層は未整備

MaaS を本当に置き換えるなら、少なくとも commission/inventory/reinstall/power の最小セットが必要。

### 7. CI が bootstrap 経路の閉じ方を保証していない

- CI は主に build / fmt / clippy / unit test
- `deployer` + `fleet-scheduler` + `node-agent` + `test-cluster` の end-to-end は publishable gate になっていない

## 目標アーキテクチャ

### 原則

`Nix` を static desired state の単一正本にする。  
動的な reconcile は UltraCloud の各 agent / control plane に任せる。

分け方は以下。

### 1. Static layer: `Nix`

ここで定義するもの:

- cluster / datacenter / rack / VLAN / BGP / IP pool
- node class / pool / hardware policy
- bootstrap seed set
- disk layout policy
- host profile
- native service の desired policy
- install image / bootstrap image の生成

`Terraform` の static な部分はここで置き換える。

### 2. Bootstrap layer: `deployer`

ここでやること:

- node discovery
- enrollment rule による class/pool/profile 決定
- machine-id/MAC/DMI 等と node-id の束縛
- install-time secrets / SSH host key / TLS 発行
- install plan の返却

`cloud-init` / `MaaS` の bootstrap 部分をここで置き換える。

### 3. Node system reconcile layer: 新しい `nix-agent` 相当

ここでやること:

- desired NixOS generation / flake attr / closure を受け取る
- closure を取得
- `switch-to-configuration` を実行
- activation 成功/失敗を report
- 世代 rollback と health gating

ここがない限り、NixOS デプロイは phone-home 後に止まる。

これは既存 `node-agent` の責務とは別物なので、最初は別 agent に分ける方が安全。

### 4. Native runtime layer: `fleet-scheduler` + `node-agent`

ここでやること:

- stateless / movable な native service を worker pool 上に配置
- process/container の desired state を ChainFire に書く
- node-agent が実行と health を担う

`Kubernetes Deployment/DaemonSet/Service` のうち、UltraCloud 自前 native service 用の部分をここで置き換える。

### 5. Tenant workload layer: `plasmavmc` / `k8shost`

ここは別 scheduler のままでよい。

- VM は `plasmavmc`
- Pod は `k8shost`

これらは NixOS host deployment とは分離して扱う。

## やるべきこと

### Phase 0: 単一正本を決める

最優先はここ。

1. `Nix` を cluster source of truth に固定する
2. `deployer-ctl` の YAML/JSON は hand-written ではなく `Nix` から生成する
3. `ultracloud-cluster` と `nix-nos` の重複生成ロジックを統一する

推奨:

- `nix/lib/cluster-schema.nix` を作る
- そこから次を生成する
  - `nixosConfigurations.<node>`
  - bootstrap 用 install plan
  - `ClusterStateSpec` 相当の JSON
  - test-cluster 用 topology

### Phase 1: generic bootstrap を完成させる

1. ISO / netboot を node 固定実装から profile/class ベースへ変更する
2. `deployer` の返却値を `node-config` ではなく `install-plan` に寄せる
3. `install-plan` には最低限以下を含める
   - node id
   - hostname
   - primary IP / network facts
   - disk layout ref
   - flake attr or system profile ref
   - SSH host key / TLS / bootstrap token
4. ISO 側は `nix_profile` を実際に使って install target を決める
5. `disko` 参照も node 固定 path ではなく profile/class から生成する

この段階で、`cloud-init` endpoint は compatibility として残してもよいが、本流ではなくすべき。

### Phase 2: NixOS apply agent を入れる

1. `node-agent` とは別に `nix-agent` を追加する
2. responsibilities:
   - desired generation の取得
   - closure prefetch
   - activation
   - success/failure/report
   - rollback
3. state model を ChainFire に持つ
   - `nodes/<id>/desired-system`
   - `nodes/<id>/observed-system`
   - `deployments/<generation-id>`
4. health gate を入れる
   - reboot required
   - activation timeout
   - rollback on failed health check

これで初めて「Terraform なしで host rollout する基盤」になる。

### Phase 3: native service scheduling を Nix と接続する

1. `ServiceSpec` / `PlacementPolicy` を Nix から生成する
2. `deployer-ctl apply` を human CLI ではなく generator/exporter の受け口にする
3. `fleet-scheduler` は以下だけに責務を限定する
   - node selection
   - rollout budget
   - failover
   - publication trigger
4. `node-agent` は以下だけに責務を限定する
   - process/container reconcile
   - health reporting
   - observed state update

重要:

- ChainFire
- FlareDB
- IAM bootstrap
- Deployer 自体
- Scheduler 自体

のような基盤コアは、最初は scheduler 対象にせず NixOS 固定配置のままにする方がよい。  
いきなり全部 movable service にすると bootstrap が壊れやすい。

### Phase 4: inventory / MaaS 代替を広げる

1. phone-home payload に hardware facts を追加する
   - CPU
   - memory
   - disks
   - NICs
   - virtualization capability
   - serial / DMI
2. enrollment rule を machine-id 依存から広げる
   - MAC
   - DMI
   - hardware traits
   - rack / TOR port metadata
3. node lifecycle を増やす
   - discovered
   - commissioned
   - install-pending
   - installing
   - active
   - draining
   - reprovisioning
   - rescue
4. 余力があれば Redfish/IPMI を追加する
   - power on/off
   - reboot
   - virtual media

ここまで来ると MaaS の最小代替になる。

### Phase 5: validation path を本流化する

1. `nix/test-cluster` に bootstrap end-to-end を追加する
   - generic ISO/netboot
   - deployer enrollment
   - install-plan
   - install
   - first boot
   - nix-agent apply
2. `fleet-scheduler` native runtime flow を CI gate に入れる
3. node loss / reboot / reprovision / rollback の試験を追加する

## 実装優先順位

### P0

- `Nix` を single source of truth に固定
- `nix_profile` を実際の install/apply 経路に接続
- generic install-plan を定義
- node 固定 ISO install path を撤廃
- `nix-agent` の最小版を作る

### P1

- Nix -> `ClusterStateSpec` generator
- `fleet-scheduler` と `node-agent` の Nix-generated service spec 化
- hardware inventory / enrollment 強化
- bootstrap E2E を `nix/test-cluster` に組み込み

### P2

- Redfish/IPMI
- reprovision / rescue
- publication と host rollout の連携
- drain / cordon / maintenance window

## このリポジトリに対する具体的な backlog

1. `nix_profile` を受け取って実際に install target を決める bootstrap API と ISO 処理を作る
2. `nix/nodes/vm-cluster/node01` のような node 固定構成を、class/profile 生成へ寄せる
3. `deployer-ctl` 用の YAML を手書きせず、Nix から JSON export する generator を追加する
4. `first-boot-automation` は使うなら bootstrap API 契約に合わせて作り直す。使わないなら一旦凍結する
5. `node-agent` とは別に `nix-agent` crate と NixOS module を追加する
6. `deployer` に hardware inventory と install state report を足す
7. `nix/test-cluster` に bare-metal-like bootstrap scenario を追加する
8. CI で `deployer/scripts/verify-deployer-bootstrap-e2e.sh` と `verify-fleet-scheduler-e2e.sh` 相当を gate に入れる

## 結論

このプロジェクトで不足しているのは、scheduler 単体ではない。  
足りていないのは次の 1 本の経路である。

`Nix cluster declaration`
-> `bootstrap/install plan generation`
-> `deployer enrollment`
-> `NixOS installation`
-> `node-side NixOS reconcile`
-> `native service scheduling`
-> `runtime health/rollback`

いまは各部品はあるが、1 本の pipeline になっていない。  
最優先でやるべきは、`Nix` を単一正本に固定し、その宣言から

- host install
- host rollout
- native service scheduling

の 3 つを生成・reconcile できるようにすること。
