# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## プロジェクト概要

ai-disk-gateway - `https://ai.disk.mydns.jp` で公開する LLM ゲートウェイ。
内部の llama-server を無改変で中継しつつ、APIキーごとのレート制限と
利用可能モデルの制御を行う。Git Submodules で管理されるマルチモジュール構成。

上流の llama-server には内部 APIキーが 1 本だけ発行されており、外部には決して出さない。
外部利用者には本アプリが発行するキーを配る。

## アーキテクチャ

```
ai-disk-gateway/
├── docker-compose.yml     # 全サービス一括起動
├── deploy.sh              # デプロイの唯一のエントリポイント
├── .deploy/               # デプロイ補助（vhost テンプレート・監視・退避）
├── public/                # ACME チャレンジ用 webroot
├── docs/                  # 実装計画書と control API 契約
├── gateway/               # submodule (公開 3000 / control 3001)
└── admin/                 # submodule (4000)
```

| モジュール | リポジトリ | ポート |
|-----------|-----------|-------|
| gateway | https://github.com/dmajima/llm-gateway-proxy | 3000 公開 / 3001 control（コンテナ内部のみ） |
| admin | https://github.com/dmajima/llm-gateway-admin | 4000 |

gateway は 1 コンテナで 2 つのリスナーを持つ。公開用はループバックへ出し、
control 用は Compose ネットワーク内に閉じてホストへは公開しない。

### 経路

| 公開 URL | 転送先 |
|---|---|
| `https://ai.disk.mydns.jp/admin/*` | admin |
| `https://ai.disk.mydns.jp/*` | gateway 経由で `http://192.168.0.13/*` |

Apache の `ProxyPass` は記述順に先勝ちで評価される。`/admin` は catch-all より前に置く。

## 開発コマンド

```bash
# デプロイ（Docker 導入から証明書取得まで一括）
bash deploy.sh

# 証明書処理を飛ばして再デプロイ
bash deploy.sh --skip-cert

# 疎通確認のみ
bash .deploy/healthcheck.sh

# サービスの状態
sudo docker compose ps
sudo docker compose logs -f gateway

# サブモジュールの初期化（クローン直後）
git submodule update --init --recursive
```

docker 系は必ず `sudo` を付ける。`usermod -aG docker` の直後は同一セッションに
グループ権限が反映されず、非 sudo の `docker compose` が失敗するため。

## サブモジュール操作の注意点

子リポジトリ内で作業した場合、**2段階のコミット**が必要。

1. 子リポジトリ内でコミット・プッシュ
2. 親リポジトリでサブモジュール参照を更新してコミット

```bash
cd <submodule-dir>
git add -A && git commit -m "..." && git push
cd ..
git add <submodule-dir>
git commit -m "chore: <name> のサブモジュール参照を更新"
```

## ルール

- 各モジュールは独立してビルド・テスト可能であること
- **モジュール間の通信は API を介して行う**。admin は SQLite を直接開いてはならない。
  データストアの所有者は gateway に一本化されている。契約は `docs/control-api-contract.md`
- 各モジュールのルートに Dockerfile を配置する
- 環境変数は `.env.example` に定義例を記載し、`.env` はコミットしない
- 子リポジトリでの変更後は必ず親リポジトリのサブモジュール参照も更新する

### このプロジェクト固有の不変条件

以下は仕様の中核であり、変更する場合は `docs/implementation-plan.md` の
該当セクションも必ず更新すること。

- **リクエストボディを再シリアライズしない**。受け取ったバイト列をそのまま上流へ渡す。
  書き換えてよいのは `Authorization` と `Host` のみ
- **レスポンスは自動解凍しない**。httpx の `aiter_raw()` を使う
- **モデル検査はフェイルクローズ**。許可リストを持つキーで検査できなければ拒否する
- **管理系パスは拒否する**。上流のスロット照会は他利用者のプロンプトを露出させる
- **内部 APIキーをログ・エラー本文へ出さない**。出力前に必ずマスクする
- **同時実行の上限はキー単位と全キー横断の二段**で持つ

## カスタムコマンド

| コマンド | 説明 |
|---------|------|
| `/submodule-update` | 全サブモジュールを最新に更新 |
| `/add-interface` | 新しいインターフェース（サブモジュール）を追加 |
