Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
10 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions issues/PLAN56_secret-group-paths-decisions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# #182: 機密ストアの置き場をアカウントグループごとに分ける(決定の記録)

設計文書 `issues/PLAN56_secret-group-paths-design.md` の「決定の記録」の節である。設計文書が
500 行を超えたため、この節だけを分けた。本文中の「決定 N」はこのファイルの見出しを指す。

## 決定の記録

### 決定 1: グループ別の置き場は `backend.yml` の `version: 2` にする

古い devbase は未知のキーを黙って無視する(`_openbao_from_dict`)。キーを足すだけでは、
配布が行き渡る前の端末が `version: 2` 相当の設定を読んで `team/global` を読み続け、別グループの
機密をコンテナへ渡す。版を上げれば、今の「`version` が 1 以外なら拒む」規則でその端末が止まる。

パスの設定に `{group}` の差し込みを許す案は採らない。個人単位のパスは `<prefix>/<user>/…` を
コードが組んでおり、差し込みの位置を設定で表すにはキーを増やすことになる。古い devbase が
`{group}` を文字どおりのパスとして読む問題も残る。

### 決定 2: グループは `SecretRef` のフィールドとして持つ

参照が自分のグループを持てば、`_seen` の鍵・キャッシュの位置・`label()` がすべて参照から
決まる。`up web` を `api` から打ったときのように、1 つの `SecretStore` の中でグループが
変わっても取り違えない。

グループを `SecretStore` のインスタンスに持たせる案は採らない。PLAN55 でストアはライフサイクル
操作 1 回の間持ち回られ、プロジェクトの切替をまたぐ。インスタンスのグループを書き換えると、
控えの鍵がグループを含まず、切替元の値が返る。

### 決定 3: グループは非機密の `env` ファイルだけから決め、プロセスの環境変数を見ない

ラッパーは実行時のディレクトリの `env` だけを読む。プロジェクトの下位ディレクトリから
打つと、プロジェクトの `env` がプロセスに載らない。`projects/<name>/env` を直接読めば、
下位ディレクトリからでも同じグループになる(受け入れ条件 3)。置き場から読んだ値も
使わないため、置き場を決める値をその置き場から読む循環も起きない(受け入れ条件 4)。

グループを宣言していないプロジェクト(`$DEVBASE_ROOT/env` にも宣言なし)でシェルから
`DEVBASE_ACCOUNT_GROUP=kkg devbase up` と打つと、ラッパーが source する `env` に同じキーが無い
ため環境変数が残り、ボリュームは `kkg`、機密は `default` になり食い違う。プロジェクトの `env` が
宣言していれば、ラッパーの source が環境変数を上書きするので食い違わない。この食い違いは
決定 7 で起動を止めて知らせる。

### 決定 4: `default` の読み替えは `group_aliases` で置き場の上だけ行う

`DEVBASE_ACCOUNT_GROUP` の既定値を `nyle` に変えると、ボリューム名が `devbase_home_default`
から変わり、既存の認証と会話ログのボリュームを移すことになる。devbase は公開リポジトリで、
コードに社名を既定値として持ち込むことにもなる。読み替えを端末の設定に置けば、ボリュームに
触らず、社名はその会社の端末の設定にだけ入る。

### 決定 5: `version: 1` とファイル backend では参照のグループを常に空にする

`ref_group()` が `None` を返せば、参照は今と同じ値になる。既存のテストの期待値・キャッシュの
位置・往復の数(受け入れ条件 9・10)が、分岐を足さずにそのまま保たれる。

グループを常に参照へ入れ、`version: 1` のパスの組み立てで無視する案は採らない。
`_seen` の鍵にグループが入り、グループの違うプロジェクトへ切り替えたときに同じパスを
2 度取りに行く。

### 決定 6: `-p` と違うグループの `--group` は拒む

`team/kkg/projects/web` に書いても、`web` のグループが `with` なら `up` はそこを読まない。
書けたように見えて使われない機密が残る。プロジェクトのグループを変えたいなら
`projects/web/env` を直すのが筋で、それを文言で案内する。

### 決定 7: `layout: group` の `up` と `scale` はボリュームと機密のグループの食い違いで止める

ボリュームは `resolve_account_group()`(プロセスの環境変数)、機密は `declared_group()`
(ファイル)で決まり、経路が 2 つある。食い違ったまま起動すると、`with` のボリュームの認証で
`nyle` の機密を使うコンテナができ、この変更で防ぎたい混ざり方がそのまま起きる。

