# 非機能要件定義書

## 概要

本ドキュメントは、Chatwootプロジェクトのコードベース解析に基づいて作成された非機能要件定義書です。
Chatwootは、オープンソースのカスタマーサポートプラットフォームであり、Ruby on Rails + Vue.jsで構築されています。

## 性能要件

### レスポンスタイム

| 処理種別 | 目標値 | 備考 |
| --- | --- | --- |
| Webリクエスト | 14秒以内 | PostgreSQL statement_timeout設定（`config/database.yml`） |
| API応答 | Rack::Timeoutによるタイムアウト制御 | デフォルトタイムアウト適用（`config/initializers/rack_timeout.rb`） |
| Redis接続 | 1秒（タイムアウト） | ConnectionPoolのtimeout設定（`config/initializers/01_redis.rb`） |
| SMTP接続 | 環境変数で設定可能 | `SMTP_OPEN_TIMEOUT`、`SMTP_READ_TIMEOUT`（`config/initializers/mailer.rb`） |

### スループット

| 項目 | 目標値 | 備考 |
| --- | --- | --- |
| Pumaスレッド数 | 最小/最大: 環境変数`RAILS_MIN_THREADS`/`RAILS_MAX_THREADS`（デフォルト: 5） | `config/puma.rb` |
| Pumaワーカー数 | 環境変数`WEB_CONCURRENCY`（デフォルト: 0） | クラスターモード対応 |
| Sidekiq並行処理数 | 環境変数`SIDEKIQ_CONCURRENCY`（デフォルト: 10） | `config/sidekiq.yml` |
| Redis Alfred接続プールサイズ | 環境変数`REDIS_ALFRED_SIZE`（デフォルト: 5） | Round Robin、会話メール、オンラインプレゼンス用 |
| Redis Velma接続プールサイズ | 5 | Rack Attack用 |
| DBコネクションプール | Sidekiq: `SIDEKIQ_CONCURRENCY` / Web: `RAILS_MAX_THREADS` | `config/database.yml` |

### レートリミット

| API/エンドポイント | 制限値 | 期間 | 備考 |
| --- | --- | --- | --- |
| 全リクエスト（IP毎） | 3000（環境変数`RACK_ATTACK_LIMIT`で変更可能） | 1分 | グローバルスロットリング |
| Super Admin ログイン（IP毎） | 5 | 5分 | ブルートフォース攻撃対策 |
| Super Admin ログイン（メール毎） | 5 | 15分 | ブルートフォース攻撃対策 |
| 一般ログイン（IP毎） | 5 | 5分 | MFA検証リクエストは除外 |
| 一般ログイン（メール毎） | 10 | 15分 | MFA検証リクエストは除外 |
| パスワードリセット（IP毎） | 5 | 30分 | アカウント保護 |
| パスワードリセット（メール毎） | 5 | 1時間 | アカウント保護 |
| 確認メール再送信（IP毎） | 5 | 30分 | スパム防止 |
| MFA検証（IP毎） | 5 | 1分 | ブルートフォース攻撃対策 |
| MFAログイン（IP/トークン毎） | 10 | 1分 | ブルートフォース攻撃対策 |
| アカウント作成（IP毎） | 5 | 30分 | スパム防止 |
| Widgetコンバーセーション作成（IP毎） | 6 | 12時間 | 濫用防止（環境変数で無効化可能） |
| Widgetコンタクト更新（IP毎） | 60 | 1時間 | 濫用防止 |
| 会話トランスクリプト取得 | 30 | 1時間 | アカウント毎 |
| 添付ファイルアップロード | 60 | 1時間 | アカウント毎 |
| コンタクト検索 | 100（環境変数`RATE_LIMIT_CONTACT_SEARCH`で変更可能） | 1分 | アカウント毎 |
| レポートAPI（ユーザー毎） | 100（環境変数`RATE_LIMIT_REPORTS_API_USER_LEVEL`で変更可能） | 1分 | ユーザー+アカウント毎 |
| レポートAPI（アカウント毎） | 1000（環境変数`RATE_LIMIT_REPORTS_API_ACCOUNT_LEVEL`で変更可能） | 1分 | アカウント毎 |

