v4 Decision ADR v3 — β採用・vendor実価格TCO版

2026-04-17 | Claude Code | v2(19)を Codex 8回目指摘6件で全面改訂 + vendor 実価格で Decision 再検証

🔄 v2(19) からの主要転換

Decision が γ(Neon+Clerk+Trigger.dev)から β(Supabase)に変更

Codex 8回目レビュー(20)を受けて vendor の data residency と実価格を WebFetch 実調査した結果、 γ の各ベンダ(Neon / Clerk / Trigger.dev)はいずれも Tokyo region 非対応で INV-18 Data residency を構造的に満たせないことが判明。 一方 Supabase は Tokyo (ap-northeast-1) を公式サポート。 TCO も vendor 実価格で再計算したところ γ は β より約 20% 高コスト(v2 で主張した 1.6% 差は誤り)。

📌 本書の位置づけ

8ラウンドの Codex レビューを経て到達した最終決定文書。 子安氏に送るのは本書(21)+ Codex 9回目レビュー(実施予定)の組み合わせ。

1. 実現したいこと(要件)

v2(19) と同一。1.1 ビジネス要件 / 1.2 機能要件 / 1.3 運用要件は 19 §1 を参照。

1.1 変更なしの要件サマリ

2. Invariants(v2 から変更なし、20項目全て MUST)

INV-1〜20 の内容は 19 §2 と同一。

3. 候補案 Bill of Materials(β 採用版)

v2 からの変更: γ を Rejected に移動(INV-18 不可)。β(Supabase)を採用、詳細を拡充。α は引き続き Hard Gate 失敗で Reject。

3.1 採用案 β — Supabase + Cloudflare Workers hybrid

レイヤー製品 / 技術備考
FrontendCloudflare Pages + React 19 + Vite既存 v2/v3 の Pages 資産を流用
API GatewayCloudflare Workers + Hono既存コード構造を流用
業務ロジック実行Supabase Edge Functions(Deno)or CF WorkersDB 近接処理は Edge Functions、残りは Workers
AuthSupabase AuthEmail/Password + MFA + SSO、JWT 発行・検証は Supabase 側
一貫性境界Postgres 14+ advisory lock + トランザクションoutbox / audit chain / rate limit counter 全部 Postgres
非同期ジョブpg-boss(Postgres 上の job queue)Trigger.dev と違い DB 同居で region 制約なし
Cronpg_cron(Postgres 拡張)同上
MediaCloudflare R2 + client presigned URL既存資産継続
DBSupabase Postgres (Medium compute)Tokyo region (ap-northeast-1)、PITR $100/mo add-on 購入
AbuseCloudflare WAF + Turnstile既存継続
観測Supabase Logs + Sentry + Better StackBetter Stack 2 responder(24/7 SMS 込み)
SecretsSupabase Vault (pgsodium) + Cloudflare Workers SecretsIG token は Supabase Vault、infra secret は CF Workers Secrets
BackupPITR 7日(Pro add-on $100/mo)+ 日次バックアップ標準INV-12 を native で満たす

3.2 Rejected 案 γ — Neon + Clerk + Trigger.dev

❌ 採用見送りの根拠(Codex 8回目指摘を web 実調査で確認)
  1. Neon: Tokyo region 非対応(公式 docs で確認)。最寄り Singapore / Sydney。日本国内 PII の越境リスク → INV-18 失敗
  2. Clerk: Data Residency 未提供(公式 pricing で確認「Data Residency: not offered」)→ INV-18 失敗
  3. Trigger.dev Cloud: run region は code 実行のみ、payload/log/output は別 region で保存(公式 docs で確認)→ INV-18 失敗
  4. Clerk Organizations の MRO 課金が 300店舗規模で膨張(100 MRO 超過、$1/月×200 = 追加 $200/月)
  5. Neon Scale は CU-hour 課金で典型的な中規模は $160〜$700/月。v2 で $19 と書いたのは完全な誤り
参照元:
- https://neon.com/pricing
- https://neon.com/docs/introduction/regions(Tokyo 記載なし)
- https://clerk.com/pricing(Data Residency "not offered")
- https://trigger.dev/docs/triggering(run region は実行のみ)

3.3 Rejected 案 α — Cloudflare all-in

Codex 7/8回目 と同じく Hard Gate 失敗で Reject:

