RELEASE NOTE

AI 連携の土台をつくる提供元とモデルの設定、API キーの 2 つの管理方式、送信の共通の入口

OpenGreenTrack では、請求書の読取りや開示文の下書きなど、AI を使う機能を順に加えていく予定です。10 月 24 日の変更は、その前に整えた「土台」で、AI を使う機能そのものはまだ入っていません。機能ごとにばらばらに AI を呼ぶのではなく、鍵・送ってよいもの・回数・記録を 1 か所で守る作りにしました。

  • AI
  • セキュリティ
  • 設計
  • セルフホスト
AI の設定のカード。鍵の管理は環境変数、提供元は Anthropic、API キーは設定済み、モデルは Claude Sonnet 5.5 がプルダウンで選ばれ、再取得とモデルを保存のボタンがある

この版のポイント

  1. AI の提供元(Anthropic・Google・OpenAI)とモデルを、管理者が設定画面で選べる
  2. API キーは環境変数(方式A・既定)か、暗号化して画面から登録(方式B)。どちらの方式でもブラウザには渡らない
  3. AI への送信は共通の入口に一本化。用途・入力・回数・監査ログを 1 か所で確かめる
  4. AI を使う機能(請求書の読取りなど)は、このあと用途ごとに追加する(現時点の登録は 0 件)

01CONCEPT

考え方:設定・共通の入口・用途の 3 層に分ける

OpenGreenTrack はセルフホストで使うソフトウェアなので、AI も各組織が自分で契約した提供元の API キーで使います。もし機能ごとに提供元の API を直接呼ぶと、鍵の扱い・送信前の確認・回数の上限・記録の残し方が機能ごとにばらばらになり、どこか 1 つの抜けが情報漏えいや想定外の課金につながります。

呼び出す側AI を使う機能

入口だけを呼ぶ

  1. 用途purposes.ts

    送ってよい入力の種類と量、回数の上限、出力の上限。例:請求書の読取り(今後追加)

    決める人:開発者(プルリクエストのレビューを通す)
  2. 共通の入口generateStructured

    用途と入力の確認、鍵とモデルの解決、回数の上限、送信、結果の検証、監査ログ

    決める人:OpenGreenTrack が提供
  3. 設定環境変数・AI の設定

    鍵の方式と API キー、暗号化の鍵、使うモデルの選択

    決める人:運用担当(環境変数)と管理者(画面)
各組織の契約Anthropic・Google・OpenAI

組織が用意した API キーで呼ぶ

図解AI 連携の 3 つの層と、それぞれを決める人。機能は共通の入口だけを呼び、提供元の API を直接は呼びません。
  • 鍵は組織が用意する。 使ってよいのは、法人契約で、送った内容を学習に使わない設定の鍵だけです(Gemini は有料枠)。
  • 送る前に見せる。 送り先と送る内容を、共通の確認ダイアログで見せてから送ります。
  • AI の出力は候補にとどめる。 そのまま確定データにはせず、画面で候補として見せ、保存は人が行います。
  • 何を送るかは用途ごとに決める。 用途はコードで登録し、プルリクエストのレビューを通ります。

02HOW TO

AI の提供元とモデルを選ぶ

対応している提供元は Anthropic(Claude)、Google(Gemini API)、OpenAI(ChatGPT)の 3 つです。どのモデルを使うかは管理者が「企業・メンバー設定」の「AI の設定」で選び、組織の全員が同じ設定を使います。メンバーは設定の内容を見るだけです。

STEPS環境変数で設定する(方式A・既定)

  1. 提供元の管理画面で API キーを作る

    法人契約で、送った内容を学習に使わない設定の鍵を作ります。Anthropic は 1 つのワークスペースに限った鍵にしてください。

  2. 環境変数に 2 行を足して再起動する

    .env.local(クラウドではホスティングの環境変数)に AI_PROVIDER と AI_API_KEY を書き、アプリを再起動します。

  3. 管理者がモデルを選んで保存する

    「設定 → 企業・メンバー設定 → AI の設定」で、プルダウンからモデルを選び「モデルを保存」を押します(冒頭の画面)。画面のモデル一覧は、ダミーの鍵で撮影したため応答をダミーに差し替えています。

