版数: 2.1  |  更新日: 2026-06-15  |  層: ② 設計

設計資料

この文書について(標準設計書の構成)

目的システムがどう動くかを、実装・レビュー・運用の共通理解として固定する
想定読者開発者、インフラ担当、レビュア、山梨大連携担当
いつ読むか機能追加前、セキュリティレビュー、障害の影響調査時
標準の章本資料
基本設計(What / 全体像)§1 基本設計
詳細設計(How / コンポーネント)§2 詳細設計
非機能・セキュリティ§3 セキュリティ設計
データ・処理の流れ§4 データフロー§5 シーケンス
機能一覧§6 機能・関数定義

§1 基本設計

1. 目的

競馬向け馬体診断アプリケーションにおいて、フィードバック収集・AI 再学習・ アプリ再ビルド・端末配布までを含む継続運用可能なインフラを構築・検証する。 アプリケーションの診断精度そのものは本プロジェクトの検証対象外とする。

2. システム構成(論理)

領域コンポーネント役割
クライアント モバイルアプリ(スタブ) 撮影(または画像選択)、端末内推論、FB 送信
エッジ Cloudflare Edge + cloudflared 内向き FB 通信を Edge で受け、Tunnel 経由で自宅 PC の FastAPI へ中継(CGNAT 回避)。PoC の poc-api-tunnelapi.dammy-otoko.com)を再利用
サーバー(自宅 PC) FastAPI / DB / Storage / AI Worker FB 保存、再学習、新モデル生成、Webhook 送信。ホストは Windows 11 PC(2026-06-07 〜)。開発経緯は 開発ログ
CI/CD GitHub Actions / Fastlane(②のみ) モデル同梱アプリの自動ビルド・配布
配布 APK 直配布(①)/ TestFlight(②) 管理下端末へ更新を届ける

3. 検証スコープ

区分対象対象外
インフラ FB 受信、再学習トリガー、GHA ビルド、配布、Webhook 連携
アプリ スタブによる通信・ビルド同梱の確認 診断精度、UI/UX の本番品質
AI モデルファイルの更新・同梱フロー 学習アルゴリズムの精度検証

4. 段階的検証方針

  1. Phase ① Android: プラットフォーム非依存のパイプラインを確立(Ubuntu ランナー、TFLite、APK 配布)
  2. Phase ② iPhone: ①の手順・構成を踏襲し、macOS ランナー・CoreML・TestFlight を追加

5. 非機能要件

項目要件
運用性手順書(HTML runbook)に従い、第三者が再現可能であること
コスト①では Apple Developer Program 不要。GHA 無料枠内での運用を目指す
セキュリティFB データに個人情報・位置情報を含めない。Tunnel 経由の HTTPS 通信。前提知識は K-24K-28、設計は セキュリティ設計
更新頻度月次 1 回のモデル更新・配布を想定

6. 技術スタック(検証時)

レイヤー① Android 検証② iPhone 検証
クライアントFlutter(スタブ)Flutter または Swift(既存資産に合わせる)
オンデバイス MLTensorFlow LiteCoreML
APIFastAPIFastAPI(共通)
DBPostgreSQL または SQLite同上
CI/CDGitHub Actions(ubuntu-latest)GitHub Actions(macos-latest)
配布APK artifact + adbTestFlight

§2 詳細設計

本資料は検証実装に伴い更新する。未確定項目は「TBD」と記載する。

1. コンポーネント一覧

