Audit Log
金融操作を後から説明できるように、行差分と業務イベントを別々に残す仕組みを学びます。
台帳、振込、KYC、権限、監査、照合をMinibankの実装と結び付ける。
先に学ぶ内容 — Prerequisite: 基礎 / 銀行業務学ぶこと — LEARNING GOALS
このページで学ぶこと
- 監査証跡が答える質問
- trigger監査とRPC監査
- append-onlyとactor attribution
体験08 · AUDIT(監査記録)
誰が・いつ操作したかを、どう残すか確かめよう
まずは予想をひと言。本文を読んだ後、実際の監査記録の表示を確認します。
- 01まず予想する — PREDICT
まずここだけ:思ったことをひと言書く
誰が・いつ操作したかを記録する必要があるのは、なぜだと思いますか?
正解でなくて大丈夫です。「〜だと思う」から始めてみましょう。
ヒント1を見る
「何が起きたか」と「誰が、なぜそうしたか」は、同じ記録で分かるでしょうか?
ヒント2を見る
会計の記録(Ledger)と、操作の記録は、同じ目的を持つでしょうか?
もっと詳しく:この問いで使う専門用語
- Audit Log(監査ログ)は、誰が・いつ・何をしたかを消せない形で残す記録です。
- actor(行為者)は、その操作を実際に行った本人です。
- business event(業務イベント)は、『KYCを承認した』のような業務上の意味を持つ記録です。
- before / after(変更前後)は、操作によって何がどう変わったかを表す値の組です。
日常語で理解する — PLAIN LANGUAGE
まず、ここだけつかむ
監査ログは、誰が・いつ・何を・どの対象へ行い、前後で何が変わったかを説明する記録です。単なるエラーログとは目的が違います。
重要操作が成功したのに監査だけ失敗して記録されない状態を避けるため、業務変更と同じtransactionで記録し、監査失敗時は操作も失敗させる考え方が重要です。
append-onlyでもDB ownerは強い権限を持ちます。アプリ内の改ざん防止と、インフラ管理者まで含む脅威対策は分けて考えます。
順番に理解する — DIFFICULT PARTS, STEP BY STEP
難しいポイントを、順番にほどく
専門用語を覚えることより、「なぜ必要か」「このシステムのどこで使うか」を理解することが目標です。
actor attribution
操作の主体を、後から一意に特定できるよう残すことです。
service_roleのような共通主体だけでは、実際に指示した管理者を追跡できません。
auth.uid()やadmin operatorをbusiness eventへ記録します。
before / after
変更前と変更後の値です。
『更新した』だけでなく、何から何へ変えたかを説明できます。
table変更triggerが行の差分を監査記録へ残します。
business event
技術的な行更新ではなく、『KYCを承認した』など業務上の意味を持つ記録です。
複数tableにまたがる操作の目的を、人が読める単位で追跡できます。
特権RPCが操作名・actor・対象などを明示して記録します。
immutableと脅威境界
通常のアプリ権限では更新・削除できない一方、DB ownerまで絶対に防げるとは限らないという境界です。
『誰にも改ざんできない』と過信せず、外部保管・権限分離など追加対策を判断できます。
trigger・revoke・private schemaでアプリ経路を守りますが、owner権限は別の運用課題です。
発展 — ADVANCEDもっと詳しく確認する最初の学習では読まなくても問題ありません。仕組みをもっと深く理解したい場合に開いてください。
監査ログが答えること
| 質問 | 列 |
|---|---|
| 誰が | actor_user_id / actor_role |
| いつ | occurred_at |
| 何を | action / source |
| どの対象に | schema_name / table_name / record_id |
| どう変えたか | old_data / new_data / metadata |
| どのrequestか | request_id / platform_request_id |
2種類の記録
- trigger source: customers、bank_accounts、admin_operatorsの許可列だけを差分記録
- rpc source: TRANSFER_COMPLETED、TRANSFER_OTP_REQUESTED、TRANSFER_OTP_REISSUED、TRANSFER_BENEFICIARY_CREATED、KYC_APPROVED、ACCOUNT_FROZENなど業務上の意味を記録
- auth.uid()とJWT roleを関数内で導出し、caller supplied actorを信用しない
- audit insertに失敗すると業務transactionも失敗するfail-closed設計
振込の監査を時系列で読む
TOTPの誤入力そのものはaudit business eventとして毎回記録される設計ではなく、試行回数は非公開のtransfer_auth.intentsのattemptsへ、検証結果はSupabase Auth側へ保存されます。『どの失敗もaudit_logsに1行ある』とは推測せず、業務event、状態table、application logの役割を分けて読みます。
- 01TRANSFER_OTP_REQUESTED:本人が振込意思を登録
- 02(#128以前はTRANSFER_OTP_REISSUEDで古いchallengeを置換。v7のreissue_transfer_otpは常に42501)
- 03認証アプリTOTPで認可したintentを消費した後にexecute_transfer()を実行
- 04TRANSFER_COMPLETED:金額、両口座、journal entryを記録
- 05顧客履歴・管理監視・auditをIDと時刻で照合
組戻しでは2つの会計ルールを説明する
組戻し仕訳自身は `REVERSAL_STANDARD` の `dynamic_mirror` ruleを参照し、元仕訳の借方・貸方を機械的に反転したことを説明します。しかし、それだけでは『なぜ元仕訳がその勘定科目だったか』を説明できません。
`/admin/accounting/journals/[id]` のJournal Explainは、組戻し自身に固定された『組戻しルール(反転メカニズム)』と、`reversal_of_entry_id` でたどった『元仕訳の会計ルール(なぜその科目か)』を並べて表示します。どちらも各Journal Entryの `accounting_rule_definition_id` から取得するため、現在ACTIVEな版から推測しません。Trace導入前の仕訳はrule未記録としてnullを正直に表示し、過去へ現在のruleを後付けしません。
実際のMinibankでは
audit schemaはData APIへ公開せず、通常roleへtable権限を与えません。audit_logsのUPDATE/DELETEはtriggerでも拒否します。管理画面の読み取りは専用のpublic wrapperと内部queryを通します。
体験学習の続き · LEARNING LOOP
ここまで読めましたか?続けて確認してみましょう
監査記録の役割を読んだところで、実際の記録画面を確認し、自分の言葉で説明してみます。
- 02実際に観察する — OBSERVE
同じ仕組みを、動く画面から観察する
この仕組みは管理者向けの監査画面で確認できます。管理者ログインがない場合は、この教材内の図・説明・Source Trail(教材の参照元)で代替学習し、次へ進めます。
管理者ログインがない場合:教材の参照元で確認する ↓ - 03証拠を確認する — EVIDENCE
何を見たか、1つずつ確かめる
Evidence(判断の根拠となる記録)を確認したらチェックします。各項目の「何を見るか」を手がかりにしてください。
画面を開けない項目は、Source Trail(教材の参照元)と教材の説明で確認できます。失敗を実際に起こす必要はありません。
- 04コードの処理経路を追う — CODE TRAIL
コードを書く必要はありません
ここでは「説明されている仕組みが、実際のMini Bankのどこに実装されているか」を確認します。まずは日本語の役割説明を読み、入力した依頼がどこへ渡るかをつかみましょう。コードの全文を読む必要はありません。
基本の順序は、画面 → API Route(依頼を受ける窓口)→ TransferService(処理をつなぐサービス)→ DB / RPC(記録の保存場所と、その処理の呼び出し)です。まず最初の2つを見て、続くサービスは「もっと詳しく」で確認します。
教材の参照元を確認する — Source Trail ↓行の変更差分(before/after)を記録する定義。何がどう記録されるかを探します。
supabase/migrations/20260819150321_create_audit_log_change_function.sqlMigration(DB定義の変更履歴)。業務上の意味を持つ出来事として記録する定義を確認します。
supabase/migrations/20260819163036_create_audit_log_business_event_function.sql - 05自分の言葉で説明する — EXPLAIN
LEVEL 1 — やさしい説明(まずここだけ)
なぜ監査記録は、会計の台帳(Ledger)とは別に必要なのでしょうか? 短い2文を目安に説明してください。
日常語だけで完了できます。専門用語の数や正誤を自動採点するものではありません。
ヒント1を見る
「会計の記録」と「操作の記録」という2つの言葉を使って書いてみましょう。
ヒント2を見る
もし監査記録が無かったら、何を後から説明できなくなるか考えてみましょう。
もっと詳しく:LEVEL 2 — 専門用語を使った説明(任意)
actor attribution、before/after、business eventの3つを使って、監査記録が答えられる質問を説明してみましょう。
挑戦する場合は、上の説明欄に追記できます。追加しなくても完了できます。
説明の確認ポイント — Checkpoint- 監査記録が誰が・いつ・何をしたかを残す記録であると説明できる
- 監査記録と会計記録が別の目的を持つと説明できる
理解を確かめる — CHECK YOUR UNDERSTANDING
3問で理解チェック
答えを頭の中で考えてから、各項目を開いて確認してください。
Q1アプリのconsole logがあれば監査ログは不要ですか?
不要にはなりません。保存保証、actor、前後値、改ざん耐性、業務上の意味が異なります。
Q2重要操作は成功したが監査INSERTだけ失敗、を許してよいですか?
原則許しません。同じtransactionで記録し、監査不能なら操作もrollbackする設計が必要です。
Q3append-onlyならDB ownerも改ざんできませんか?
通常はそう言い切れません。ownerや基盤管理者を脅威に含むなら、権限分離や外部の改ざん検知保管が必要です。
完全に暗記する必要はありません。大切な考え方を説明できそうなら押してください。
教材の参照元 — SOURCE TRAIL
根拠と関連教材
CURRENT IMPLEMENTATIONは確認したrepository内の実装です。HISTORICAL IMPLEMENTATIONは導入・修正の履歴で、後続migrationによる再定義があり得ます。RELATED LESSONは教材への案内で、実装の一次Sourceではありません。ASSUMPTIONは仮定、UNKNOWNは未確認を表します。
確認基点: d02eae8914f39cbf06bfe422dc39c71886f82b3e / 2026-09-11。稼働環境への適用確認: UNKNOWN。パス実在の自動検査と、記述した技術的事実の確認は別です。
- 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260819150320_create_audit_schema_and_audit_logs.sql監査table - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260819150321_create_audit_log_change_function.sqlaudit.log_change()row差分とappend-only - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260819163036_create_audit_log_business_event_function.sqlaudit.log_business_event()業務イベント - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260821120003_create_audit_read_api.sql管理者向け監査read API - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260823170001_create_transfer_beneficiary_and_otp.sqlOTP依頼・再発行・完了event - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260823190001_create_transfer_beneficiaries.sql振込先登録event - 過去の実装 — HISTORICAL IMPLEMENTATION
supabase/migrations/20260828220001_explain_rule_trace.sql組戻しruleと元仕訳ruleのversion固定trace - 現在の実装 — CURRENT IMPLEMENTATION
src/components/admin/JournalExplainPanel.tsx2つのruleを区別して表示するUI