## 可用性要件

| 項目 | 目標値 | 備考 |
| --- | --- | --- |
| ヘルスチェックエンドポイント | `/health` | `HealthController`で提供、Rack Attackから除外 |
| Redis再接続試行回数 | 2回 | `lib/redis/config.rb` |
| DBプールリーパー頻度 | 30秒（環境変数`DB_POOL_REAPING_FREQUENCY`で変更可能） | デッドスレッドからの接続回収 |
| Redis Sentinel対応 | 環境変数`REDIS_SENTINELS`で設定可能 | 高可用性Redis構成をサポート |
| Sidekiq最大リトライ | 3回 | `config/sidekiq.yml` |
| Sidekiqジョブタイムアウト | 25秒 | `config/sidekiq.yml` |

### ジョブリトライ設定

| ジョブ種別 | リトライ設定 | 待機時間 | 試行回数 |
| --- | --- | --- | --- |
| WebhookJob（Instagram/Facebook/Slack等） | LockAcquisitionError | 1-3秒 | 3-8回 |
| TiktokEventsJob | LockAcquisitionError | 2秒 | 8回 |
| DataImportJob | ActiveStorage::FileNotFoundError | 1分 | 3回 |
| 全ジョブ共通 | ActiveJob::DeserializationError | 破棄（discard） | - |

## セキュリティ要件

### 認証・認可

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| パスワード長 | 6-128文字 | `config/initializers/devise.rb` |
| パスワード複雑性要件 | 大文字1文字以上、小文字1文字以上、数字1文字以上、特殊文字1文字以上 | `config/initializers/secure_password.rb` |
| パスワードハッシュ化 | bcrypt（stretches: 11） | テスト環境は1 |
| パスワードリセット有効期限 | 6時間 | `config/initializers/devise.rb` |
| メール再確認 | 有効 | メールアドレス変更時 |
| ログアウト時Rememberトークン無効化 | 有効 | 全トークン無効化 |
| MFA/2FA | Active Record暗号化が設定されている場合に有効 | 暗号化キー必須 |

### セッション管理

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| セッションストア | Cookie Store | `_chatwoot_session` |
| SameSite属性 | Lax | CSRF対策 |
| HTTPのみクッキー | 有効（デフォルト） | XSS対策 |

### データ保護

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| ログフィルタリング | password, secret, _key, auth, crypt, salt, certificate, otp, access, private, protected, ssn, otp_secret, otp_code, backup_code, mfa_token, otp_backup_codes, token（website_token除く） | `config/initializers/filter_parameter_logging.rb` |
| Active Record暗号化 | 環境変数で設定可能 | `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY`等 |
| SSL/TLS強制 | 環境変数`FORCE_SSL`で設定可能 | 本番環境で推奨 |
| Redis SSL検証 | デフォルトでVERIFY_PEER | 環境変数`REDIS_OPENSSL_VERIFY_MODE`で変更可能 |