IDコンポーネント配置実装(予定)
C-01スタブモバイルアプリクライアントFlutter
C-02Cloudflare Tunnel自宅 PC(Docker)cloudflared(PoC の poc-api-tunnel を再利用。公開 URL: https://api.dammy-otoko.com
C-03Web API自宅 PC(Docker)FastAPI + Uvicorn
C-04データベース自宅 PC(Docker)PostgreSQL または SQLite
C-05ファイルストレージ自宅 PCローカルディレクトリ
C-06AI Worker自宅 PC(Docker)Python(スタブ)
C-07CI/CDGitHubGitHub Actions

2. FB 受信 API(C-03)

項目内容
エンドポイントPOST /api/v1/feedbackGET /api/v1/health
Content-Typemultipart/form-data
リクエスト画像ファイル、推論結果(JSON)、タイムスタンプ
レスポンス201 Created + フィードバック ID
処理画像を Storage へ保存、メタデータを DB へ記録

3. AI Worker(C-06)スタブ仕様

項目内容
トリガー手動実行、または cron / Webhook(検証時は手動可)
入力DB / Storage 内の FB データ(スタブでは参照のみでも可)
出力新バージョンのモデルファイル(TFLite / CoreML)
完了時処理GitHub repository_dispatch を送信

4. GitHub Actions ワークフロー(C-07)

4.1 android-build.yml(①)

項目内容
トリガーworkflow_dispatchrepository_dispatch(type: model-updated)
ランナーubuntu-latest
主な処理Flutter セットアップ → モデル同梱ビルド → APK artifact 出力
成果物app-release.apk

4.2 ios-build.yml(②・未実装)

項目内容
トリガーrepository_dispatch(type: model-updated)
ランナーmacos-latest
主な処理署名 → CoreML 同梱ビルド → Fastlane で TestFlight upload
前提Apple Developer Program、Secrets(証明書・API キー)

5. Webhook 連携

項目内容
送信元AI Worker(C-06)
送信先GitHub REST API: POST /repos/{owner}/{repo}/dispatches
event_typemodel-updated
client_payloadmodel_version, model_path 等(TBD)
認証GitHub Personal Access Token または GitHub App(Secrets 管理)

6. ディレクトリ構成(予定)

github-actions-test/
        ├── .github/workflows/     # GHA ワークフロー
        ├── docs/                  # 本資料(HTML)
        ├── app/                   # Flutter スタブアプリ(TBD)
        ├── server/                # FastAPI + Worker スタブ(TBD)
        └── models/                # モデルファイル配置(TBD)

§3 セキュリティ設計

本番導入に向けたセキュリティ方針・脅威・対策の設計ドラフトです。 基本設計 の非機能要件「HTTPS・個人情報を含めない」は方針レベルのみであり、 本資料で資産・攻撃面・対策の具体化を行います。

読む前に: 前提知識(OSI・HTTPS・FW・シークレット等)は Phase1 ナレッジ K-24K-29 に会話ベースで図解付き整理済み。 本資料は設計の正本(SEC 一覧・チェックリスト)。
実装状況(2026-06-14): SEC-01〜03 は server/api に実装済み。 SEC-04 は起動時警告 + server/env.exampleFEEDBACK_API_KEY 未設定時は移行用に無認証可(ログに WARNING)。 運用検証(2026-06-14): server/.env にキー設定後、Tunnel 経由 test-feedback.ps1 で Bearer 付き POST が HTTP 201(K-30)。
スコープ: 役場サーバー、Cloudflare Tunnel、FB 受信 API、Storage、PostgreSQL、 GitHub Actions / PAT、山梨大 Worker 連携。 アプリ内の推論ロジック・学習データの機密性評価は山梨大側の責務とし、インフラ境界を中心に扱います。

1. 設計原則

原則内容
最小公開インターネットから到達できるのは Tunnel 経由の API のみ。DB・管理ポートは非公開
秘密の分離トークン・DB パスワードは .env / GitHub Secrets。リポジトリに含めない
多層防御HTTPS(Cloudflare)+ API 認証(本番で追加)+ ホスト保護 + 運用監視
実用優先小規模運用に過剰な仕組みは避け、ゴーライブ前の必須対策と後追いを分ける

2. 資産一覧

ID資産機密性保管場所
AST-01FB 画像(馬体写真)data/storage/
AST-02推論結果 JSON低〜中PostgreSQL feedbacks
AST-03DB 全データDocker volume pgdata
AST-04TUNNEL_TOKENserver/.env
AST-05GitHub PAT(Worker dispatch)worker/.env
AST-06PostgreSQL 認証情報server/.env
AST-07APK / モデル世代GHA artifact・data/apk-archive/
AST-08Apple 署名鍵(将来)GitHub Secrets / Match

3. 現状の攻撃面とギャップ

現状(検証環境)リスク本番での対策(案)優先
通信(Transit) Cloudflare Tunnel 経由 HTTPS 低(平文露出なし) 本番ドメイン移管後も Tunnel 維持。証明書は Cloudflare 管理
API 認証 SEC-01 実装済みFEEDBACK_API_KEY + Bearer)。未設定時は移行用に無認証可 高(URL 漏洩で不正投稿・容量枯渇)→ 低減 クライアント APK 同梱・ローテーション(実装済み 2026-06-14) 必須
アップロード制限 画像 MIME のみ検証。サイズ上限なし 中(巨大ファイル・DoS) 最大サイズ(例: 10MB)・レート制限(SEC-02) 必須
ネットワーク境界 API 8000 をホストに bind 中(LAN からの直接アクセス) localhost bind のみ、または FW で 8000 遮断(SEC-03)
DB 露出 Docker 内部のみ(ポート未公開) 本番も外部非公開を維持。デフォルトパスワード変更(SEC-04) 必須
保存時暗号化 平文ファイル・DB 低〜中(物理盗難・ディスク流出) Windows BitLocker + バックアップ先のアクセス制御(SEC-05)
シークレット管理 .env 手動・gitignore 中(誤コミット・平文保管) 本番パスワード生成・一覧表・ローテーション手順(3.1.2) 必須
Cloudflare Access 未使用 管理用エンドポイント追加時に Zero Trust ポリシー検討(TBD)
CI/CD PAT を Worker ローカル保持 中(漏洩でリポジトリ操作可) 最小スコープ PAT・期限・四半期ローテーション(K-23)
ログ・個人情報 方針のみ(PII 禁止) 中(アプリ実装依存) FB スキーマに位置情報・氏名フィールドを持たない設計を維持

