# 機能設計書 55-SSHキャッシュ

## 概要

本ドキュメントは、GhosttyターミナルエミュレータのSSHキャッシュ機能(`+ssh-cache`コマンド)の設計仕様を記載する。

### 本機能の処理概要

SSHキャッシュ機能は、SSH接続時にリモートホストへのterminfo自動インストール履歴を管理するCLIコマンドである。SSH統合機能と連携し、既にterminfoをインストールしたホストを追跡することで、冗長なアップロードを防止する。

**業務上の目的・背景**：Ghosttyはカスタムterminfoエントリをリモートホストにインストールすることで、SSHセッション中の最適なターミナル体験を提供する。しかし、毎回のSSH接続でterminfoを転送するのは非効率である。本機能により、インストール済みホストの情報をキャッシュし、不要な転送を回避できる。

**機能の利用シーン**：
- キャッシュされたホストの一覧を確認したい場合
- 手動でホストをキャッシュに追加/削除したい場合
- キャッシュをクリアして初期状態に戻したい場合
- 特定のホストがキャッシュされているか確認したい場合

**主要な処理内容**：
1. コマンドライン引数の解析（`--clear`、`--add`、`--remove`、`--host`オプション）
2. ディスクキャッシュの初期化
3. 指定されたアクションの実行（一覧表示、追加、削除、確認、クリア）
4. 結果の出力

**関連システム・外部連携**：ファイルシステム（XDG_STATE_HOME配下のキャッシュファイル）、シェル統合機能

**権限による制御**：特になし。全ユーザーが実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | CLI | 主機能 | コマンドライン実行によるキャッシュ管理 |

## 機能種別

CLI操作 / CRUD操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| --clear | bool | No | キャッシュ全体をクリア | - |
| --add | 文字列 | No | 指定ホストをキャッシュに追加 | 有効なホスト名形式（hostname または user@hostname） |
| --remove | 文字列 | No | 指定ホストをキャッシュから削除 | 有効なホスト名形式 |
| --host | 文字列 | No | 指定ホストがキャッシュされているか確認 | 有効なホスト名形式 |
| --expire-days | u32 | No | カスタム有効期限（日数） | 正の整数 |

*注: --clear、--add、--remove、--hostは排他的。複数指定時は1つのみ実行（順序は保証されない）

### 入力データソース

- コマンドライン引数
- キャッシュファイル（`${XDG_STATE_HOME}/ghostty/ssh_cache`）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ホスト一覧 | テキスト | キャッシュされたホストとインストール日時 |
| 操作結果 | テキスト | 追加/削除/クリア操作の結果メッセージ |

### 出力先

- 標準出力（stdout）: 結果メッセージ
- 標準エラー出力（stderr）: エラーメッセージ
- 終了コード: 0（成功）または1（エラー/ホスト未キャッシュ）

## 処理フロー

### 処理シーケンス

