エンティティ一覧
新基盤コアのエンティティ定義です。集約ルートは Room(WebRTC ルーム)で、用途は kind(interview / connection_test)で区別します。テナント側 PK との対応は物理 FK を張らず external_type / external_id で双方向解決します(リソース識別子 / external_ref 参照)。下図の ER に続けて各エンティティの責務・主要カラム・関連を示します。
ER 図
erDiagram
TENANTS ||--o{ SESSIONS : "scopes (tenant_id)"
TENANTS ||--o{ USERS : "scopes (tenant_id)"
USERS ||--o{ PARTICIPANTS : "enters as minedia_observer (nullable)"
TENANTS ||--o{ ROOMS : "scopes (tenant_id)"
TENANTS ||--o{ WEBHOOK_ENDPOINTS : "scopes (tenant_id)"
TENANTS ||--o{ API_KEYS : "scopes (tenant_id)"
TENANTS |o--o{ JWKS_KEYS : "scopes (tenant_id・基盤鍵は NULL)"
USERS ||--o{ WEBHOOK_ENDPOINTS : "created_by (nullable)"
USERS ||--o{ API_KEYS : "created_by (nullable)"
API_KEYS ||--o{ API_KEY_EVENTS : "has_many (audit)"
USERS ||--o{ API_KEY_EVENTS : "actor (nullable)"
USERS ||--o{ JWKS_KEYS : "created_by (nullable)"
ROOMS ||--o| SESSIONS : "has_one (0..1, booking)"
ROOMS ||--o| CONNECTION_TESTS : "has_one (0..1, kind=connection_test)"
SESSIONS ||--o{ HANDOUTS : "has_many"
ROOMS ||--o{ PARTICIPANTS : "has_many"
ROOMS ||--o{ RECORDINGS : "has_many"
ROOMS ||--o{ CHAT_CHANNELS : "has_many"
ROOMS ||--o{ ROOM_EVENTS : "has_many"
ROOMS ||--o{ ENTRY_HISTORIES : "has_many"
RECORDINGS ||--o{ TRANSCRIPTIONS : "has_many (G5: core)"
RECORDINGS ||--o| PROCESSED_RECORDINGS : "has_one (0..1)"
WEBHOOK_ENDPOINTS ||--o{ WEBHOOK_DELIVERIES : "has_many"
CONNECTION_TESTS ||--o{ CONNECTION_TEST_EVENTS : "has_many"
CHAT_CHANNELS ||--o{ CHAT_MESSAGES : "has_many"
CHAT_CHANNELS ||--o{ CHAT_POSTS : "has_many"
PARTICIPANTS ||--o{ CHAT_MESSAGES : "authors (nullable)"
PARTICIPANTS ||--o{ CHAT_POSTS : "authors (nullable)"
PARTICIPANTS ||--o{ ENTRY_HISTORIES : "linked (nullable)"
TENANTS {
bigint id PK
string uuid UK
string name
}
USERS {
bigint id PK
bigint tenant_id "default 0"
string uuid UK
string email UK
string encrypted_password
string name
string role "admin/member"
string locale
string time_zone
datetime deactivated_at
}
SESSIONS {
bigint id PK
bigint tenant_id "default 0"
string uuid
bigint room_id FK "0..1 per room (booking)"
string external_type "ext-ref (no FK)"
string external_id "ext-ref (no FK)"
datetime starts_at
datetime ends_at
string status "scheduled/active/cancelled/closed (booking)"
}
ROOMS {
bigint id PK
bigint tenant_id "default 0"
string uuid UK
string kind "interview/connection_test"
string status "open/closed"
string livekit_session_id "= LiveKit Room SID (RM_…); UK"
string archive_mode
string archive_resolution
string transcription_language "default ja-JP"
string transcription_service "default azure"
string stream_resolution
integer stream_frame_rate
string access_token UK
text entry_notice_text "nullable"
string entry_name_field_label "nullable"
}
PARTICIPANTS {
bigint id PK
bigint tenant_id "default 0"
string uuid
bigint room_id FK
string role "moderator/observer/panelist/minedia_observer"
bigint user_id FK "nullable (platform actor)"
string external_type "ext-ref (no FK)"
string external_id "ext-ref (no FK)"
string display_name
}
HANDOUTS {
bigint id PK
bigint tenant_id "default 0"
string uuid UK
bigint session_id FK
string external_type "ext-ref (no FK)"
string external_id "ext-ref (no FK)"
string file
string content_type "image/video"
integer sort
}
CONNECTION_TESTS {
bigint id PK
bigint tenant_id "default 0"
string uuid UK
bigint room_id FK "unique (kind=connection_test)"
string external_type "ext-ref (no FK)"
string external_id "ext-ref (no FK)"
string status "created/completed/failed"
integer dummy_stream_count
float baseline_metrics "bitrate/loss/fps/jitter"
float load_metrics "avg+worst x video/audio"
json raw_data
}
CONNECTION_TEST_EVENTS {
bigint id PK
bigint tenant_id "default 0"
bigint connection_test_id FK
string action_type
string browser_name
string platform_name
string device_name
json data
}
RECORDINGS {
bigint id PK
bigint tenant_id "default 0"
string uuid
bigint room_id FK
string provider_archive_id
string status
string output_mode
bigint size
decimal duration_sec
string storage_key
}
TRANSCRIPTIONS {
bigint id PK
bigint tenant_id "default 0"
string uuid UK
bigint recording_id FK
string provider "eleven_labs/openai/azure/anyenv/rimo"
string provider_transcription_id
string status
json result_json "行内格納 (As-Is 踏襲)"
string language
string kind "transcription/translation"
}
PROCESSED_RECORDINGS {
bigint id PK
bigint tenant_id "default 0"
string uuid UK
bigint recording_id FK "unique (1:1)"
string source "trim/upload"
decimal trim_start_sec "nullable"
decimal trim_end_sec "nullable"
string status "pending/processing/available/failed"
string failure_reason "nullable"
string storage_key
bigint size
decimal duration_sec
}
CHAT_CHANNELS {
bigint id PK
bigint tenant_id "default 0"
string uuid
bigint room_id FK
string visibility "public/private/observer_only"
}
CHAT_MESSAGES {
bigint id PK
bigint tenant_id "default 0"
string uuid
bigint chat_channel_id FK
bigint participant_id FK "nullable"
integer content_type
text text
string sender_ref
string sender_name
}
CHAT_POSTS {
bigint id PK
bigint tenant_id "default 0"
string uuid
bigint chat_channel_id FK
bigint participant_id FK "nullable"
string file
}
ROOM_EVENTS {
bigint id PK
bigint tenant_id "default 0"
bigint room_id FK
string event_type
json payload
}
ENTRY_HISTORIES {
bigint id PK
bigint tenant_id "default 0"
bigint room_id FK
bigint participant_id FK "nullable"
string name
string role
string ip_address
}
WEBHOOK_DELIVERIES {
bigint id PK
bigint tenant_id "default 0"
bigint webhook_endpoint_id FK
string event_id
string event_type
string status "pending/delivered/failed/exhausted"
integer attempt_count
datetime last_attempt_at
integer response_code
}
WEBHOOK_ENDPOINTS {
bigint id PK
bigint tenant_id FK "scope (配信スコープ)"
string uuid UK
bigint user_id FK "nullable (audit)"
string url
string secret
json subscribed_types
boolean enabled
string description
}
API_KEYS {
bigint id PK
bigint tenant_id FK "scope"
string uuid UK
bigint user_id FK "nullable (audit)"
string key_id UK
string secret_hash "平文非保持・ハッシュのみ"
json scopes "任意・MT-ready"
boolean active
datetime rotated_at
}
API_KEY_EVENTS {
bigint id PK
bigint tenant_id "default 0"
bigint api_key_id FK
string event_type "issued/revoked/updated"
bigint user_id FK "nullable (actor)"
string actor_email "CF Access スナップショット"
json payload "digest/rotated_from/reason/changes"
datetime created_at "追記専用 (updated_at なし)"
}
JWKS_KEYS {
bigint id PK
bigint tenant_id FK "scope・基盤鍵は NULL"
string uuid UK
bigint user_id FK "nullable (audit)"
string issuer "JWT iss 値 (interview-platform / tenants.uuid)"
string kid "unique [issuer, kid]"
string alg "EdDSA/RS256"
json public_jwk "JWK (RFC 7517)"
string use "sig"
string private_key_ref "AWS Secrets Manager ARN (秘密鍵は DB 非保持)"
string status "active/retiring/revoked"
datetime expires_at
}
エンティティ定義
各表は全カラムを省略せず記載します。共通の id(内部 BIGINT 主キー・非公開)/ tenant_id(テナントスコープ・PoC は 0 固定)/ uuid(公開識別子・API では id として露出)/ created_at / updated_at も各表に明記しています。高ボリュームのログ系(RoomEvent / EntryHistory / ConnectionTestEvent / WebhookDelivery)は uuid を持たず整数 id のみ、Tenant は自身がテナントのため tenant_id を持ちません。
Room 集約ルート
WebRTC の接続先となるルーム。kind で用途を区別し、参加者・チャット・録画・イベント・入退室を束ねる集約ルート。プロバイダ束縛(livekit_session_id / archive_*)を保持する。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通(PK / テナント / 公開ID) |
kind | string NOT NULL | interview / connection_test。用途の権威 |
status | string NOT NULL default open | open / closed。Room ライフサイクル。統一解放 API(POST /rooms/{id}/close)で closed に。閉室後も配下リソースは参照可 |
livekit_session_id | string NOT NULL | = LiveKit Room SID(RM_…)。unique [livekit_session_id](生値は非公開・診断は派生 URL) |
archive_mode | string | always / manual(録画モード) |
archive_resolution | string default 640x480 | 録画解像度 |
transcription_language | string NOT NULL default ja-JP | 自動文字起こしの既定言語(BCP 47) |
transcription_service | string NOT NULL default azure | 文字起こしエンジン既定(eleven_labs/openai/azure/anyenv⚠/rimo⚠)。kind=interview でのみ有意味。Transcription の provider(実行事実)とは別物 |
stream_resolution | string default 320x240 | publisher 映像解像度 |
stream_frame_rate | integer default 15 | publisher フレームレート(30/15/7/1) |
access_token | string NOT NULL | 入室口(ルーム URL)。unique |
entry_notice_text | text nullable | 入室前画面の注意文言。Project / ExtemporaryFolder 設定からテナントが作成時に渡す入力(archive_mode 等と同じ扱い)。未指定時は SPA 側の既定文言を使用 |
entry_name_field_label | string nullable | 入室前の氏名入力欄のラベル / プレースホルダ。null は SPA 既定ラベルを使用(必須 / 任意制御・非表示化の要否は未確定) |
created_at / updated_at | datetime | 共通 |
関連: has_many recordings / participants / chat_channels / room_events / entry_histories ・ has_one session(0..1) / connection_test(0..1)。media_mode は OpenTok 固有のため持たない。
Session (予約・旧 Slot / ExtemporarySlot の枠部分)
インタビューの予約(時間枠)。Room に従属し、調査ドメインの枠(Slot / ExtemporarySlot)を external_ref で指す。予約のない Room(接続テスト・常設ルーム)は Session を持たない。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通 |
room_id | bigint FK NOT NULL | 従属する Room(1 Room に 0..1 Session) |
external_type | string NOT NULL | 調査側の出所種別(Slot / ExtemporarySlot) |
external_id | string NOT NULL | 調査側 PK(物理 FK なし。unique [tenant_id, external_type, external_id]) |
starts_at | datetime | 枠の開始時刻 |
ends_at | datetime | 枠の終了時刻 |
name | string | 表示名(任意) |
status | string default scheduled | scheduled(開始前)/ active(時間窓内)/ cancelled(テナントによる解放・キャンセル)/ closed(時間窓の自然終了) |
created_at / updated_at | datetime | 共通 |
関連: belongs_to room ・ has_many handouts。
Participant (参加者+役割)
ルームの参加者と役割。テナント委譲の参加者は external_ref、基盤運用者(minedia_observer)は user_id で身元を持つ。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通 |
room_id | bigint FK NOT NULL | 所属 Room |
role | string NOT NULL | moderator / observer / panelist / minedia_observer |
user_id | bigint FK nullable | 基盤運用者の入室時のみ User に紐づく |
external_type / external_id | string | テナント側参加者の identity(物理 FK なし。user_id と排他) |
display_name | string | 表示名スナップショット |
status | string NOT NULL default active | active / kicked / banned(再入室阻止の根拠) |
kicked_at | datetime nullable | 強制退出 / BAN の発生時刻(監査用) |
created_at / updated_at | datetime | 共通 |
関連: belongs_to room ・(任意)user。
Handout (提示物)
インタビュー中に提示する資料(画質保持のため事前アップロード+ブラウザ直表示)。調査側で割付解決済みのリストを Session 配下に登録する。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通 |
session_id | bigint FK NOT NULL | 所属 Session |
external_type / external_id | string | 調査側の出所(重複排除キー・物理 FK なし) |
file | string | アップロードファイル(保存先はテナント設定) |
content_type | string | image / video |
sort | integer default 0 | 提示順 |
created_at / updated_at | datetime | 共通 |
関連: belongs_to session。提示の表示/非表示は RoomEvent(handout.show/handout.hide)。
Recording (録画の事実・旧 OtArchive)
録画の事実のみを保持。文字起こし・要約・加工は別(調査側 or Transcription)。インタビュー / 接続テストとも Room 配下で一様に扱う。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通(uuid は調査側の録画参照キー) |
room_id | bigint FK NOT NULL | 所属 Room(旧 rtc_session_id。unique [room_id, provider_archive_id]) |
provider_archive_id | string NOT NULL | エンジン側の録画 ID(LiveKit=Egress ID / Zoom=recording ID / OpenTok=Archive ID) |
status | string NOT NULL | started / paused / stopped / uploaded / available / expired / failed |
output_mode | string NOT NULL default composed | composed / individual |
size | bigint nullable | ファイルサイズ(bytes・確定は uploaded 以降) |
duration_sec | decimal(10,4) nullable | 録画の長さ(秒・確定は uploaded 以降) |
storage_key | string | S3 キー(非公開・署名 URL で払い出し) |
created_at / updated_at | datetime | 共通 |
関連: belongs_to room ・ has_many transcriptions ・ has_one processed_recording。
ProcessedRecording (加工動画・旧 ProcessedOtArchive)
録画から生成する加工済み動画。トリム指示 API + Workflows によるトリム処理(source=trim)と、加工済み動画の手動アップロード(source=upload・S3 署名付き PUT + complete)の 2 系統。Recording と 1:1(現行 has_one 踏襲・差替えは DELETE → 再 POST)。ブラー / 音声変換の自動化は対象外。録画の外部共有(OtArchiveAccess)は調査側に残置し、閲覧可否はテナント側が判断する。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通(uuid は調査側の参照キー・Recording.uuid と同パターン。unique [uuid]) |
recording_id | bigint FK NOT NULL | 加工元 Recording(コア内部 FK・unique [recording_id]=1:1 の DB 強制) |
source | string NOT NULL | trim(Workflows によるトリム処理)/ upload(手動アップロード) |
trim_start_sec / trim_end_sec | decimal(10,4) nullable | source=trim 時のトリム範囲指定(Workflows への処理指示。upload は null) |
status | string NOT NULL default pending | pending →(processing →)available / failed。trim は Workflows が遷移、upload は署名 PUT 完了後の complete API で available 化 |
failure_reason | string nullable | failed 時の要約理由(W7 processed_recording.status_changed に露出。内部エラー詳細は非公開=Transcription.error_message と同方針) |
storage_key | string | 加工済みファイルの S3 キー(非公開・署名 URL で払い出し。Recording.storage_key と同方針) |
size / duration_sec | bigint / decimal(10,4) | 加工後ファイルのメタデータ |
created_at / updated_at | datetime | 共通 |
関連: belongs_to recording(recordings 1 — 0..1 processed_recordings・recording_id unique)。
個人情報保護加工(ブラー / 音声変換)の依頼管理(要否判断・Slack 依頼・依頼中 / 完了の状態=現行 processed_information の blur / voice_change 記録の後継)は調査ドメインの関心事として minedia-www 側に残置する。コアが持つのは source=trim/upload とファイルの事実のみ。現行 status の unprocessed / started / expired も依頼管理側の概念のため持ち込まない。
Transcription (文字起こし・G5 でコア化)
録画に対する文字起こし。複数言語 / 翻訳がぶら下がる。要約・分析は調査側が uuid 参照で付加する。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通(uuid は調査側の参照キー) |
recording_id | bigint FK NOT NULL | 対象 Recording |
provider | string NOT NULL | eleven_labs / openai / azure / anyenv / rimo |
provider_transcription_id | string | ベンダのジョブ ID(unique [provider, provider_transcription_id]) |
status | string NOT NULL | sending_request / processing / retrying / done / error / request_error |
result_json | json | VTT / json の結果本文(行内格納・As-Is 踏襲。非公開で API は署名 URL result_url 経由で払い出す) |
language | string NOT NULL default ja-JP | 言語(BCP 47) |
kind | string NOT NULL default transcription | transcription(自前文字起こし)/ translation(翻訳・日→英翻訳を統合) |
error_message | text | 失敗時のエラー(非公開・通知は要約のみ) |
created_at / updated_at | datetime | 共通 |
関連: belongs_to recording。
result_json の格納形式(provider 別)
中身は provider ごとに形が異なる(As-Is 踏襲・読み出し時に VTT へ正規化)。現役の作成経路(ElevenLabs / OpenAI / Azure)はいずれも実体が VTT テキストで、単語タイムスタンプ付きの生レスポンスを保持するのはレガシー 2 系統のみ。新基盤コアが書き込むのは現役 3 形式で、anyenv / rimo 形式は旧データを移行する場合にのみ現れる。
| provider | 格納される値 | 読み出し |
|---|---|---|
eleven_labs / openai | VTT テキストそのもの(JSON 値としては文字列) | そのまま VTT |
azure | transcription_status(ジョブ状態)+ vtt(本文)の 2 キーオブジェクト | result_json['vtt'](話者タグ <v 話者 1> 付き) |
anyenv ⚠レガシー | Google Speech 系の生レスポンス(transcript / speech_info / 単語タイムスタンプ words[]) | 表示時に VTT へ都度変換 |
rimo ⚠レガシー | 独自形式(transcripts[].words[]・confidence 付き) | 表示時に VTT へ都度変換 |
例 — ElevenLabs / OpenAI(VTT 文字列)
WEBVTT 00:00:01.200 --> 00:00:04.500 本日はよろしくお願いします。 00:00:04.800 --> 00:00:09.120 まず、普段のお買い物についてお聞かせください。
例 — Azure(ジョブ状態+VTT)
{
"transcription_status": {
"self": "https://japaneast.api.cognitive.microsoft.com/speechtotext/v3.2/transcriptions/8a1f...",
"status": "Succeeded",
"createdDateTime": "2026-07-01T06:10:00Z",
"properties": { "durationInTicks": 36125000000 }
},
"vtt": "WEBVTT\n\n00:00:01.200 --> 00:00:04.500\n<v 話者 1>本日はよろしくお願いします。\n..."
}
例 — anyenv(レガシー・単語タイムスタンプ付き生レスポンス)
{
"name": "operations/xxxx",
"done": true,
"response": {
"results": [{
"alternatives": [{
"transcript": "本日はよろしくお願いします。まず普段のお買い物について…",
"speech_info": { "vowels_num": 1842, "duration": 3612.5, "speed": 6.2, "pitch_ave": 182.4, "intonation": 0.31 },
"words": [
{ "startTime": "1.2s", "endTime": "1.5s", "word": "本日" },
{ "startTime": "1.5s", "endTime": "1.7s", "word": "は" }
]
}]
}]
}
}
例 — Rimo(レガシー・独自形式)
{
"id": "xxxx",
"duration": 3612,
"state": "ASR_DONE",
"transcripts": [{
"id": "t1",
"content": "本日はよろしくお願いします。",
"start_time": 1200,
"end_time": 4500,
"words": [
{ "id": "w1", "content": "本日", "confidence": 0.98, "start_time": 1200, "end_time": 1500 }
]
}]
}
現役 3 形式は実体が VTT で、60〜90 分のインタビューでも数百 KB 級(行内格納で成立するサイズ感)。MB 級に肥大しうるのは単語タイムスタンプを持つレガシー 2 形式のみ。API は生形式を露出せず、result_url(署名 URL)が VTT へ正規化した text/vtt を返す。
ChatChannel (チャットチャネル)
ルーム内のチャットチャネル。可視性で公開範囲を分ける。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通 |
room_id | bigint FK NOT NULL | 所属 Room |
visibility | string NOT NULL | public / private / observer_only(unique [room_id, visibility]) |
created_at / updated_at | datetime | 共通 |
関連: belongs_to room ・ has_many chat_messages / chat_posts。
ChatMessage (メッセージ)
チャネル内の個別メッセージ。送信者は participant 参照+表示用スナップショット。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通 |
chat_channel_id | bigint FK NOT NULL | 所属 ChatChannel |
participant_id | bigint FK nullable | 送信者(system メッセージは null) |
content_type | integer default 1 | text / stamp / archive_status |
text | text | 本文 |
sender_ref | string | テナント不透明文字列の表示用送信者 ID |
sender_name | string NOT NULL | 送信者名スナップショット |
created_at / updated_at | datetime | 共通 |
関連: belongs_to chat_channel ・(任意)participant。
ChatPost (ファイル投稿)
チャネルへのファイルアップロード投稿。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通 |
chat_channel_id | bigint FK NOT NULL | 所属 ChatChannel |
participant_id | bigint FK nullable | 投稿者 |
file | string | アップロードファイル(保存先はテナント設定) |
created_at / updated_at | datetime | 共通 |
関連: belongs_to chat_channel ・(任意)participant。
RoomEvent (イベントログ)
ルーム内で発生したイベント(提示物表示/非表示・再生・リアクション等)の永続ログ。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id | bigint / bigint | 共通(uuid なし・整数 id) |
room_id | bigint FK NOT NULL | 発生した Room |
event_type | string NOT NULL | 例 handout.show / handout.hide(コア正典)・その他は不透明文字列 |
payload | json | イベント本体 |
created_at / updated_at | datetime | 共通 |
関連: belongs_to room。整数 id(uuid なし)。
EntryHistory (入退室ログ)
ルームへの入退室と滞在記録。出欠・課金・録画障害判定の根拠。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id | bigint / bigint | 共通(uuid なし・整数 id) |
room_id | bigint FK NOT NULL | 対象 Room |
participant_id | bigint FK nullable | 入室者(匿名は null) |
name | string NOT NULL | 表示名 |
role | string NOT NULL | moderator / observer / panelist / minedia_observer |
browser_name / browser_version | string | ブラウザ情報 |
platform_name / platform_version | string | プラットフォーム情報 |
ip_address | string | 接続元 IP |
exited_at | datetime nullable | 退室時刻(disconnect webhook で更新) |
duration_sec | integer | 滞在秒数(出欠・課金・障害判定の根拠) |
created_at / updated_at | datetime | 共通(created_at = 入室時刻) |
関連: belongs_to room ・(任意)participant。整数 id(uuid なし)。
ConnectionTest (接続テスト・旧 BrowserTestHistory)
被験者ブラウザ / 回線の品質計測。kind=connection_test の Room を 1 つ持つ。合格判定はテナント側、基盤は計測の事実のみ。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通(uuid は調査側の結果参照キー) |
room_id | bigint FK NOT NULL | kind=connection_test の Room(unique) |
external_type / external_id | string | 被験者 identity(例 {"User","999"}・物理 FK なし) |
status | string NOT NULL default created | created / completed / failed |
dummy_stream_count | integer | ダミー配信数(負荷シミュレーション構成) |
hardware_concurrency | integer | 端末の論理コア数 |
connected_subscriber_count | integer | 接続購読者数 |
| ベースライン計測(baseline) | ||
baseline_video_average_bitrate | float | 映像平均ビットレート |
baseline_video_average_packets_loss_ratio | float | 映像平均パケットロス率 |
baseline_frames_dropped_ratio | float | フレームドロップ率 |
baseline_fps | float | フレームレート |
baseline_audio_bitrate | float | 音声ビットレート |
baseline_audio_packets_loss_ratio | float | 音声パケットロス率 |
baseline_audio_jitter | float | 音声ジッタ |
| 負荷時計測(load) | ||
load_video_average_bitrate | float | 負荷時 映像平均ビットレート |
load_video_average_packets_loss_ratio | float | 負荷時 映像平均パケットロス率 |
load_worst_video_bitrate | float | 負荷時 映像最悪ビットレート |
load_worst_video_packets_loss_ratio | float | 負荷時 映像最悪パケットロス率 |
load_overall_frames_dropped_ratio | float | 負荷時 全体フレームドロップ率 |
load_worst_frames_dropped_ratio | float | 負荷時 最悪フレームドロップ率 |
load_overall_fps | float | 負荷時 全体 FPS |
load_worst_fps | float | 負荷時 最悪 FPS |
load_audio_average_bitrate | float | 負荷時 音声平均ビットレート |
load_audio_average_packets_loss_ratio | float | 負荷時 音声平均パケットロス率 |
load_worst_audio_packets_loss_ratio | float | 負荷時 音声最悪パケットロス率 |
load_audio_average_jitter | float | 負荷時 音声平均ジッタ |
load_worst_audio_jitter | float | 負荷時 音声最悪ジッタ |
総合集計(API summary) | ||
video_average_bitrate / video_median_bitrate | float | 映像ビットレート 平均 / 中央値 |
video_average_packets_loss_ratio / video_median_packets_loss_ratio | float | 映像パケットロス 平均 / 中央値 |
audio_average_bitrate / audio_median_bitrate | float | 音声ビットレート 平均 / 中央値 |
audio_average_packets_loss_ratio / audio_median_packets_loss_ratio | float | 音声パケットロス 平均 / 中央値 |
| 生統計・LiveKit ConnectionCheck | ||
raw_data | json | 生サンプル系列(非公開・集計値を派生で露出) |
checks | json default [] | チェック単位結果 [{type,status,data}](signaling/webrtc/turn/reconnect/publish_audio/publish_video/protocol/region) |
best_protocol | string nullable | UDP/TCP 比較の最適結果(OpenTok=null) |
best_region | string nullable | 最良 Cloud リージョン(非 Cloud/OpenTok=null) |
created_at / updated_at | datetime | 共通 |
関連: belongs_to room ・ has_many connection_test_events ・録画は room 経由。
ConnectionTestEvent (診断ログ・旧 WebrtcHistory)
接続テスト UI の各ステップ・成否の診断ログ。サポート対応・ファネル分析用。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id | bigint / bigint | 共通(uuid なし・整数 id) |
connection_test_id | bigint FK NOT NULL | 親 ConnectionTest |
action_type | string NOT NULL | アクション種別(rtc_session_connected 等・ot_*→中立化) |
check_type | string nullable | どのチェックのログか(signaling/webrtc/turn/… ) |
user_agent | string | UA 文字列 |
browser_name / browser_version | string | ブラウザ情報 |
platform_name / platform_version | string | プラットフォーム情報 |
device_name | string | デバイス名 |
data | json | エラー詳細等 |
created_at / updated_at | datetime | 共通 |
関連: belongs_to connection_test。整数 id(uuid なし)。
Tenant (論理テナント)
論理テナント。PoC では行を作らず tenant_id=0 を予約値として運用(MT は将来サービス複製で対応する伏線)。
| カラム | 型 | 説明 |
|---|---|---|
id / uuid | bigint / string(36) | 共通(tenant_id なし・自身がテナント。unique [uuid]) |
name | string | 表示名(任意) |
created_at / updated_at | datetime | 共通 |
User (基盤運用アカウント台帳・管理プレーン)
基盤自身の運用アクター(管理画面ログイン・録画運用・障害対応)。ルーム参加者(Participant)とは別物。テナント側パネルの個人情報は持たない。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint / string(36) | 共通(unique [email] も) |
email | string NOT NULL | ログイン ID(unique) |
encrypted_password | string NOT NULL | Devise 相当(参加 JWT とは別系統) |
name | string NOT NULL | 表示名(first/last を統合) |
role | string NOT NULL default member | admin / member |
locale | string default ja | ロケール |
time_zone | string default Tokyo | タイムゾーン |
deactivated_at | datetime | 入退社管理(論理無効化・行は消さない) |
| sign_in 系 | — | sign_in_count / current_sign_in_at / failed_attempts / locked_at 等(Devise trackable/lockable・実装時確定) |
created_at / updated_at | datetime | 共通 |
関連: has_many participants(基盤運用者の入室時のみ)。
webhook_endpoints (Webhook 配信先)
基盤 → テナントへの Webhook 配信先設定。用途別に複数登録でき、個別 secret と購読種別を持つ。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint FK / string(36) | 共通。tenant_id は Tenant への FK=**所有・配信スコープ**(event.tenant_id == endpoint.tenant_id) |
user_id | bigint FK nullable | 作成した User(監査・任意) |
url | string NOT NULL | 受信先 URL(minedia-www 側) |
secret | string NOT NULL | endpoint 単位の HMAC 署名シークレット |
subscribed_types | json NOT NULL default ["*"] | 購読イベント種別(["*"]=全種別) |
enabled | boolean NOT NULL default true | 無効化フラグ(secret 回転に使用) |
description | string | 用途メモ(本番/監視/再試験 等) |
created_at / updated_at | datetime | 共通 |
webhook_deliveries (Webhook 配信ログ)
基盤 → テナントへの Webhook 配信試行の記録。署名付き配信・リトライの追跡と、イベント重複排除(冪等性)の根拠。dead-letter 保管(exhausted 後のペイロード永続化)・自動再送・endpoint 自動無効化は v2 スコープ。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id | bigint / bigint | 共通(uuid なし・純ログ) |
webhook_endpoint_id | bigint FK NOT NULL | 配信先 webhook_endpoints(unique [webhook_endpoint_id, event_id] で重複排除) |
event_id | string NOT NULL | Webhook エンベロープの id(冪等性キー) |
event_type | string NOT NULL | 配信イベント種別(例 participant.entered) |
status | string NOT NULL default pending | pending / delivered / failed / exhausted(5 回リトライ失効後) |
attempt_count | integer NOT NULL default 0 | リトライ回数(最大 5) |
last_attempt_at | datetime | 直近の配信試行時刻 |
response_code | integer nullable | 直近レスポンスの HTTP ステータス |
created_at / updated_at | datetime | 共通 |
関連: belongs_to webhook_endpoint(webhook_endpoints 1 — 0..N webhook_deliveries)。整数 id(uuid なし)。配信試行の記録のみを持ち、配信ペイロード本体は保持しない。
jwks_keys (参加 JWT 検証鍵)
参加 JWT(EdDSA 署名)の検証用公開鍵レジストリ。JWKS エンドポイントの裏付け。秘密鍵は DB に置かず Secrets Manager 参照。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint FK nullable / string(36) | 共通。tenant_id は Tenant への FK=**所有スコープ**(Model B のテナント別鍵を区別)。基盤自身の鍵は tenant_id = NULL(tenant 0 = minedia-www の鍵との混同を避ける) |
user_id | bigint FK nullable | 登録した User(監査・任意。鍵ローテは自動の場合あり) |
issuer | string NOT NULL | この鍵が検証する JWT の iss 値(基盤鍵 = interview-platform / テナント鍵 = tenants.uuid)。鍵解決は常に (iss, kid) 複合 |
kid | string NOT NULL | JWT ヘッダ kid(ローテ世代キー・unique [issuer, kid]=テナント間の kid 衝突は許容し発行者内で一意) |
alg | string NOT NULL | 署名アルゴリズム(EdDSA / RS256 等) |
public_jwk | json NOT NULL | 公開鍵(JWK 形式・RFC 7517)。JWKS エンドポイントは issuer=interview-platform かつ status=active の行のみを集約して返す(テナント鍵は保存しても公開しない) |
use | string NOT NULL default sig | 用途(署名検証) |
private_key_ref | string | 秘密鍵の Secrets Manager ARN(DB 非保持・Model B は null) |
status | string NOT NULL default active | active / retiring / revoked |
not_before / expires_at | datetime nullable | 有効期間(ローテ窓) |
created_at / updated_at | datetime | 共通 |
api_keys (テナント認証資格情報)
テナント(他社サーバー)が発行 API を叩くための server-to-server 認証鍵。参加 JWT とは別系統。secret 平文は保持せずハッシュのみ。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id / uuid | bigint / bigint FK / string(36) | 共通。tenant_id は Tenant への FK=**所有スコープ**(API key → tenant 解決) |
user_id | bigint FK nullable | 発行した User(監査・任意。鍵自体はテナントの機械資格情報) |
key_id | string NOT NULL | 公開識別子(非機密・unique) |
secret_hash | string NOT NULL | secret のハッシュ(平文非保持・定数時間比較) |
name / description | string | 用途メモ |
scopes | json | スコープ(任意・MT-ready 予約) |
active | boolean NOT NULL default true | 失効フラグ(回転で旧 key を false) |
last_used_at / rotated_at | datetime nullable | 監査・回転履歴。last_used_at は条件付き間引き更新(60 秒以内は書かない・更新失敗でも認証は成功させる) |
created_at / updated_at | datetime | 共通 |
監査: 物理削除禁止(失効は active=false+revoked イベント。行は消さない=監査帰属を保つ)。発行 / 失効 / メタデータ変更は api_key_events に同一トランザクションで記録する。リクエスト単位の使用状況は構造化ログ(運用検知用)へ出し、DB の使用正本は last_used_at のみ(監査は DB 正本・ログに監査要件を負わせない)。
api_key_events (ApiKey ライフサイクル監査ログ)
api_keys の発行 / 失効 / メタデータ変更の監査ログ。追記専用(updated_at なし・アプリ層で UPDATE / DELETE を持たない)。汎用 audit_logs の決着を待たない狭い専用テーブル(webhook_deliveries と同じ前例)。
| カラム | 型 | 説明 |
|---|---|---|
id / tenant_id | bigint / bigint | 共通(uuid なし・純ログ)。tenant_id は対象 api_keys.tenant_id と一致(アプリ層整合) |
api_key_id | bigint FK NOT NULL | 対象 api_keys |
event_type | string NOT NULL | issued / revoked / updated(name・description 等の変更)。回転は独立イベントにせず、新キー issued+旧キー revoked の 2 イベントで表現 |
user_id | bigint FK nullable | 操作した User(Cloudflare Access のメールを users.email に解決できた場合のみ。認可の実体は CF Access ポリシーで、この FK は帰属記録) |
actor_email | string NOT NULL | CF Access JWT のメールのスナップショット(小文字化・trim 済み)。users 未登録でも監査証跡が常に残る |
payload | json | issued: secret_hash_digest(SHA-256(secret_hash)・改ざん照合の基準値)+rotated_from_api_key_id(回転時のみ)/revoked: reason(推奨値 rotation / compromised / unused)/updated: 変更フィールドの before / after。secret_hash / active / key_id / tenant_id は changes に現れないのが不変条件 |
created_at | datetime | 追記専用(updated_at なし) |
関連: belongs_to api_key(api_keys 1 — 0..N api_key_events)。整数 id(uuid なし)。
完全性監査(イベント突合型検知): 本テーブルを「正」とし、定期照合(Cron・1 時間毎)で api_keys の行状態と突合する — ① issued イベントを持たない行=不正挿入 ② secret_hash ダイジェスト不一致=改ざん ③ revoked 済みなのに active=true=不正再有効化。検知は運用チャンネルへ直接通知。イベントは書き込み時に構造化ログへも複写され(DB 外のアンカー)、DB 内で両テーブルを整合的に改ざんされても事後検知できる。DB ロール分離による完全な改ざん耐性は v2(外販 / マルチテナント時)。