4. 脅威モデル(簡易)

        flowchart LR
          subgraph internet [インターネット]
            Attacker[不正利用者]
            App[本番アプリ]
          end
          subgraph cf [Cloudflare]
            TLS[HTTPS終端]
            Tunnel[Named Tunnel]
          end
          subgraph host [役場PC]
            API[FastAPI :8000]
            DB[(PostgreSQL)]
            Store[Storage]
            PAT[worker/.env PAT]
          end
          subgraph gh [GitHub]
            GHA[Actions]
          end
          App -->|HTTPS FB POST| TLS --> Tunnel --> API
          Attacker -.->|URL知得時 無認証POST可| TLS
          API --> DB
          API --> Store
          PAT -->|dispatch| GHA
                

最大のギャップ: Tunnel は「通信路の保護」であり、「利用者の認証」ではない。 現状、api.dammy-otoko.com/api/v1/feedback の URL を知れば curl から投稿可能です(検証で意図的に開放)。

5. 対策一覧(SEC-xx)

ID対策内容実装タイミング成果物
SEC-01 API 認証 Authorization: Bearer <API_KEY> または X-API-Key ヘッダ。キーはアプリに埋め込み、サーバー .env で検証 MS-6 前(必須) API ミドルウェア・FEEDBACK_API_KEY
SEC-02 アップロード制限 最大ファイルサイズ、Content-Type 検証強化。必要なら IP / レート制限(Cloudflare Rate Limiting) MS-6 前(必須) FastAPI 設定・Cloudflare ルール(任意)
SEC-03 ポート露出最小化 API を 127.0.0.1:8000 のみに bind。Tunnel は localhost へ接続 MS-6 前(推奨) docker-compose 変更
SEC-04 DB 強化認証 本番用ランダムパスワード。デフォルト change_me 禁止 3.1.2 と同時 server/.env 本番値
SEC-05 ホスト保護 Windows Update、BitLocker(可能なら)、物理アクセス制限、自動ログイン回避 MS-6 前 役場チェックリスト
SEC-06 シークレットローテーション PAT・API キー・Tunnel 再発行手順。漏洩時の初動 3.1.2 / 3.6 運用 runbook 追記
SEC-07 依存関係 Docker イメージ・Python パッケージの定期更新方針 MS-7 後(定例) 四半期レビュー
SEC-08 iOS 署名 証明書・Provisioning を Secrets / Match で管理。平文リポジトリ禁止 WBS 2.3 ios-build 手順

6. 層別設計

6-1. 公開 URL の意味(よくある誤解)

