• Status: Accepted
  • Mode: Standard
  • Kruchten Type: Existence/Executive
  • Scope: platform
  • Implementation Status: Done
  • 起案者: [email protected]
  • 起案日時 (JST): 2026-06-01 12:36
  • 承認日時 (JST): 2026-06-21
  • Deciders: [email protected] (単独)

コンテキスト

§1.1 背景

2026-10 の Jr 入社で ADR 起案者が複数になることを控え、起案者情報の一貫性確保が必要になった。現在は単一共有 Basic 認証 + 自由テキスト入力で、誰が起案したかが手入力依存になっている。

§1.2 現状 (As-Is)

  • 認証は Basic 認証のみ (src/index.ts:22 basicAuth({username: AUTH_USER, password: AUTH_PASSWORD}))。単一共有 credential で per-user の email を持たない
  • 起案者フィールド (public/chat.html:1691 <input id="author">) は自由テキスト + PERSIST_FIELDS による localStorage 永続。
  • worker には認証主体の email を取得する経路が存在しない。
  • ADR frontmatter「起案者」と telemetry author が手入力依存で不統一 (同一人物が「代表取締役」「[email protected]」と混在)。

§1.3 課題

手入力依存により起案者の誤記・属性欠落・なりすましリスクが顕在化。Jr 入社で起案者が複数になると、誰が起案したかが ADR・telemetry で追跡不能になる。

§1.4 制約・要件

  • Cloudflare Zero Trust Free tier (50 users / 1000 tunnels) 内に収める (追加課金 0 円)。
  • CI/headless 経路 (drp-trigger.yml/runs) は人間でない呼び出しのため、認証統合後も動作継続必須。
  • ローカル開発で Access を bypass する手順が必要 (Jr 入社時のオンボーディング前提)。
  • RQ-085 (Basic 認証 → Cloudflare Access service token) の方向性に整合させる。
  • ADR frontmatter は長期保管文書であり、改ざん防止ポリシーと衝突しない author 記録方式を採る。

§1.5 目標 (To-Be)

ログイン中アカウントの email を起案者として自動入力し、ADR frontmatter と telemetry author を per-user で一貫記録する。Non-Goals: Basic 認証の完全廃止 (本 ADR は部分実装、完全廃止は別 ADR)。

決定

chat UI を提供する decision-pipeline worker を Cloudflare Access (Zero Trust) 配下に置き、Access が付与する Cf-Access-Authenticated-User-Email ヘッダ (検証は Cf-Access-Jwt-Assertion の署名検証) を worker が読み取り、/chat ページ配信時に起案者フィールドへ自動入力 (読み取り専用) する。自動入力値を POST /chat/start / /runs の author として送出し、ADR frontmatter「起案者」と telemetry author に一貫記録する。Basic 認証は Cloudflare Access の service token に統合し、CI/headless は service token で認証する。RQ-085 の推奨を部分実装する位置づけ。