```
1. コマンドライン引数の解析
   └─ Options構造体に各オプション値を設定
2. DiskCacheの初期化
   └─ defaultPath()でキャッシュファイルパスを取得
3. アクションの実行
   └─ --clear指定時: cache.clear()
   └─ --add指定時: cache.add()
   └─ --remove指定時: cache.remove()
   └─ --host指定時: cache.contains()
   └─ 引数なし: cache.list()で一覧表示
4. 結果の出力
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[引数解析]
    B --> C[DiskCache初期化]
    C --> D{--clear?}
    D -->|Yes| E[cache.clear]
    E --> F["Cache cleared" 出力]
    D -->|No| G{--add?}
    G -->|Yes| H[cache.add]
    H --> I{追加成功?}
    I -->|Yes| J["Added/Updated" 出力]
    I -->|No| K[エラー出力]
    G -->|No| L{--remove?}
    L -->|Yes| M[cache.remove]
    M --> N["Removed" 出力]
    L -->|No| O{--host?}
    O -->|Yes| P[cache.contains]
    P --> Q{キャッシュあり?}
    Q -->|Yes| R["has terminfo" 出力]
    Q -->|No| S["does not have" 出力]
    O -->|No| T[cache.list]
    T --> U[ホスト一覧出力]
    F --> V[終了コード0]
    J --> V
    N --> V
    R --> V
    U --> V
    S --> W[終了コード1]
    K --> W
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-55-1 | ホスト名フォーマット | hostnameまたはuser@hostname形式のみ受け付ける | --add、--remove、--host指定時 |
| BR-55-2 | キャッシュ有効期限 | デフォルトでは無期限。--expire-daysで指定可能 | 常時 |
| BR-55-3 | ファイルパーミッション | キャッシュファイルは0600で作成 | ファイル作成時 |
| BR-55-4 | 排他制御 | ファイルロックにより同時アクセスを制御 | ファイル操作時 |

### 計算ロジック

キャッシュエントリの有効期限判定:
- 現在時刻 - エントリのタイムスタンプ > 有効期限日数 * 秒/日 の場合は期限切れ

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

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

| 操作 | 対象ファイル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧表示 | ssh_cache | READ | キャッシュファイルの読み取り |
| 追加 | ssh_cache | WRITE | ホストエントリの追加/更新 |
| 削除 | ssh_cache | WRITE | ホストエントリの削除 |
| クリア | ssh_cache | DELETE | キャッシュファイルの削除 |

### キャッシュファイル形式

```
hostname|timestamp|terminfo_version
user@hostname|timestamp|terminfo_version
```

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | 無効なホスト名 | ホスト名が不正な形式 | 正しいホスト名形式を指定 |
| 1 | キャッシュロック | 他プロセスがキャッシュを使用中 | 少し待ってから再試行 |
| 1 | ホスト未キャッシュ | --hostで指定したホストがキャッシュにない | 終了コード1で終了（正常動作） |

### リトライ仕様

リトライなし（ユーザーが手動で再実行）

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

- アトミックファイル書き込みを使用（atomicFile）
- ファイルロックによる排他制御

## パフォーマンス要件

即座にレスポンスが返ること（通常1秒未満）

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

- キャッシュファイルは0600パーミッションで作成
- 機密情報（パスワード等）は保存しない

## 備考

- シェル統合のSSH機能から自動的に呼び出される
- IPv4/IPv6アドレスもホスト名として使用可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Entry.zig | `src/cli/ssh-cache/Entry.zig` | Entry構造体（4-11行目）でキャッシュエントリを定義 |
| 1-2 | DiskCache.zig | `src/cli/ssh-cache/DiskCache.zig` | DiskCache構造体（4-18行目）でキャッシュ操作を定義 |

**読解のコツ**: Entry構造体はhostname、timestamp、terminfo_versionの3フィールドを持つシンプルな構造。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ssh_cache.zig | `src/cli/ssh_cache.zig` | run関数（49-79行目）がエントリーポイント |

**主要処理フロー**:
1. **50-52行目**: アリーナアロケータの初期化
2. **54-61行目**: Options解析
3. **63-71行目**: stdout/stderrのwriter準備
4. **73行目**: runInner関数の呼び出し

#### Step 3: 各操作ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ssh_cache.zig | `src/cli/ssh_cache.zig` | runInner関数（81-183行目）で各操作を実装 |

**主要処理フロー**:
- **88-89行目**: DiskCache.defaultPath()でパス取得
- **91-95行目**: --clear処理
- **97-122行目**: --add処理
- **124-145行目**: --remove処理
- **147-176行目**: --host処理
- **178-182行目**: デフォルトの一覧表示処理

#### Step 4: DiskCacheの操作を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DiskCache.zig | `src/cli/ssh-cache/DiskCache.zig` | add関数（63-129行目）で追加処理 |
| 4-2 | DiskCache.zig | `src/cli/ssh-cache/DiskCache.zig` | remove関数（133-169行目）で削除処理 |
| 4-3 | DiskCache.zig | `src/cli/ssh-cache/DiskCache.zig` | contains関数（173-195行目）で確認処理 |

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

```
ghostty +ssh-cache
    │
    └─ ssh_cache.run()
           │
           ├─ args.parse() ─ オプション解析
           │
           └─ runInner()
                  │
                  ├─ DiskCache.defaultPath() ─ キャッシュパス取得
                  │
                  ├─ cache.clear() ─ キャッシュクリア
                  │
                  ├─ cache.add() ─ ホスト追加
                  │      ├─ isValidCacheKey() ─ ホスト名検証
                  │      ├─ readEntries() ─ 既存エントリ読み取り
                  │      └─ writeCacheFile() ─ アトミック書き込み
                  │
                  ├─ cache.remove() ─ ホスト削除
                  │
                  ├─ cache.contains() ─ ホスト確認
                  │
                  └─ cache.list() ─ 一覧取得
                         └─ listEntries() ─ ソート・出力
```

### データフロー図

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

コマンドライン ───▶ args.parse() ───────▶ Options
引数                                     (clear, add, remove, host)

XDG_STATE_HOME ───▶ DiskCache.defaultPath()
                         │
                         ▼
キャッシュファイル ◀──▶ DiskCache操作
                         │
                         ├─▶ 「Cache cleared」
                         ├─▶ 「Added/Updated '...'」
                         ├─▶ 「Removed '...'」
                         ├─▶ 「has/does not have terminfo」
                         └─▶ 「Cached hosts (N):」
                                          ▼
                                     標準出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ssh_cache.zig | `src/cli/ssh_cache.zig` | ソース | メインエントリーポイント |
| DiskCache.zig | `src/cli/ssh-cache/DiskCache.zig` | ソース | ディスクキャッシュ操作 |
| Entry.zig | `src/cli/ssh-cache/Entry.zig` | ソース | キャッシュエントリ構造体 |
| args.zig | `src/cli/args.zig` | ソース | コマンドライン引数解析 |