https://api.dammy-otoko.com/api/v1/feedbackサーバー PC が「設定した URL」ではありません。 DNS で Cloudflare を向き、Cloudflare が Tunnel 経由で PC 内の API に転送するインターネット上の入口です。

  • 端末は Cloudflare のエッジに HTTPS で届ける(直接 PC の IP には行かない)— ご認識どおり
  • しかし curl も同じ URL を叩ける — Cloudflare は「正規アプリか」を見ない
  • 危険なのは PC IP の漏洩より、この意図して公開した入口に門番(API 認証)がないこと

6-2. 通信・ネットワーク

  • クライアント → Cloudflare: TLS 1.2+(Cloudflare 既定)
  • cloudflared → API: localhost(SEC-03 適用後は LAN 非公開)
  • PostgreSQL: Docker 内部ネットワークのみ。5432 をホストに公開しない

6-3. API・認証(本番案)

# クライアント(アプリ)
        POST /api/v1/feedback
        Authorization: Bearer <FEEDBACK_API_KEY>
        Content-Type: multipart/form-data
        
        # サーバー(.env)
        FEEDBACK_API_KEY=<ランダム 32 文字以上>

キー配布: 本番アプリビルド時に環境変数または難読化して同梱。 ローテーション時はアプリ再配布が必要(月次更新と整合)。 HTTPS 上で送れば途中傍受は困難(TLS の役割)。アプリに埋め込んだキーは逆コンパイルで取られる可能性あり(小規模運用では許容しつつ認識)。 将来、端末ごとのキーや Cloudflare Access は規模拡大時に検討(TBD-06)。

6-4. データ保護

データ転送中保存時保持期間(案)
FB 画像HTTPS平文(BitLocker でホスト保護)学習利用後も蓄積。容量ポリシーは TBD-04
DB レコードPostgreSQL 平文バックアップと同期(3.7)
ログローカル / Docker画像バイナリをログに出さない

6-5. シークレット管理(.env と DB の使い分け)

秘密保管ローテーション漏洩時
TUNNEL_TOKENserver/.envCloudflare で再発行 → compose 再起動即時再発行。旧トークン失効
FEEDBACK_API_KEYserver/.env + アプリ計画メンテ時に再生成・再配布即時変更 + アプリ緊急配布
GitHub PATworker/.env四半期(K-23)GitHub で revoke → 新 PAT
POSTGRES_PASSWORDserver/.env年次 or 漏洩時DB パスワード変更 + 再起動
POSTGRES_PASSWORDserver/.env年次 or 漏洩時DB パスワード変更 + 再起動

DB ユーザー/パスワードもシークレットだが、すべての秘密を DB テーブルに入れるのは一般的ではない。 API 起動時に DB へ接続するため「鶏と卵」になり、運用も複雑化する。 小規模構成では .env(gitignore)+ ファイルアクセス制限が定石。大規模では Vault / クラウド Secret Manager。

7. OSI 参照モデルと FW(本プロジェクト)

FW(ファイアウォール)は主に層 3〜4(IP・TCP/UDP ポート)で「この入口への通信を許可/拒否」する門番です。

        flowchart TB
          subgraph L7["層7 アプリ: HTTP POST /api/v1/feedback"]
            App[本番アプリ]
            Curl[curl いたずら]
            API[FastAPI]
          end
          subgraph L6["層6 表現: TLS"]
            TLS[HTTPS 暗号化]
          end
          subgraph L4["層4 輸送: TCP ポート"]
            CF443["Cloudflare :443"]
            LAN8000["PC LAN :8000 ← 現状 docker expose"]
          end
          subgraph L3["層3 ネットワーク: IP"]
            Internet[インターネット]
            LAN[同一 LAN]
          end
          App --> TLS --> CF443
          Curl --> TLS --> CF443
          CF443 --> Tunnel[cloudflared Tunnel]
          Tunnel --> API
          LAN -->|"FW未設定時 到達可"| LAN8000 --> API
                
本プロジェクトの例現状あるとよい対策
L7 アプリFB API・認証ヘッダ無認証で POST 可API キー(SEC-01)・サイズ上限(SEC-02)
L6 表現TLS / HTTPSCloudflare で有効維持
L4 輸送TCP 8000, 4438000 が PC に公開8000 を localhost のみ(SEC-03)+ FW で LAN 拒否
L3 ネットワークIP・ルーティンググローバル IP 非公開(Tunnel)維持。LAN IP 直接アクセスは FW で制限
DBPostgreSQL 5432Docker 内部のみ維持 + 強パスワード

