# 機能設計書 67-シークレット追加

## 概要

本ドキュメントは、Giselleシステムにおける「シークレット追加」機能の設計仕様を定義する。

### 本機能の処理概要

シークレット追加機能は、ワークスペースに機密情報（APIキー、認証トークン等）を安全に保存する機能である。シークレットは暗号化されてストレージに保存され、ワークスペースインデックスにも登録される。

**業務上の目的・背景**：AIワークフローでは、外部APIへのアクセスに認証情報が必要となる場合がある。本機能により、ユーザーはAPIキーやトークンなどの機密情報を安全に保存し、ワークフロー実行時に参照できるようになる。暗号化により、ストレージ上でも機密情報が保護される。

**機能の利用シーン**：
- ワークフローエディタ画面でのシークレット追加
- APIキー管理画面でのチームAPIキー作成
- 外部サービス連携時の認証情報の登録

**主要な処理内容**：
1. 入力としてラベル、値、オプションでワークスペースID、タグを受け取る
2. Vaultを使用して値を暗号化
3. Secretオブジェクトを作成（ID生成、タイムスタンプ設定）
4. ストレージにシークレットを保存
5. ワークスペースIDがある場合、ワークスペースインデックスにも追加

**関連システム・外部連携**：
- `@giselles-ai/vault`パッケージによる暗号化
- `@giselles-ai/storage`パッケージによるストレージ抽象化レイヤー

**権限による制御**：本機能自体には認証・認可チェックは含まれない。呼び出し元のAPIレイヤーにて適切な認可制御が行われることを前提とする。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 17 | ワークフローエディタ画面 | API連携 | ワークスペースへのシークレット追加処理 |
| 29 | APIキー管理画面 | 主機能 | チームのAPIキー作成・管理処理 |
| 40 | APIキー管理画面（リダイレクト） | 遷移先機能 | APIキー管理画面へのリダイレクト処理 |

## 機能種別

データ登録（CREATE）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| context | `GiselleContext` | Yes | Giselleコンテキスト | - |
| label | `string` | Yes | シークレットの表示名 | - |
| value | `string` | Yes | 暗号化前のシークレット値 | - |
| workspaceId | `WorkspaceId` | No | 関連付けるワークスペースID | `wks_`プレフィックスで始まる文字列 |
| tags | `string[]` | No | シークレットの分類タグ | - |

### 入力データソース

- ユーザー入力（画面からのフォーム入力）
- ワークスペースIDはセッションコンテキストから取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | `SecretId` | 生成されたシークレットID |
| label | `string` | シークレットの表示名 |
| value | `string` | 暗号化されたシークレット値 |
| createdAt | `number` | 作成日時（ミリ秒タイムスタンプ） |
| workspaceId | `WorkspaceId` | （オプション）関連ワークスペースID |
| tags | `string[]` | （オプション）分類タグ |

### 出力先

- ストレージにシークレットを保存
- ワークスペースインデックスに追加（ワークスペースIDがある場合）
- 呼び出し元への戻り値として返却

## 処理フロー

### 処理シーケンス