ボリュームの側を `declared_group()` へ揃える案は採らない。スナップショット・`status`・
entrypoint へ渡す値まで経路が変わり、この変更の範囲(前提 2)を超える。`version: 1` では
検査しない(今の起動を止めない)。`scale` は `_run_deploy_pipeline` を通らずにコンテナを
足すため、検査を共通の関数にして両方から呼ぶ。

### 決定 8: `env backend test` は対象のグループに属するプロジェクトだけを調べる

今の `test` は `projects/` の全プロジェクトの参照を取りに行く。グループ単位のポリシーの
サーバでは、別グループのプロジェクトで 403 になり、正しい設定でも失敗に見える。対象の
グループ(実行時のプロジェクト、無ければ `$DEVBASE_ROOT/env`)に属するプロジェクトに絞る。

### 決定 9: 置き場を移し直すコマンドは作らない

移し直しはグループ別の置き場へ切り替える端末ごとに 1 回で、チームのパスは 1 人が移せば
済む。`version: 1` の設定で `env get` / `bao kv get` で読み、`version: 2` の設定で
`env edit --group` で書けば足りる。版の履歴を消す操作(`kv metadata delete`)は権限が
管理者側にあり(carmo-cdk#340)、devbase のコマンドに入れても利用者の端末からは実行できない。

### 決定 10: `_ensure_env_files` は子プロセスの `env init` へ `--group` を渡す

子プロセスは `cwd=$DEVBASE_ROOT` で起動する(`commands/container.py` の `_ensure_env_files`)。
実行時のプロジェクトが無いため、共通の参照は `$DEVBASE_ROOT/env` のグループになる。`with` の
プロジェクトの `up` で `team/with/global` が空だと、子プロセスは `team/nyle/global` へ書き、
親は読み直しても空のまま起動する。`--group` でプロジェクトのグループを渡せば、書く先と読む先が
揃う。

子プロセスの `cwd` をプロジェクトのディレクトリへ変える案は採らない。`env init` の収集器が
`cwd` に依存しないことを確かめる範囲が広がり、`version: 1` の端末の挙動まで変わりうる。
`layout: group` でないときは `--group` を渡さない。

### 決定 11: 名前を指定したライフサイクル操作は、dispatch 前の注入から切替先で解決する

`cli._load_secret_env` は dispatch の前に実行時のディレクトリのプロジェクトで注入する。
`projects/api` から Python を直接起動した `up web`(TUI や `python -m devbase.cli`)では、
切替元 `api` のグループのパスへ要求し、別グループの機密をいったんホストのプロセスへ載せる。
`layout: group` で指定した名前が `projects/` に実在するときは、その名前で解決する。

`version: 1` には広げない。PLAN55 の往復の表とテストの期待値(受け入れ条件 9)が変わる。
`version: 1` ではパスがグループで分かれず、取得するのは同じチームの置き場である。

### 決定 12: `export` は対象のグループのプロジェクトだけを集め、`import` は別グループのプロジェクトで止める

今の `export` は既定で全プロジェクト、`import` はバンドル内の全プロジェクトを扱う。
グループ単位のポリシーのサーバでは、別グループのパスで 403 になる。`export` は
`declared_group` が対象のグループと同じ置き場のプロジェクトだけを集め、外したプロジェクト名を
標準エラーへ出す。

`import` で別グループのプロジェクトを黙って飛ばすと、取り込んだつもりの機密が欠ける。
名前とグループを挙げて 1 件も取り込まずに 1 で終了し、既存の `--exclude-project NAME`(繰り返し可)での
除外を案内する。

### 決定 13: `env sync` の同期済みハッシュは置き場のグループごとに持つ

`SourcesManager` は `$DEVBASE_ROOT/.env.sources.yml` 1 つに、ソースファイルのハッシュを記録する。
`env sync` の宛先だけをグループ別にすると、グループ A で同期した時点でハッシュが更新され、
グループ B の同期は「変更なし」と判定される。B の置き場には古い認証情報が残る。
`layout: group` では控えのファイル名に置き場のグループ名を入れ、`env sync` / `export` / `import` の
`--merge-metadata` が対象のグループの控えを読み書きする。控えは認証情報のソースの位置とハッシュを
持つため、今の `.env.sources.yml` と同じく Git の追跡から外す。`.gitignore` を `.env.sources*.yml` へ
広げ、`env doctor` の除外の点検に加える。

控えを 1 つのファイルの中でグループごとの節に分ける案は採らない。`version: 1` の端末と
同じファイルの形が変わり、古い devbase が読むと節を知らずに全体を書き戻す。
Loading
Loading