FW をしないと何が危いか(本件): 同一 LAN や誤設定時に http://<PCのLAN IP>:8000 で Cloudflare・HTTPS・API 認証をバイパスして API に届く可能性(層 7 の門番がないとそのまま通る)。 Raspberry Pi で行った FW 設定は、まさに層 3〜4 で「不要なポートへの入り口を閉じる」作業です。

多層防御: DB 非公開(L4)だけでは不十分。API 侵害=DB も読めるため、L7 の API 認証も必要。

8. Cloudflare Access と Pages のメール認証

概念は同じ(コンテンツの前に「誰か」を確認する)が、製品の適用先が違います

Pages のメール認証Cloudflare Access(本 API 向け)
保護対象静的 HTML(docs)API / 管理画面など動的オリジン
利用者人間(ブラウザ)人間向け。モバイルアプリの自動 POST には不向き
本 FB API第一候補ではない(API キーが適する)

9. セキュリティ実測確認(WBS 3.1.5・学習目的)

理論を体験で補強する。実施記録を本節または開発ログに残す。

手順確認することコマンド例
1. 正規 FB 送信 アプリ or test-feedback.ps1 で 1 件成功 .\scripts\test-feedback.ps1 -BaseUrl https://api.dammy-otoko.com
2. DB 確認 feedbacks に何が保存されるか(PII の有無) docker compose exec db psql -U app -d feedback_db -c "SELECT id, left(inference_result,80), created_at FROM feedbacks ORDER BY created_at DESC LIMIT 3;"
3. Storage 確認 画像ファイルの実体・ファイル名 data/storage/ をエクスプローラで確認
4. API ログ リクエスト body・画像がログに出ないか docker compose logs api --tail 50
5. 無認証 curl(SEC-01 有効時) 認証なし POST が 401 で拒否されること curl で /api/v1/feedback(ヘッダなし)→ missing or invalid authorization header
6. 誤った API キー(SEC-01) 正しくない Bearer が 401 で拒否されること test-feedback.ps1 -ApiKey "wrong-key-on-purpose"invalid api key
7. LAN 経路(任意) :8000 直叩きが可能か(FW / bind 設定の理解) 同一 LAN から http://<PC LAN IP>:8000/api/v1/health

実施記録: K-28 実測記録(2026-06-13)。 SEC-01 本番キー運用・deny 確認: K-30deny 実測、2026-06-14)。

10. ゴーライブ前チェックリスト(MS-6)

#項目SEC
1API 認証が有効で、無認証 POST が 401 になるSEC-01
2アップロードサイズ上限が動作するSEC-02
3DB パスワードがデフォルトから変更済みSEC-04
4.env が git に含まれていないSEC-06
5PAT スコープが最小(repo dispatch に必要な範囲)SEC-06
6役場 PC の物理・OS 更新方針が合意されているSEC-05
7漏洩時エスカレーション先が 運用計画 に記載されているSEC-06

11. 未決定事項(TBD)

ID項目選択肢決定時期
TBD-06API 認証方式共有 API キー(推奨・簡易) / Cloudflare Access / mTLS3.1.4
TBD-07レート制限アプリのみ / Cloudflare Rate Limiting / FastAPI ミドルウェア3.1.4
TBD-08山梨大 Worker からのサーバーアクセスSSH / Tailscale / データは FB のみ(現状)3.2

12. 関連資料

§4 データフロー

内向き通信(クライアント → 自宅 PC): CGNAT 環境のため、 スタブアプリから FastAPI への FB 送信は必ず Cloudflare Edge → Tunnel(cloudflared)経由とする。 自宅 PC へ直接到達する経路は存在しない。 SEC-01(2026-06-14〜): 経路①に加え、アプリは Authorization: Bearer を付与する — 詳細は シーケンス図 §2、旧来の無認証フローは §1