```
1. 値の暗号化
   └─ context.vault.encrypt()で値を暗号化

2. Secretオブジェクト作成
   └─ SecretId.generate()でID生成
   └─ label, 暗号化値, createdAt, workspaceId, tagsを設定

3. ストレージに保存
   └─ workspaceIdがある場合: Promise.allで並列実行
      ├─ storage.setJson()でシークレット本体を保存
      └─ addWorkspaceIndexItem()でインデックスに追加
   └─ workspaceIdがない場合: storage.setJson()のみ実行

4. 結果返却
   └─ 作成したSecretオブジェクトを返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[vault.encryptで値を暗号化]
    B --> C[SecretId生成]
    C --> D[Secretオブジェクト作成]
    D --> E{workspaceIdあり?}
    E -->|Yes| F[Promise.allで並列保存]
    F --> F1[storage.setJson シークレット保存]
    F --> F2[addWorkspaceIndexItem インデックス追加]
    F1 --> G[Secretオブジェクト返却]
    F2 --> G
    E -->|No| H[storage.setJson シークレット保存のみ]
    H --> G
    G --> I[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-67-01 | 暗号化必須 | シークレット値は必ずVaultで暗号化されて保存される | 常時 |
| BR-67-02 | ID生成 | SecretIdは`scrt_`プレフィックスで自動生成される | 常時 |
| BR-67-03 | タイムスタンプ | createdAtにはDate.now()のミリ秒タイムスタンプが設定される | 常時 |
| BR-67-04 | インデックス登録 | workspaceIdがある場合のみワークスペースインデックスに登録 | workspaceId指定時 |
| BR-67-05 | グローバルシークレット | workspaceIdを指定しない場合、グローバルシークレットとして保存 | workspaceId未指定時 |

### 計算ロジック

本機能に特別な計算ロジックは存在しない。

## データベース操作仕様

### 操作別データベース影響一覧

本機能はBlobストレージのみを使用し、リレーショナルデータベースへの直接アクセスは行わない。

| 操作 | 対象ストレージ | 操作種別 | 概要 |
|-----|-------------|---------|------|
| シークレット保存 | Blob Storage | SET | シークレット本体をJSONとして保存 |
| インデックス追加 | Blob Storage | SET | ワークスペースシークレットインデックスを更新 |

### ストレージ操作詳細

#### Blobストレージ

| 操作 | パス | データ形式 | 備考 |
|-----|------|----------|------|
| SET | secrets/{secretId}/secret.json | JSON | Secret構造体 |
| SET | secrets/byWorkspace/{workspaceId}.json | JSON | SecretIndex配列（ワークスペースID指定時のみ） |

#### Secret構造体

```typescript
{
  id: SecretId;          // シークレットID（scrt_xxx）
  label: string;         // 表示名
  value: string;         // 暗号化された値
  createdAt: number;     // 作成日時（ミリ秒）
  workspaceId?: string;  // ワークスペースID（オプション）
  tags?: string[];       // 分類タグ（オプション）
}
```

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | VaultError | 暗号化処理の失敗 | Vault設定を確認 |
| - | StorageError | ストレージへの書き込み失敗 | ストレージ接続を確認 |

### リトライ仕様

ストレージアクセス失敗時のリトライは、ストレージドライバ層で実装される。アプリケーション層でのリトライは行わない。

## トランザクション仕様

シークレット本体とインデックスの保存はPromise.allで並列実行されるが、アトミックではない。一方が失敗した場合、もう一方はロールバックされない。

## パフォーマンス要件

- レスポンス時間: 200ms以内（暗号化処理とストレージ書き込みを含む）
- 同時リクエスト: ストレージの制限に依存

## セキュリティ考慮事項

- 認証・認可: API呼び出し元で実装（本機能では未実装）
- データ保護: Vaultによる暗号化 + ストレージレベルでの暗号化
- 監査ログ: シークレット作成のログ記録を推奨

## 備考

- シークレット値は暗号化されてストレージに保存される
- 復号化はシークレット使用時に行われる
- インデックスには値（value）は含まれない

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、シークレット関連の型定義を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | secret/index.ts | `packages/protocol/src/secret/index.ts` | Secret, SecretId, SecretIndexの型定義 |

**読解のコツ**:
- **5行目**: `SecretId = createIdGenerator("scrt")`で`scrt_`プレフィックス
- **8-15行目**: Secretスキーマ定義（id, label, value, createdAt, workspaceId?, tags?）
- **19-26行目**: SecretIndexはSecretから一部フィールドをpick

#### Step 2: エントリーポイントを理解する

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | add-secret.ts | `packages/giselle/src/secrets/add-secret.ts` | addSecret関数の実装 |

**主要処理フロー**:
1. **11-23行目**: 関数シグネチャと引数定義
2. **24行目**: `context.vault.encrypt(value)`で値を暗号化
3. **26-33行目**: Secretオブジェクトを作成
4. **35-48行目**: workspaceIdがある場合、Promise.allでシークレット保存とインデックス追加を並列実行
5. **49-54行目**: workspaceIdがない場合、シークレット保存のみ
6. **57行目**: 作成したSecretを返却

#### Step 3: ユーティリティ関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | paths.ts | `packages/giselle/src/secrets/paths.ts` | secretPath, workspaceSecretIndexPathの定義 |
| 3-2 | workspace-index.ts | `packages/giselle/src/utils/workspace-index.ts` | addWorkspaceIndexItemの実装 |

**主要処理フロー**:
- **3-5行目（paths.ts）**: `secrets/{secretId}/secret.json`パスを生成
- **7-9行目（paths.ts）**: `secrets/byWorkspace/{workspaceId}.json`パスを生成
- **4-30行目（workspace-index.ts）**: インデックスの存在確認→取得→追加→保存

#### Step 4: ストレージ抽象化を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | types.ts | `packages/storage/src/types.ts` | GiselleStorageインターフェースのsetJsonメソッド |

**主要処理フロー**:
- **10-14行目**: SetJsonParams型定義（path, schema?, data）
- **67行目**: GiselleStorageインターフェースにsetJsonが含まれる

### プログラム呼び出し階層図

```
Giselle.addSecret (giselle.ts:319-325)
    │
    └─ addSecret (add-secret.ts:11-58)
           │
           ├─ context.vault.encrypt(value)
           │
           ├─ SecretId.generate()
           │
           ├─ secretPath(secretId) (paths.ts:3-5)
           │
           ├─ context.storage.setJson({path, data, schema})
           │
           └─ addWorkspaceIndexItem() (workspace-index.ts:4-30)
                  │
                  ├─ context.storage.exists(indexPath)
                  │
                  ├─ context.storage.getJson({path, schema})
                  │
                  └─ context.storage.setJson({path, data})
```

### データフロー図

```
[入力]                          [処理]                              [出力]

label ──────────┐
                │
value ──────────┼──▶ vault.encrypt(value) ───▶ encryptedValue
                │
workspaceId? ───┼──▶ SecretId.generate() ───▶ secretId
                │
tags? ──────────┘          │
                           ▼
                   Create Secret Object
                   {id, label, value, createdAt, ...}
                           │
                           ▼
               ┌───────────┴───────────┐
               ▼                       ▼
        storage.setJson           addWorkspaceIndexItem
        (secret.json)             (byWorkspace/{wksId}.json)
               │                       │
               └───────────┬───────────┘
                           ▼
                    Return Secret
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| add-secret.ts | `packages/giselle/src/secrets/add-secret.ts` | ソース | メイン処理の実装 |
| index.ts | `packages/protocol/src/secret/index.ts` | ソース | Secret型定義 |
| paths.ts | `packages/giselle/src/secrets/paths.ts` | ソース | パス生成ユーティリティ |
| workspace-index.ts | `packages/giselle/src/utils/workspace-index.ts` | ソース | インデックス管理ユーティリティ |
| types.ts | `packages/storage/src/types.ts` | ソース | ストレージインターフェース定義 |
| giselle.ts | `packages/giselle/src/giselle.ts` | ソース | Giselleクラスのエントリーポイント |
