> ## Documentation Index
> Fetch the complete documentation index at: https://docs.iru.com/llms.txt
> Use this file to discover all available pages before exploring further.

# マルチテナント分離

> Iru がすべての組織のデータを分離し、行レベルセキュリティによってデータベースエンジンで強制し、すべての変更を改ざん検知可能にする方法。

Iru Identity はマルチテナントです。多数の組織が同じサービスを共有しています。この設計の最も重要な特性は、**あるテナントが別のテナントのデータを見たり触れたりすることは決してできない**ことです。Iru はこれをアプリケーションコードだけでなくデータベースエンジン自体で強制するため、クエリのバグでさえ境界を越えることはできません。

## データベースによって強制される分離

テナントデータを保持するすべてのテーブルには `tenant_id` があり、**PostgreSQL の行レベルセキュリティ (RLS)** によって保護されています。

* テーブルでは RLS が**有効化かつ強制**されている (`FORCE ROW LEVEL SECURITY`) ため、ポリシーはすべてのロールに適用されます。それを回避する特権的な経路はありません。
* 単一のポリシーが読み取りと書き込みの両方を管理します。行は、その `tenant_id` が現在のリクエストのテナントと一致する場合にのみ表示 (`USING`) および書き込み (`WITH CHECK`) が可能です。

```sql theme={null}
-- The shape every tenant table shares
ALTER TABLE app.<table> ENABLE ROW LEVEL SECURITY;
ALTER TABLE app.<table> FORCE  ROW LEVEL SECURITY;

CREATE POLICY tenant_access_policy ON app.<table> FOR ALL
  USING      (tenant_id = current_setting('iru.tenant_id'))
  WITH CHECK (tenant_id = current_setting('iru.tenant_id'));
```

これは、一部の特権テーブルだけでなく、一律かつ大規模に適用されています (**数百のテーブル**、それぞれに強制 RLS と同一の読み取り/書き込みポリシー)。

<Note>
  境界はデータベースに存在するため、テナントによるフィルタリングを忘れたクエリは、別のテナントから何も返しません。エンジンは、アクティブなテナント以外の行の表示や変更を拒否します。
</Note>

## テナントコンテキストはすべてのリクエストとともに伝わる

```mermaid theme={null}
flowchart LR
  req["tenant.subdomain への<br/>リクエスト"] --> mw["テナントを解決<br/>(不明な場合は拒否)"]
  mw --> tx["DB トランザクションを開始"]
  tx --> setl["SET LOCAL iru.tenant_id = …<br/>+ 実行者 + 相関 ID"]
  setl --> rls["RLS ポリシーが<br/>同じ設定を読み取る"]
  rls --> data[("このテナントの行のみ")]
```

1. テナントは、ミドルウェアでリクエストのサブドメインから解決されます。解決できない場合、リクエストは**ハンドラーに到達する前に拒否されます**。
2. データベーストランザクションごとに、Iru はテナント (実行ユーザーと相関/トレース ID とともに) を**トランザクションスコープ**の設定 (`SET LOCAL`) として設定します。トランザクションにスコープされているため、プールされた接続を再利用する次のリクエストに漏れることはありません。
3. RLS ポリシーはまさにその設定を読み取るため、セキュリティ境界はアプリケーションが設定したのと同じ値であり、すべての接続でトランザクション的に確立されます。

## 最小権限により監査履歴は書き換えられない

アプリケーションが実行されるデータベースロールは**ライブ**データの読み取りと書き込みができますが、**監査スキーマ**には**読み取り**アクセスのみを保持します。万一アプリケーションの SQL が悪用されたとしても、監査履歴を変更または削除する権限はありません。証跡はアプリケーションロジックとは独立して、権限レイヤーで保護されています。

参照整合性もテナントスコープです。外部キーは複合 (`tenant_id` + id) であるため、レコードは**同じテナント内**の別のレコードのみを参照できます。

## すべての変更はジャーナルに記録される (追記専用)

Iru は、テナントデータへの変更の完全で改ざん検知可能な履歴を保持します。

<AccordionGroup>
  <Accordion title="取得される内容" icon="clipboard-list">
    すべての変更可能なテーブルには、挿入、更新、削除時にデータベーストリガーによって書き込まれるミラーの**監査 (ジャーナル) テーブル**があります。各エントリには、**何が**起こったか (アクション)、**誰が** (実行したデータベースプリンシパルと認証されたユーザーの両方)、**いつ**、発生元のリクエストに結び付ける**相関/トレース ID**、および行の**変更前後の完全な状態**が記録されます。
  </Accordion>

  <Accordion title="追記専用である理由" icon="lock">
    証跡は 3 つの独立した方法で保護されています。トリガーはジャーナル行を*挿入*するだけであること、アプリケーションのデータベースロールは監査スキーマに**読み取り専用**アクセスを持つこと、およびトリガーは定義者権限で実行されることです。アプリケーションが監査レコードを変更または消去する経路はありません。
  </Accordion>

  <Accordion title="黙って乖離することはない" icon="circle-check">
    常設の整合性チェックが、すべてのライブテーブルの列を監査テーブルと比較し、列が欠落している場合は失敗するため、新しいフィールドが監査証跡から密かに漏れることはありません。スキーママイグレーション自体もバージョン管理され、チェックサムで検証され、ジャーナルに記録されます。
  </Accordion>
</AccordionGroup>

## 機密フィールドには追加のレイヤー

分離に加えて、特に機密性の高い値は、保存前に**フィールドレベルで AES-256-GCM** (認証付き暗号化) で**暗号化**され、アプリケーションが読み取るときにのみ復号されます。暗号化キーは、コードやデータベースからではなく、マネージドシークレットストアから実行時に提供されます。

## 全体像との関係

分離は**リージョンのデータレジデンシー**と連携して機能します。各リージョンには独自のデータベースがあり、テナントのクエリはそのリージョンのデータベースに対してのみ実行されます ([プラットフォームアーキテクチャ](/ja/identity/security/platform-architecture)を参照)。そのデータベース内では、RLS が各テナントを分離します。

<CardGroup cols={2}>
  <Card title="プラットフォームアーキテクチャ" icon="server" href="/ja/identity/security/platform-architecture">
    この分離が実行されるリージョン、プレーン、インフラストラクチャ。
  </Card>

  <Card title="認証アーキテクチャ" icon="signature" href="/ja/identity/security/authentication-architecture">
    サインインと API リクエストをフィッシング耐性と改ざん防止を備えたものにする方法。
  </Card>

  <Card title="アクティビティログ" icon="list-timeline" href="/ja/identity/administration/activity-log">
    管理者が確認できるよう表示される監査証跡。
  </Card>

  <Card title="セキュリティとプライバシー" icon="lock" href="/ja/identity/security/security-and-privacy">
    これらのメカニズムがもたらす保証。
  </Card>
</CardGroup>