1. 全体データフロー

        flowchart TB
          subgraph Client["クライアント(スタブアプリ)"]
            FB["FB データ(画像 + 推論結果 JSON)"]
          end
        
          subgraph CFNet["インターネット / Cloudflare"]
            CF["Edge Server(HTTPS 終端)"]
          end
        
          subgraph Server["自宅 PC(Docker)"]
            TUN["cloudflared(Tunnel クライアント)"]
            API[FastAPI]
            DB[(Database)]
            STG[(File Storage)]
            WK[AI Worker]
          end
        
          subgraph CICD["GitHub(インターネット)"]
            GHA[GitHub Actions]
            ART[APK / IPA artifact]
          end
        
          subgraph Deploy["配布"]
            DEV[管理下端末]
          end
        
          FB -->|"①内向き HTTPS"| CF
          CF -->|"Tunnel 中継"| TUN
          TUN --> API
          API --> STG
          API --> DB
          DB --> WK
          STG --> WK
          WK -->|新モデル| STG
          WK -->|"②外向き Webhook"| GHA
          GHA --> ART
          ART -->|"③配布(Tunnel 外)"| DEV
                

通信方向の整理

番号方向経路備考
内向き スタブアプリ → Cloudflare Edge → cloudflared → FastAPI FB 受信。CGNAT 回避のため Tunnel 必須。SEC-01 後は Bearer 必須(抜け道の整理
外向き AI Worker → GitHub API(repository_dispatch) 再学習完了後のビルド起動。Tunnel は不使用
配布 GHA artifact → 管理下端末(adb / TestFlight) アプリ更新。FB 経路とは別

2. FB 受信フロー(02-1 相当)

段階データ保存先SEC-01 前 / 後
1. アプリ送信画像 + 推論結果 + タイムスタンプ後: + Authorization
2. Cloudflare EdgeHTTPS 終端・CDN変わらず(盗聴防止)
3. Tunnel 中継Edge → cloudflared(自宅 PC)へ転送変わらず
4. API 受信multipart データ後: 鍵検証(不一致 → 401)
5. 永続化画像ファイルFile Storage201 のときのみ
6. 永続化メタデータ(ID, 結果, パス, 日時)Database201 のときのみ

Phase1 初期のギャップ: 段階 2〜3 までは安全でも、段階 4 で「誰の POST か」を見ていなかった( シーケンス §1)。

3. モデル更新・配布フロー(02-2 / 02-3 相当)

Phase1 の月次運用イメージ(手動 / 自動の境界、Android adb vs iPhone TestFlight)は Phase1 ナレッジ K-21 を参照。

        flowchart LR
          A[FB データ蓄積] --> B[AI Worker 再学習]
          B --> C[新モデルファイル]
          C --> D[Git 反映 or 配置]
          D --> E[repository_dispatch]
          E --> F[GHA ビルド]
          F --> G[モデル同梱 APK/IPA]
          G --> H[端末へ配布]
                

4. データ要素定義

データ形式個人情報
FB 画像JPEG / PNGhorse_20260605_001.jpg含まない
推論結果JSON{"label":"A","score":0.92}含まない
モデル(①).tflitemodel_v202606.tflite
モデル(②).mlmodel / .mlpackagemodel_v202606.mlpackage

§5 シーケンス図

本資料は主要処理の時系列を示します。 WBS 1(Phase1 インフラ検証)では §1(旧) の図だけで MS-1〜MS-3 まで進めましたが、 WBS 3.1.4(SEC-01)以降は §2(現行) が正本です。 「HTTPS があるのに何が守れていなかったか」は §3 を参照。

関連: データフロー(論理構造) / K-31 全体構成図(鍵の流れ) / セキュリティ設計(SEC 一覧) / K-28 実測(無認証 POST の体感)

0. 時系列 — どの図がいつ有効だったか

期間FB 受信の図状態実測の目安
2026-06-07 〜 2026-06-13 §1 旧(SEC 前) MS-1 / MS-3 達成。Tunnel + HTTPS のみ 実機・test-feedback.ps1 とも 鍵なしで 201K-28 手順5
2026-06-14 〜 §2 現行(SEC-01 有効) FEEDBACK_API_KEY 設定時は Bearer 必須 正しい鍵 → 201、無認証・誤キー → 401(K-30 deny)。実機 201: runbook §4c

0b. HTTPS / Tunnel が守るもの・守らないもの

Phase1 初期に「Tunnel があれば安全」と誤解しやすい点の整理(K-25)。

レイヤーPhase1 初期から有効SEC-01 前のギャップSEC-01 後
通信路(盗聴) HTTPS(Cloudflare Edge で TLS 終端) 変わらず
到達性(CGNAT 回避) Named Tunnel(外向き接続のみ) 変わらず
送信者の正当性 なし URL を知れば誰でも POST 可(curl・スクリプト・偽アプリ) Authorization: Bearer で API キー検証
health エンドポイント 認証なし 意図的に公開(死活監視) 意図的に公開(POST のみ保護)

1. FB 受信 — 旧(SEC-01 前・2026-06-13 まで)

WBS 1.2.3 / 1.5.2 / MS-1〜3 の検証時点の図。認証ステップが存在しない。 当時の実装どおり、API は multipart を受け取れば保存した。

        sequenceDiagram
          autonumber
          actor User as 利用者
          participant App as スタブアプリ
          participant Attacker as 不正送信者(任意)
          participant CF as Cloudflare Edge
          participant TUN as cloudflared
          participant API as FastAPI
          participant STG as Storage
          participant DB as Database
        
          Note over App,API: SEC-01 前 — Authorization ヘッダなし
          User->>App: 画像選択・推論
          App->>CF: POST /api/v1/feedback (HTTPS, 画像+JSON)
          CF->>TUN: Tunnel 中継
          TUN->>API: 転送
          API->>STG: 画像保存
          API->>DB: メタデータ記録
          API-->>App: 201 Created
        
          Note over Attacker,API: 抜け道 — URL さえ分かれば同じ POST が通る
          Attacker->>CF: POST(鍵なし curl 等)
          CF->>TUN: 中継
          TUN->>API: 転送
          API-->>Attacker: 201 Created
                

当時「セキュリティが担保されていた」と言えたこと: 路上の盗聴は TLS で防げる、自宅 PC にポート開放不要。

当時「担保されていなかった」こと: 「正規アプリからの POST か」の区別。公開 URL は実質書き込み用の入口だった(K-26)。

2. FB 受信 — 現行(SEC-01 有効・2026-06-14 〜)

server/.envFEEDBACK_API_KEY があるとき。 アプリはビルド時 --dart-define で同じ鍵を同梱(runbook §4b)。

        sequenceDiagram
          autonumber
          actor User as 利用者
          participant App as スタブアプリ
          participant Attacker as 不正送信者
          participant CF as Cloudflare Edge
          participant TUN as cloudflared
          participant API as FastAPI
          participant Auth as SEC-01 認証
          participant STG as Storage
          participant DB as Database
        
          Note over App: APK 内 feedbackApiKey
(ビルド時 dart-define) User->>App: 画像選択・推論 App->>CF: POST + Authorization Bearer 鍵 CF->>TUN: Tunnel 中継(TLS 済み) TUN->>API: 転送 API->>Auth: 鍵一致? Auth-->>API: OK API->>STG: 画像保存 API->>DB: メタデータ API-->>App: 201 Created Note over Attacker: 鍵なし / 誤キー Attacker->>CF: POST(Authorization なし) CF->>TUN: 中継 TUN->>API: 転送 API->>Auth: 鍵一致? Auth-->>API: NG API-->>Attacker: 401 Unauthorized

healthGET /api/v1/health)は従来どおり認証不要。 保護対象は POST /api/v1/feedback のみ(K-30 deny 実測済み)。