.env.local(方式A)
AI_PROVIDER=anthropic   # anthropic / google / openai
AI_API_KEY=<提供元の API キー>
  1. 1提供元からモデル一覧を取る管理者が AI の設定を開いたとき。組織ごとに 1 分 10 回まで
  2. 2使えるモデルに絞る画像・PDF を読めて、決まった形(JSON スキーマ)で出力できるもの。廃止予定は末尾に
  3. 3管理者がプルダウンで選ぶメンバーの画面では一覧を取りに行かない
  4. 4保存の前に一覧を取り直す選んだ ID が提供元にあるかを確かめる。同じモデルなら書き込まない
  5. 5組織の設定として保存するai_settings に 1 行。選んだ時点の提供元も記録し、変更を監査ログに残す

環境変数で提供元を替えると、保存済みのモデルは無効として扱い、管理者に選び直してもらう

図解モデルの一覧から保存までの流れ。保存の前にも一覧を取り直すので、提供元に無い ID は保存できません。

03DESIGN

設計:API キーは、環境変数か、暗号化して画面から

サーバーの環境変数を触れる人がいない組織のために、管理者が画面から API キーを登録する方式(方式B)も用意しました。画面から鍵を差し替えられるぶん攻撃の入口が増えるため、既定は方式A のままです。どちらにするかは環境変数でしか切り替えられません。ログイン中のアカウントを乗っ取った人が、自分で方式B を有効にして鍵を差し替えることはできません。

方式A(既定)

設定環境変数に鍵を書く

AI_PROVIDER・AI_API_KEY

保管サーバーだけが読む

鍵の差し替えは環境変数を書き換えて再起動

画面「設定済み」とだけ表示

管理者はモデルを選ぶ

方式B(環境変数で有効にした組織だけ)

設定管理者が画面から登録

ログインパスワードで本人確認し、その鍵で提供元に接続できるかを確かめる

保管AES-256-GCM で暗号化して保存

組織と提供元に結び付け、ブラウザからは読めないテーブルに置く

画面末尾 4 文字と更新日時だけ

登録・差し替え・削除は管理者だけ

図解2 つの方式の違い。機能や共通の入口は、どちらの方式かを意識せずに同じ形で鍵を受け取ります。
方式B の AI の設定のカード(管理者)。API キーの登録の欄に、提供元のプルダウン、API キー、ログインパスワード(本人確認)の入力欄と「登録する」ボタンが並ぶ
方式B の管理者の画面。登録・差し替え・削除のたびに、ログインパスワードで本人確認をします。登録の前に、その鍵で提供元に接続できるかも確かめます。
方式B の AI の設定のカード(メンバー)。API キーは「登録済み(末尾 Qa7x・2026/10/24 04:35 更新)」とだけ表示されている
メンバーには、鍵の末尾 4 文字と更新日時だけを表示します(画面の鍵はダミーです)。

入力

  • API キー管理者が画面で入力(平文はサーバーの中だけ)
  • 暗号化の鍵環境変数 AI_ENCRYPTION_KEY(32 バイト)。DB の外に置く
  • IV保存のたびに 12 バイトの乱数で作り直す
  • 追加認証データai-credential:v1:〈組織 ID〉:〈提供元〉
AES-256-GCM

ai_credentials(組織に 1 行)

  • 暗号文・IV・認証タグ鍵そのものは保存しない
  • keyId暗号化の鍵の SHA-256 の先頭 16 文字
  • 末尾 4 文字・更新日時画面に出すのはここだけ
  • DB やバックアップだけが漏れても暗号化の鍵が無いので読めない
  • 暗号文を別の行に移しても組織と提供元が合わないので復号できない
  • 暗号化の鍵を替えたらkeyId が合わず「登録し直してください」と出る
  • ブラウザからはテーブルに権限を与えていないので読めない
図解方式B の鍵の暗号化。暗号化の鍵は DB の外(環境変数)に置き、暗号文は組織と提供元に結び付けます。

Supabase Vault や pgcrypto を使わず、アプリのサーバーで暗号化しているのは、鍵の平文が SQL 文に乗ってデータベースのログに残るのを避けるためです。暗号化の鍵の指紋(SHA-256 の先頭 16 文字)を一緒に保存しておくので、暗号化の鍵を替えたときは、復号を試さなくても「登録し直してください」と案内できます。

.env.local(方式B。AI_PROVIDER と AI_API_KEY は外す)
# 暗号化の鍵は openssl rand -base64 32 で作る(SUPABASE_SERVICE_ROLE_KEY とは別の値)
AI_ALLOW_KEY_REGISTRATION=true
AI_ENCRYPTION_KEY=<openssl rand -base64 32 の出力>
# 任意。画面から登録してよい提供元(未設定なら 3 社すべて)
AI_ALLOWED_PROVIDERS=anthropic,openai