### CORS設定

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| /packs/*, /audio/* | 全オリジン許可、GET/OPTIONSのみ | CDN対応 |
| /public/api/* | 全オリジン許可、全メソッド | 公開API |
| /api/* | 環境変数`ENABLE_API_CORS`で有効化可能 | access-token, client, uid, expiry公開 |
| Action Cable | CSRF保護無効 | WebSocket接続対応 |

### OAuth/SSO

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| Google OAuth2 | 環境変数で設定可能 | `GOOGLE_OAUTH_CLIENT_ID`, `GOOGLE_OAUTH_CLIENT_SECRET` |
| SAML認証 | Enterprise版で対応 | `enterprise/config/initializers/omniauth_saml.rb` |

## 拡張性要件

### 水平スケーリング

| コンポーネント | スケーリング方式 | 備考 |
| --- | --- | --- |
| Webサーバー（Puma） | ワーカー数（`WEB_CONCURRENCY`）増加 | preload_app!でメモリ効率化 |
| バックグラウンドジョブ（Sidekiq） | 並行処理数（`SIDEKIQ_CONCURRENCY`）増加 | キュー優先度設定あり |
| データベース | PostgreSQL（接続プール自動調整） | Sidekiqとウェブで分離 |
| キャッシュ/セッション | Redis | Sentinel対応で冗長化可能 |
| 検索エンジン | OpenSearch/Elasticsearch | AWS対応、非同期インデックス更新 |

### Sidekiqキュー構成

| キュー名 | 優先度 | 用途 |
| --- | --- | --- |
| critical | 最高 | 重要なジョブ |
| high | 高 | 高優先度ジョブ |
| medium | 中 | 中優先度ジョブ |
| default | 中 | デフォルトジョブ |
| mailers | 中 | メール送信 |
| action_mailbox_routing | 中 | 受信メールルーティング |
| low | 低 | 低優先度ジョブ |
| scheduled_jobs | 低 | スケジュールジョブ |
| deferred | 低 | 遅延ジョブ |
| purgable | 低 | パージ可能ジョブ |
| housekeeping | 低 | ハウスキーピング |
| async_database_migration | 低 | DB非同期マイグレーション |
| bulk_reindex_low | 最低 | 一括再インデックス |
| active_storage_* | 低 | ファイルストレージ |
| action_mailbox_incineration | 最低 | メール削除 |

### ストレージ

| プロバイダー | 設定方法 | 備考 |
| --- | --- | --- |
| ローカルディスク | デフォルト | 開発/テスト環境向け |
| Amazon S3 | 環境変数 | `AWS_ACCESS_KEY_ID`等 |
| Google Cloud Storage | 環境変数 | `GCS_PROJECT`等 |
| Azure Blob Storage | 環境変数 | `AZURE_STORAGE_ACCOUNT_NAME`等 |
| S3互換（MinIO, DigitalOcean Spaces等） | 環境変数 | `STORAGE_ENDPOINT`等 |

## 保守性要件

### コード品質

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| Ruby行長制限 | 150文字 | RuboCopルール |
| ESLintルールセット | Airbnb base + Vue 3 recommended | JavaScript/Vue |
| Vueコンポーネント命名 | PascalCase | 規約 |
| i18n | 必須（バックエンド: en.yml、フロントエンド: en.json） | 多言語対応 |

### 監査ログ

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| 監査ログクラス | `Enterprise::AuditLog` | `config/initializers/audited.rb` |
| 対象 | Enterprise版機能 | 監査証跡 |

### デプロイメント

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| Rubyバージョン | 3.4.4 | `Dockerfile` |
| Node.jsバージョン | 24.x | `Dockerfile` |
| パッケージマネージャ | pnpm 10.2.0 | `Dockerfile` |
| コンテナベースイメージ | ruby:3.4.4-alpine3.21 | 軽量イメージ |

## 運用・監視要件

### APM/モニタリング統合

| サービス | 設定方法 | 備考 |
| --- | --- | --- |
| Datadog | 環境変数`DD_TRACE_AGENT_URL` | トレーシング |
| Elastic APM | 環境変数`ELASTIC_APM_SECRET_TOKEN` | パフォーマンス監視 |
| Scout APM | 環境変数`SCOUT_KEY` | パフォーマンス監視 |
| New Relic | 環境変数`NEW_RELIC_LICENSE_KEY` | Sidekiqメトリクス含む |
| Sentry | 環境変数`SENTRY_DSN` | エラー監視、サンプリング10% |

### ログ管理

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| ログレベル | 環境変数`LOG_LEVEL`（デフォルト: info） | 本番環境 |
| ログフォーマット | JSON（Sidekiq本番環境） | 構造化ログ |
| ログ出力先 | STDOUT（環境変数`RAILS_LOG_TO_STDOUT`）またはファイル | 切替可能 |
| ログローテーション | 1ファイル、環境変数`LOG_SIZE`（デフォルト: 1024MB） | ファイル出力時 |
| Lograge | 環境変数`LOGRAGE_ENABLED`で有効化 | 構造化ログ、JSON形式 |
| リクエストID | タグ付け有効 | トレーサビリティ |

### Sentry設定

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| 有効環境 | staging, production | 開発環境は除外 |
| トレースサンプリング | 10%（環境変数`ENABLE_SENTRY_TRANSACTIONS`で有効） | パフォーマンス監視 |
| 除外例外 | Rack::Timeout::RequestTimeoutException | ノイズ削減 |
| PII送信 | デフォルト有効（環境変数`DISABLE_SENTRY_PII`で無効化可能） | デバッグ用 |

### スケジュールジョブ

| ジョブ名 | スケジュール | 用途 |
| --- | --- | --- |
| CheckNewVersionsJob | 毎日12:00 | バージョン確認 |
| TriggerScheduledItemsJob | 5分毎 | スケジュールアイテム実行 |
| FetchImapEmailInboxesJob | 1分毎 | IMAPメール取得 |
| RemoveStaleContactInboxesJob | 毎日22:30 UTC | 古いコンタクトインボックス削除 |
| RemoveStaleRedisKeysJob | 毎日22:30 UTC | 古いRedisキー削除 |
| ProcessStaleContactsJob | 毎日04:30 UTC | 古いコンタクト処理 |
| DeleteAccountsJob | 毎日01:00 UTC | 削除マーク付きアカウント削除 |
| BulkAutoAssignmentJob | 15分毎 | 自動アサイン |
| PeriodicAssignmentJob | 30分毎 | 定期アサイン（v2） |
| RemoveOldNotificationJob | 毎日22:30 UTC | 古い通知削除（1ヶ月以上、300件/ユーザーに制限） |
| RemoveOrphanConversationsJob | 12時間毎 | 孤立会話削除 |

### オートスケーリング

| サービス | 設定方法 | 備考 |
| --- | --- | --- |
| Judoscale（Heroku） | 環境変数`JUDOSCALE_URL` | Rails/Sidekiqオートスケール |

## 備考

### 環境変数一覧（主要なもの）

| カテゴリ | 環境変数 | デフォルト値 | 説明 |
| --- | --- | --- | --- |
| パフォーマンス | `RAILS_MAX_THREADS` | 5 | Pumaスレッド数 |
| パフォーマンス | `WEB_CONCURRENCY` | 0 | Pumaワーカー数 |
| パフォーマンス | `SIDEKIQ_CONCURRENCY` | 10 | Sidekiq並行処理数 |
| パフォーマンス | `REDIS_ALFRED_SIZE` | 5 | Redis Alfred接続プールサイズ |
| パフォーマンス | `POSTGRES_STATEMENT_TIMEOUT` | 14s | DBクエリタイムアウト |
| パフォーマンス | `DB_POOL_REAPING_FREQUENCY` | 30 | DBプールリーパー頻度（秒） |
| セキュリティ | `FORCE_SSL` | false | SSL強制 |
| セキュリティ | `ENABLE_RACK_ATTACK` | true | レートリミット有効化 |
| セキュリティ | `RACK_ATTACK_LIMIT` | 3000 | グローバルレートリミット |
| セキュリティ | `RACK_ATTACK_ALLOWED_IPS` | - | レートリミット除外IP |
| セキュリティ | `ENABLE_RACK_ATTACK_WIDGET_API` | true | WidgetAPIレートリミット |
| セキュリティ | `ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY` | - | AR暗号化主キー |
| 監視 | `LOG_LEVEL` | info | ログレベル |
| 監視 | `RAILS_LOG_TO_STDOUT` | true | STDOUT出力 |
| 監視 | `LOGRAGE_ENABLED` | false | Lograge有効化 |
| 監視 | `SENTRY_DSN` | - | Sentry DSN |
| 監視 | `DD_TRACE_AGENT_URL` | - | Datadog Agent URL |

### 制限事項

1. CSP（Content Security Policy）はデフォルトでコメントアウトされており、必要に応じて有効化が必要
2. Rack::Timeoutのデフォルトタイムアウト値は設定ファイルで明示されていない（gem デフォルト使用）
3. Widget APIのレートリミットは環境変数で無効化可能だが、セキュリティ上推奨されない
4. MFA/2FAを利用するにはActive Record暗号化キーの設定が必須