3. 抜け道の整理 — Phase1 で実測したもの

抜け道SEC-01 前SEC-01 後記録
公開 URL への無認証 POST 可能(201) 不可(401) K-28 手順5K-30 deny
誤った API キー —(検証なし) 401 invalid api key K-30 deny
localhost / LAN から :8000 直接 health 到達可(Tunnel バイパス) SEC-03 で bind 方針を検討。POST も鍵が必要 K-28 手順6、SEC-03
古い APK(鍵なし)からの POST 401(鍵更新後に旧 APK は使えない) 鍵ローテーション時に再ビルド・再配布
APK 逆解析で鍵抽出 理論上可能(モバイル API キーの限界) セキュリティ設計 — 無認証よりマシ、完全防御ではない

3b. 不正 POST のイメージ(SEC 前)

        flowchart LR
          subgraph ok [守れていたこと]
            TLS["HTTPS: 盗聴防止"]
            TUN["Tunnel: CGNAT 回避"]
          end
          subgraph ng [守れていなかったこと]
            URL["公開 URL の共有"]
            CURL["curl / 任意クライアント"]
            URL --> CURL
            CURL --> API["FastAPI POST → 201"]
          end
                

4. 再学習〜配布(02-2 / 02-3)— 認証の別系統

FB 受信(SEC-01)とは別の秘密が絡む経路。混同注意(K-31)。

        sequenceDiagram
          autonumber
          actor Op as 運用者
          participant WK as AI Worker(PC)
          participant STG as Storage
          participant DB as Database
          participant GH as GitHub API
          participant GHA as GitHub Actions
          participant Dev as 管理下端末
        
          Op->>WK: run-training-stub.ps1
          WK->>DB: FB データ参照(ローカル)
          WK->>STG: モデル生成
          Note over WK,GH: PAT(worker/.env)で認証