JWT 公開鍵は JWKS エンドポイント (https://<team>.cloudflareaccess.com/cdn-cgi/access/certs) から動的取得し、Cache API で TTL 付きキャッシュ + 鍵ローテーション時の自動再取得を実装する。worker でヘッダ値の RFC5322 メール形式バリデーションを必須化し、不正値は /chat 配信を 400 で中断する。

判断基準 (Decision Drivers)

3.1 評価軸

#重要度 (係数)案件特有の解釈
1#secure[Must] (×2.0)起案者識別の偽装耐性 (ヘッダ単独信頼でなく JWT 署名検証)
2#maintainable[High] (×1.0)ADR frontmatter/telemetry の author 一貫性、長期保管耐性
3#operable[High] (×1.0)service token 棚卸し、JWKS ローテーション、ローカル bypass、Access 障害復旧
4#suitable[Medium] (×0.5)Jr 入社 (2026-10) までに per-user 識別が利用可能になるか
5#efficient[Medium] (×0.5)実装人日、追加金銭コスト

K.O. criterion: Must 軸 (#secure) の score < 3 は不採用。

3.2 評価軸 × 案スコア表

各案 0-5 で評価。加重和 = (Σ score × 係数) / (5 × Σ 係数) で正規化。

係数採択案 (A: Access 移行)案 B: Basic 認証 per-user 化案 C: localStorage email 固定
#secure (Must)×2.0521
#maintainable (High)×1.0431
#operable (High)×1.0345
#suitable (Medium)×0.5434
#efficient (Medium)×0.5345
加重和 (正規化)0.8000.5800.460
K.O. 通過 (Must ≥3)❌ (score=2)❌ (score=1)

検討した代替案 (Alternatives Considered)

  • 案 A (採択): Cloudflare Access 移行 + JWT 署名検証 — JWT 検証により偽装耐性を確保。RQ-085 と整合。実装 1.5 人日、金銭 0 円。運用追加 (policy 棚卸し・JWKS 管理) は受容可能。
  • 案 B: Basic 認証 username を per-user 化 — 共有 credential を個人別に分割。実装 0.5 人日と軽量だが、Basic 認証は credential 漏洩時の偽装耐性が弱く #secure K.O. を通過しない。RQ-085 の方向性とも逆行。
  • 案 C: localStorage に email 固定値を保存 — 0.25 人日以下で実装可能だが認証情報と紐づかないため #secure K.O. 不通過。Jr 入社までの暫定措置としても、手動設定漏れで空 author が発生する。撤退時の縮退先として位置づける。

コスト試算 (Cost Estimate)

  • 実装工数: 約 1.5 人日 (採択案 A)。作業内訳: ① worker の JWT 署名検証 + JWKS 動的取得/キャッシュ実装、② CI の service token 移行、③ ローカル bypass 手順 + Runbook 整備。各項目の人日内訳は Phase A 着手時に実績で確定する (現状は総量の概算・実装未着手のため推定)。
  • 金銭コスト: 0 円。Cloudflare Zero Trust Free tier (50 users) 内に収め追加課金なし。
  • 運用追加コスト (継続): service token / policy の四半期棚卸し + JWKS ローテーション監視 + service token 期限 30 日前の Slack アラート構築 (R5)。棚卸しは四半期ごと、アラート構築は初期一度。
  • 代替案比較: 案 B 約 0.5 人日 / 案 C 約 0.25 人日 (いずれも #secure K.O. 不通過のため不採択)。

影響 (Consequences)

§5.1 正の影響 (Good)

  • per-user 識別で「誰が起案したか」が ADR・telemetry で一貫・明確化。Jr 参加後も自動で正しい属性が付く。
  • 手入力 (代表取締役 等) の不統一・誤記を排除。
  • RQ-085「Basic 認証 → Cloudflare Access」を前進させ、認証統合の第一歩になる。

§5.2 負の影響 (Bad)

  • Access 設定の運用追加 (policy / service token の四半期棚卸し、JWKS キャッシュ管理)。
  • CI/headless 経路 (/runs) は service token 認証への移行が必須。未対応だと CI 起動不能。
  • ローカル開発で Access を bypass する手順 (cloudflared access / service token) が要る。Jr オンボーディング前にドキュメント化必須。
  • ADR frontmatter と telemetry に email を恒久記録するため、退職者・改姓・ドメイン変更時の Git 履歴残存問題が発生。改ざん防止ポリシーと「忘れられる権利」要求が衝突する可能性。
  • 起案者フィールドを読み取り専用にすることで、共同起案・代理起案 (上長が部下の代理で起案) のユースケースが技術的に不可能になる。

§5.3 中立・トレードオフ (Neutral / Trade-offs)

  • R1 Access 障害で /chat アクセス不能 → 起案停止。fallback として Basic 認証経路を移行後 4 週間は併存 (Access の bypass ルール + wrangler.toml で経路分離)。Access 全断時は Cloudflare dashboard で policy 無効化 (推定復旧 15 分)。
  • R2 ヘッダ偽装 (Access 非経由で Cf-Access-Authenticated-User-Email 詐称) → worker は Cf-Access-Jwt-Assertion を Access 公開鍵で検証、未検証は 403。ヘッダ単独を信頼しない。
  • R3 service token アクセス (CI) は email を持たない → author に service principal 名を入れ、/runstrigger_source で人間起案と区別。
  • R4 JWT 公開鍵ローテーション → JWKS エンドポイントから動的取得 + Cache API で TTL キャッシュ + ローテーション検知時の自動再取得。公式ライブラリ (@cloudflare/workers-access-authenticator) の採用可否を実装時に評価。
  • R5 service token 有効期限切れ → Cloudflare API で期限監視、30 日前に Slack アラート発火 (CI 組み込み)。token ローテーション手順を Runbook 化、担当者不在時の代替者明示。CI 移行と Access 有効化は独立ステップにし、CI 正常動作確認後に Access 有効化。
  • R6 ヘッダ値の空・不正 → RFC5322 バリデーション必須化、不正時は 400 で中断、localStorage フォールバック禁止。
  • R7 email 識別子の可変性 → ドメイン変更・退職・IdP 移行時に過去 ADR の email と現在の Access email が不整合になる。3〜5 年後の追跡性を維持するため、frontmatter 更新方針を別途決定 (本 ADR では「意図的に変更しない、変更履歴は Access 側で管理」とする)。将来改善候補として JWT の sub claim 等の不変 ID 併用を記載。
  • R8 共同起案ユースケース → 読み取り専用固定は将来の運用ニーズに対する設計制約として明記。発生時は再 ADR で「自動入力 + 編集可 + 変更を audit log 記録」への変更を検討。

撤退条件 (Rollback Plan)

  • 導入後 2 週で、Access 起因の起案アクセス障害が月 3 回以上、または author 自動入力の誤り (空・誤メール) が直近 20 起案で 2 件超 → Basic 認証 + 自由入力に戻す (案 C の固定デフォルトへ縮退)。
  • CI service token 期限切れで CI 起動不能になった場合は即時手動 token 再発行 + Runbook 改善。
  • 判定: [email protected] (プライマリ)。telemetry の author 欠落率・Access エラー率・JWT 検証失敗率で観測。
  • Basic 認証 fallback 撤去日: 移行完了から 4 週後の特定カレンダー日付を実装時に確定し、Runbook に記載。撤去判断権者は [email protected]。撤去完了の確認方法は wrangler.toml から Basic 認証経路の routes 削除 + CI で 401 応答確認。撤去日を延期する場合は再 ADR が必要 (先送りにコストを課す)。

Confirmation

  • 検証手段:
    1. telemetry author フィールドの 非空・RFC5322 email 形式準拠率 100% (worker バリデーション + telemetry 集計クエリ)。
    2. Cf-Access-Jwt-Assertion 未検証リクエストが 403 になる E2E テスト + 正規 JWT (鍵ローテーション後含む) が 200 になる E2E テストを CI 必須化。
    3. ADR frontmatter「起案者」の email 形式準拠率 ≥ 95% (adr-lint で frontmatter author を RFC5322 検証)。
    4. service token 有効期限 30 日前の Slack アラート発火テスト (CI で月次模擬実行)。
    5. ローカル開発手順を別メンバーが Jr 入社前に追試し動作確認 (受け入れ基準)。
    6. 空 author が telemetry に書き込まれた件数の alert 閾値監視 (>0 で即時通知)。
  • 実行頻度:
    • (1)(6) 全 run ごと (リアルタイム)
    • (2) 全 PR の CI
    • (3) 導入後 4 週時点で計測、以降月次
    • (4) 月次
    • (5) 2026-09 末まで (Jr 入社前) に 1 回
  • 違反時対応:
    • (1)(3)(6) 違反 → §6 撤退条件発火、Basic 認証 + 自由入力に縮退検討
    • (2) 違反 → 該当 PR マージブロック
    • (4) 違反 → service token 即時再発行 + Runbook 改善
    • (5) 違反 → 手順書改訂 + 再追試

参照 (References)

  • 関連 ADR: RQ-085 (認証・シークレット統合 / 本 ADR が部分実装)、ADR-0019 / ADR-0066 (decision-pipeline worker アーキ)、ADR-0082 (TelemetryRecord author フィールド)
  • 関連 PR/Issue: -
  • 外部資料: Cloudflare Access JWT Validation (https://<team>.cloudflareaccess.com/cdn-cgi/access/certs)、@cloudflare/workers-access-authenticator (採用可否は実装時評価)