4. スコアリング再評価(β 採用版)

v2 からの変更: γ を追加 Rejected とし、β の詳細を再評価。Codex 8回目の INV-16 / INV-18 指摘を反映。

4.1 Rubric(v2 から変更なし)

5=Native / 4=Sound / 3=Achievable / 2=Difficult / 1=Blocked(Hard Gate) / ?=Unknown

4.2 Hard Gate(INV-3 / 8 / 12 / 13)

4.3 INV-18 を Hard Gate に昇格

Codex 8回目指摘 #3 対応で INV-18 を pre-Q1 blocker に。評価は以下:

INV-18 Data residencyαβγ
CF Workers Tokyo555
DB Tokyo region5 (D1 edge)5 (Supabase Tokyo 公式)1 (Neon Tokyo なし)
Auth Tokyo residency5 (自前実装)5 (Supabase Tokyo)1 (Clerk 未提供)
Queue Tokyo residency5 (CF Queues)5 (pg-boss DB同居)1 (Trigger.dev payload 越境)
総合551 (Hard Gate Fail)

4.4 β の Weighted Score(Hard Gate 通過後 16項目)

INV-1〜17, 19, 20(INV-18 は Hard Gate)でスコア合計:

5. 3年 TCO(vendor 実価格版)

v2 からの変更: Codex 8回目指摘 #2 対応で vendor 実価格(WebFetch 実調査)で全面再計算。flat price 主張を撤回、usage-based で再試算。

5.1 Vendor 実価格(2026年4月時点)

Vendor実価格(調査結果)
Supabase Pro$25/mo 基本 + Micro compute 込み($10 credit)
Supabase Compute add-onSmall $15 / Medium $60 / Large $110(Pro plan は Micro 無料)
Supabase PITR$100/mo add-on(INV-12 のため必須)
Supabase Disk8GB 込み、超過 $0.125/GB
Cloudflare Workers Paid$5/mo(10M req 込み)
Cloudflare R2$0.015/GB/mo + $4.50/M Class A
Cloudflare WAF Rate Limiting$20/mo(実質必須)
Cloudflare TurnstileFree(本番ほぼ無料)
Sentry Team$26/mo
Better Stack 2 responders$68/mo(SMS 無制限含む)
Claude API (Haiku 4.5)usage-based、$0.25/$1.25 per MTok

実調査ソース: supabase.com/pricing, developers.cloudflare.com/workers/platform/pricing, betterstack.com/pricing, sentry.io/pricing

5.2 月額コスト(100店舗・現実シナリオ)

項目30店舗100店舗300店舗備考
Supabase Pro 基本$25$25$25Pro plan subscription
Supabase ComputeMicro込み ($0)Small $15Medium $60- $10 credit
Supabase PITR add-on$100$100$100INV-12 Hard Gate
Supabase Disk (超過分)$0$2$128GB 込み、超過 $0.125/GB
Supabase Egress$0$0$9250GB 込み、超過 $0.09/GB
CF Workers Paid$5$5$1010M req 超過分
CF R2$2$5$15画像ストレージ
CF WAF$20$20$20
Sentry Team$26$26$26
Better Stack 2 responders$68$68$6824/7 on-call + SMS
Claude API$15$40$120usage
Staging/Dev$25 (Free + Small)$30 (Free + Small)$50 (Pro + Small)
月額合計$286$336$515
店舗あたり単価$9.5$3.4$1.7
売上比¥42,900 / ¥600,000 = 7.2%¥50,400 / ¥3.5M = 1.4%¥77,250 / ¥6M = 1.3%Infra 比率

5.3 3年 TCO(100店舗ベース)

項目案β 合計備考
Infra 3年 ($336 × 36)$12,096 ≈ ¥1.81Musage-based 再計算済み
実装工数(180h × ¥8,000)¥1.44M子安氏稼働単価
運用工数(3年 10h/月 × ¥8,000)¥2.88M24/7 escalation 込み
Incident 対応(年1回想定)¥0.6MPITR + runbook 整備済み前提
3年 TCO 合計約 ¥6.7M
売上 3年(¥3.5M × 36)¥126M100店舗想定
粗利率94.7%

5.4 感度分析