FEEDBACK_API_KEY とは無関係 WK->>GH: repository_dispatch + PAT GH->>GHA: android-build.yml 起動 Note over GHA: secrets.FEEDBACK_API_KEY
→ dart-define → APK GHA->>GHA: Flutter ビルド GHA-->>Dev: artifact → adb install

5. 手動ビルド検証(workflow_dispatch)

        sequenceDiagram
          actor Op as 運用者
          participant GHA as GitHub Actions
          participant Sec as Actions Secret
          participant Art as Artifact
        
          Op->>GHA: Run workflow(GitHub UI)
          GHA->>Sec: FEEDBACK_API_KEY 読み込み
          Sec-->>GHA: ビルド時 dart-define へ
          GHA->>GHA: flutter build apk --release
          GHA->>Art: APK 出力
          Op->>Art: ダウンロード → adb install
                

UI からの手動起動に PAT は不要。APK への鍵同梱には Actions Secret が必要(K-22)。

6. ② iPhone 配布(参考・未検証)

        sequenceDiagram
          participant GHA as GitHub Actions
          participant FL as Fastlane
          participant ASC as App Store Connect
          participant TF as TestFlight
          participant iPhone as iPhone
        
          GHA->>FL: iOS ビルド・署名
          FL->>ASC: アップロード
          ASC->>TF: ビルド公開
          iPhone->>TF: 更新取得・インストール
                

② の検証は①完了後に実施。FB 受信の SEC-01 は iPhone アプリでも同様に Bearer 付き POST となる想定。

7. 図の読み分け早見表

知りたいこと見る図
MS-1〜3 の当時の FB の流れ§1 旧
いまの FB(鍵付き)§2 現行
HTTPS だけでは足りなかった理由§0b§3
PAT と API キーの違い§2 + K-31 §4
月次 dispatch → APK§4

§6 機能・関数定義

検証対象コンポーネントの機能一覧。実装済み項目はパス・配置を具体化済み。

1. スタブモバイルアプリ(C-01)

機能 ID機能名概要入出力フェーズ
F-01 画像取得 ギャラリーから画像を選択(image_picker 出力: 画像ファイルパス ① 実装済み
F-02 ダミー推論 DummyInference.generate() — ラベル A/B/C、スコア 0.75〜0.99 出力: {"label","score"} ① 実装済み
F-03 FB 送信 FeedbackClient.send()POST /api/v1/feedback デフォルト URL: https://api.dammy-otoko.com ① 実装済み

2. Web API(C-03)

機能 ID関数 / エンドポイント概要フェーズ
F-10 POST /api/v1/feedback FB データを受信し Storage / DB に保存
F-11 GET /api/v1/health ヘルスチェック(Tunnel 疎通確認用)

3. AI Worker スタブ(C-06)

機能 ID関数(予定)概要フェーズ
F-20 run_training_stub() 学習処理のスタブ。モデルファイルをコピーして新版本を生成
F-21 trigger_github_dispatch() 学習完了後に GitHub Actions を起動

4. GitHub Actions(C-07)

機能 IDワークフロー概要フェーズ
F-30 android-build.yml Flutter APK ビルド、artifact 出力
F-31 ios-build.yml iOS ビルド、TestFlight 配布

5. 外部 API 呼び出し

機能 IDAPI呼び出し元概要
F-40 GitHub repos/.../dispatches AI Worker モデル更新完了を CI に通知