04DESIGN

設計:AI への送信は、共通の入口を一本だけ

AI を使う機能は、共通の入口(generateStructured)だけを呼びます。入口は決まった順番で確かめ、どこかで条件を満たさなければ提供元には送りません。

呼び出す側AI を使う機能

請求書の読取り・開示文の下書きなど(今後追加)

共通の入口(generateStructured)

  1. 1用途の確認登録した用途だけが呼べる
  2. 2入力の確認種類・量。形式は先頭のバイト列で判定
  3. 3設定とモデル鍵・提供元・管理者が選んだモデル
  4. 4回数の上限用途ごと・組織全体
  5. 5提供元へ送信60 秒で打ち切る
  6. 6結果の検証機能が決めた形かを確かめる
  7. 7監査ログ鍵・送った内容・出力は残さない
提供元Anthropic・Google・OpenAI

ファイル保管 API は使わない

図解共通の入口での処理の順番。機能が決めた出力の形(JSON スキーマ)に合わない結果も使いません。
  • 用途ごとに、送ってよい入力の種類(テキスト・PDF・画像)と量(既定 4MB)、回数の上限、出力の上限を決めます。登録していない用途からは、型の段階で呼べません。
  • ファイルの形式は、拡張子やブラウザの申告ではなく、先頭のバイト列で判定します(PDF・PNG・JPEG・WebP)。
  • 提供元に送るときはツールを渡さず、各社のファイル保管 API も使いません。OpenAI には store: false を付けて、応答を保存させません。
監査ログ(ai.request)に残す
  • 用途・提供元・モデル・鍵の方式
  • 入力の種類と量、SHA-256
  • 結果(成功・失敗の分類)とかかった時間
残さない
  • API キー
  • 送った内容(ファイル・文章)
  • AI の出力
提供元のエラーHTTP のエラー

本文には鍵の一部が含まれることがある

共通の分類にそろえる
  • invalid_key
  • billing
  • rate_limited
  • overloaded
  • model_unavailable
  • input_rejected
  • timeout
  • bad_response
  • unknown
画面には決まった文だけ

提供元の本文は画面にもログにも出さない

図解監査ログとエラーの扱い。監査ログはメンバー全員が読めるので、鍵や送った内容は残しません。

回数の上限は、監査ログの件数を数えて判定します(用途ごとの 1 分・24 時間と、組織全体の 24 時間に 500 回)。件数を数えられなかったときは、送らない側に倒します。上限は連打やスクリプトによる暴走を止めるためのもので、厳密な課金の上限ではありません。

送った内容の扱い(2026 年 10 月 23 日時点の各社の公開情報。契約の前に最新の規約を確認してください)
提供元学習への利用提供元での保持OpenGreenTrack からの送り方
Anthropic明示の許可なしには使わない30 日以内に削除(ポリシー違反と判定されたものは最長 2 年)本文に直接入れて送る(ファイル保管 API は使わない)
Google(Gemini API の有料枠)使わない不正利用の監視のため 55 日generateContent で送る。無料枠の鍵は使わない
OpenAI既定では使わない不正利用の監視のログは 30 日store: false を付けて送る(応答を保存させない)

05ROADMAP

これから:用途ごとに AI の機能を加える

公式ロードマップの次の項目は、請求書の読取り(AI による活動量データの自動入力)です。機能を加えるときは、用途を登録し、共通の入口だけを通して呼びます。開発者向けの決まりは docs/architecture.md の「AI 連携」にまとめています。

  • 機能の処理でログインと組織を確かめ、組織 ID はリクエストではなくログイン中のプロフィールから取る
  • 利用者が入力した文は指示文に混ぜず、入力として渡す
  • 出力の形を決め、値の範囲まで機能側で検証する
  • AI の出力は文字として表示し、HTML として解釈しない

06CHANGELOG

変更の一覧

  • 新機能・改善AI の設定(方式B):画面から API キーを登録できるように(暗号化・本人確認)#188
  • 新機能・改善AI の共通の入口:用途の登録・3 社への送信・エラーの分類・監査ログ・回数の上限#186
  • 新機能・改善AI の設定(方式A):環境変数の API キーでモデル一覧を取得し、管理者がモデルを選べるように#185

TRY OPENGREENTRACK

コードもロードマップも、GitHub で公開しています。

README を読んで動かす 導入・共創について相談する