シナリオ年次コスト売上比
30店舗で停滞(¥600k/mo)¥3.43M47.6%(赤字リスク)
100店舗達成(¥3.5M/mo)¥4.03M9.6%(健全)
300店舗拡大(¥6M/mo)¥6.18M8.6%(健全)

重要: 30店舗停滞時は運用工数 + Better Stack/Sentry 固定費が重い。 30店舗到達まで Better Stack を 1 responder にして $34/月に抑えるSentry は Developer plan $26 を Hobby (Free) から始める選択肢を追加。

6. Decision(v3 最終)

✅ 採用アーキテクチャ(v3 確定)

案β — Supabase + Cloudflare Workers hybrid

Supabase (Tokyo region) + Postgres 14 + pgsodium Vault + pg-boss + pg_cron
+ Cloudflare Pages + Workers + R2 + WAF + Turnstile
+ Sentry + Better Stack (24/7 responders)

Hard Gate 通過(INV-3/8/12/13/18)/Weighted 66/75 / 3年 TCO ¥6.7M / 粗利率 94.7%

6.1 β を γ より選ぶ決定的理由

  1. INV-18 Data Residency が Hard Gate: γ は Neon/Clerk/Trigger.dev とも Tokyo 非対応で日本 PII 越境。β は Supabase Tokyo region 公式サポートで完全クリア
  2. 単一ベンダ集中による composite availability 向上: Supabase 99.9% + CF 99.99% ≒ 99.89%(月 72分 downtime、γ の 113分より優位)
  3. Vendor 管理の簡潔さ: 4ベンダ(Supabase, CF, Sentry, Better Stack) vs γ 6ベンダ
  4. 実価格 TCO でも劣位なし: Supabase PITR add-on $100/月で γ と同等、Neon Scale の $160〜700 より安定
  5. pgsodium Vault で INV-13 Secrets lifecycle が native 実装: γ で要求した envelope encryption のカスタム実装が不要

6.2 β の弱点と mitigation

弱点Mitigation
Supabase 単一障害点Better Stack monitoring + Supabase status 購読 + 非常時 read-only mode(Runbook R4)
Edge Functions の cold start業務ロジックは CF Workers 側、DB 近接処理のみ Edge Functions、pgbouncer で connection pool
Supabase Auth の機能が Clerk より弱い(MFA は Pro 含、SSO は Team $599 から)SSO は Phase 2 まで不要、MFA は Pro 込み

7. MEK / Secrets 設計(β 版、pgsodium ベース)

v2 からの変更: Codex 8回目指摘 #1 (critical) 対応。γ 専用 envelope encryption を削除し、β 採用でpgsodium nativeに再設計。

7.1 Supabase Vault (pgsodium) の活用

Supabase は Vault(pgsodium 拡張ベース)を native に提供。secret の暗号化保管と復号ヘルパが SQL から使える。

-- IG token を暗号化保管
SELECT vault.create_secret(
  'ig_access_token_plaintext_here',
  'ig_token_store_001',
  'Instagram access token for store_001'
);

-- 復号
SELECT decrypted_secret FROM vault.decrypted_secrets WHERE name = 'ig_token_store_001';

7.2 Key Rotation (pgsodium)

pgsodium は master key rotation を自動管理。Supabase 側で encryption key の世代管理を 内部で行うため、アプリ側は rotation job を持たなくて良い。 Codex 8回目指摘 critical(MEK rotation と PITR の矛盾)は β 採用で自動解決

7.3 Revocation / Crypto-shredding

7.4 PITR との整合

Supabase PITR は Vault data を含めて過去時点に restore 可能。 master key は Supabase が管理するので、restore 時にも自動的に対応する master key で復号できる。 v2/v3 の γ 設計で問題になった「MEK 削除で PITR restore 後に復号不能」という矛盾は発生しない。

7.5 Audit

以下を audit_logs に記録:

8. Evidence Failure Matrix(v3 修正版)

v2 からの変更: Codex 8回目指摘 #4 対応。test mechanics を具体化。

8.1 E1 Authz matrix

v2 (19 §10.1) と同じ。Supabase Auth + RLS policies の test。

8.2 E2 Publish idempotency(crash injection mechanics 追加)

Crash timingInjection 方法Expected recovery
Before IG media_createCF Workers throw feature flag で強制例外pg-boss が retry、outbox からキューに再登録
After media_create, before creation_id savedIG mock で 200 返却後、throw で DB commit 前停止reconciliation cron が orphan check、creation_id 取得して再開
After creation_id saved, before media_publishpg-boss job が creation_id を保存後に kill(signal 経由)次の cron で保存済み creation_id を読んで publish 実行
After media_publish, before DB commitSupabase tx 中に pg_cancel_backend で強制切断reconciliation: IG から media_id 取得、posts 更新
After DB commit, before pg-boss ackpg-boss 完了前に job worker を killpg-boss retry、status='published' を見て skip(idempotent)
During reconciliationreconciliation cron を中断次の cron で再開、orphan-free 保証

8.3 E3 Audit append(100並行)

Test mechanics: k6 or autocannon で 100 並行リクエスト、各 endpoint が audit_logs.append を呼ぶ。 Postgres advisory lock (pg_advisory_xact_lock) + seq INSERT 原子的。 Pass: 全 100行が prev_hash で連結、seq 連番、fork ゼロ。

8.4 E4 Retry storm mechanics

ScenarioInjectionExpected
IG API 500 (30 posts backlog)wiremock で IG 相当の endpoint を返す、500 応答outage 解除後 5分で全完了
IG API 429 rate limit (100 posts)wiremock で Retry-After header 付き 429exponential backoff で 20分以内完了
Supabase DB 障害(mock)Supabase Edge Functions 側で意図的に DB connection 切断pg-boss job 失敗 → CF Workers 側で degraded mode、復旧後 queue から再実行

8.5 E5 Migration dry-run mechanics

DatasetMechanicsPass
v2 現本番 10店舗D1 export → Postgres import script、dual-read で 検証1店舗 5分以内、posts 件数・内容 diff ゼロ
合成 100店舗faker で合成、大規模一括 import全件 30分以内、サンプル 100件の内容一致
移行中 v2→v4 dual-writeCF Workers 経由で v2/v4 両方に投稿 insert、cron で diff 検出最大 10秒遅延で同期
Rollback: v4→v2Supabase Branch で v4 state を保存 → v2 D1 へ restore script1店舗 10分以内、データ喪失ゼロ

8.6 E6〜E9(v2 と同じ、INV-18 を β では pass 前提)

E9 Data residency: β 採用により Supabase Tokyo + CF Tokyo + R2 で全ベンダ JP region 対応。PII が region を出ない設計を確認。

9. Operating Model(v2 と同じ、24/7 採用)

v2 (19 §12) と同じ。子安氏への質問 Q6 として on-call 受諾 + compensation 確認を追加(§11 参照)

9.1 8つの Runbook(P1 優先 6本をドラフト作成対象に)

  1. R1 予約投稿一括失敗(P1)
  2. R2 Instagram outage 中の顧客コミュニケーション(P1)
  3. R3 Supabase Auth 障害時の degraded login(P1)
  4. R4 Supabase DB 障害時の read-only mode(P1)
  5. R5 pg-boss 不整合時の手動 cron(P2)
  6. R6 Audit chain fork 検知時 forensics(P1)
  7. R7 Secret 漏洩時の緊急 rotation(P1)
  8. R8 店舗単位 v4→v2 rollback(P2)

10. Delivery Plan(v2 から軽微調整)

Phase 1 MVP 12週間は v2 と同じ。ただし Week 3-4 の Auth 実装は Clerk → Supabase Auth に変更のため工数は同等。

11. 子安氏への依頼事項(Q1-Q6)

Q1-Q6 + blocking concerns 自由記述
#質問回答形式
Q1Decision(6章)の案β(Supabase + Cloudflare hybrid)で進めて良いかYes / No
Q2Invariants 20項目(v2 19§2)に追加・削除・優先度変更はあるか追加リスト or 「なし」
Q3Evidence(§8)E1〜E9 の test mechanics に修正はあるか修正リスト or 「なし」
Q4Delivery Plan 12週間で実装可能か(Supabase 経験前提で)Yes / No(No なら現実工期)
Q5Blocking concerns 自由記述自由記述
Q624/7 on-call 受諾可否 + compensation / escalation 合意
  • 平日 19時〜翌9時 primary → Yes / No
  • 週末 rotation → Yes / No
  • on-call 手当(応答1回 ¥X, etc)提案

11.1 進め方

12. 付録:関連ドキュメント