概要
OneCLI は、AI エージェントが API キーなどの機密情報を直接保持せずに外部サービスへアクセスするための、オープンソースのクレデンシャル Vault です。エージェントと外部 API の間に透過的な HTTP ゲートウェイとして介在し、認証情報の一元管理・注入・監査を担います。
この記事は、AI エージェントのセキュリティ設計に関心がある開発者・インフラエンジニアを対象としています。OneCLI のアーキテクチャ・データモデル・構築方法・運用を網羅的に解説します。
| 項目 | 内容 |
|---|---|
| 種別 | オープンソースソフトウェア |
| リポジトリ | onecli/onecli |
| 公式サイト | onecli.sh |
| 主要言語 | TypeScript / Rust |
| スター数 | 717(2026-03-17 時点) |
特徴
セキュリティ
| 項目 | 内容 |
|---|---|
| ゼロトラスト設計 | エージェントはプレースホルダーキー(FAKE_KEY)のみ保有。実キーに到達できない構造 |
| AES-256-GCM 暗号化 | {iv_b64}:{authTag_b64}:{ciphertext_b64} 形式で保存。リクエスト時のみメモリ内で復号 |
| MITM CA 証明書 | ECDSA P-256 / SHA-256 で自己署名 CA を自動生成。ホストごとにリーフ証明書(有効期間 24 時間)を動的生成 |
| スコープ付きトークン | 各エージェントに aoc_ プレフィックスの独立したアクセストークンを付与 |
| 監査ログ | ゲートウェイの tracing ログで全 CONNECT/MITM リクエストを記録 |
アーキテクチャ
| 項目 | 内容 |
|---|---|
| 透過的プロキシ注入 | HTTP CONNECT をインターセプトし、MITM 方式で実認証情報を注入 |
| パターンマッチング | ホスト(完全一致 + *. ワイルドカード)とパス(完全一致 + /prefix/*)でルーティング |
| デュアルサービス構成 | Next.js 製 Web ダッシュボード(ポート 10254)+ Rust 製ゲートウェイ(ポート 10255) |
| 柔軟なストレージ | 組み込み PGlite または外部 PostgreSQL を選択可能 |
| 2 種類の認証モード | シングルユーザーモード(デフォルト)と Google OAuth(チーム利用) |
DX
| 項目 | 内容 |
|---|---|
| ワンコマンドデプロイ | Docker Compose 1 コマンドで起動可能 |
| フレームワーク非依存 | HTTPS_PROXY 環境変数と CA 証明書の配布のみで任意のフレームワークと連携 |
構造
システムコンテキスト図
| 要素名 | 説明 |
|---|---|
| 開発者 | ダッシュボード経由で認証情報とエージェントを管理するオペレーター |
| AI エージェント | OneCLI プロキシ経由で外部 API を呼び出すクライアント |
| OneCLI | 認証情報を安全に保管し、エージェントのリクエストに注入するクレデンシャル Vault |
| 外部 API | エージェントが呼び出す対象の外部サービス |
| KMS | ローカルまたはクラウド上のキー管理サービス |
| データベース | ユーザー・エージェント・暗号化済みシークレットを永続化するストレージ |
コンテナ図
| 要素名 | 説明 |
|---|---|
| Web ダッシュボード | Next.js 製の管理 UI + API サーバー。ポート 10254 で動作。シークレット・エージェント管理と認証情報解決 API を提供 |
| Rust ゲートウェイ | Tokio・Hyper 製の HTTP プロキシ。ポート 10255 で動作。エージェントのリクエストをインターセプトして認証情報を注入 |
| KMS | ローカルまたは OneCLI Cloud 上のキー管理サービス。シークレットの暗号化キーを管理 |
| データベース | PGlite(組み込み)または PostgreSQL(外部)。ユーザー・エージェント・シークレットを格納 |
コンポーネント図
| 要素名 | 説明 |
|---|---|
| 認証システム | NextAuth ベースのユーザー認証モジュール |
| ダッシュボード UI | shadcn/ui を使用した管理画面。シークレットとエージェントを管理 |
| resolve-credentials API | ゲートウェイからの認証情報解決リクエストを処理する Next.js API エンドポイント |
| サーバーアクション | Next.js サーバーアクション。データ操作をサーバーサイドで実行 |
| Prisma ORM | データベースアクセス層。スキーマ定義とマイグレーションを管理 |
| 共有 UI コンポーネント | packages/ui に配置された再利用可能な shadcn/ui コンポーネント群 |
| HTTP リクエストハンドラ | Tokio・Hyper でエージェントからの HTTP リクエストを受け付けるモジュール |
| ホスト-パス パターンマッチャ | リクエストの host と path を Secret の hostPattern・pathPattern と照合するモジュール |
| 認証情報インジェクタ | プレースホルダー値を復号済みの実認証情報に置換するモジュール |
| MITM CA 証明書マネージャ | HTTPS インターセプション用の CA 証明書を生成・管理する rustls モジュール |
| User モデル | ユーザー情報を格納する Prisma モデル |
| Agent モデル | エージェント情報と aoc_ プレフィックス付きアクセストークンを格納する Prisma モデル |
| Secret モデル | name・hostPattern・pathPattern・headerName・valueFormat・encryptedValue を持つ Prisma モデル |
プロキシリクエストフロー
| 要素名 | 説明 |
|---|---|
| HTTP CONNECT | エージェントが Proxy-Authorization: Basic base64("x:{aoc_token}") を付与して送信 |
| intercept | シークレットが存在するホストは MITM、存在しないホストは TCP パススルー |
| MITM - TLS 終端 | CA 署名のリーフ証明書(有効期間 24 時間、DashMap キャッシュ)でエージェント側 TLS を終端 |
| パターンマッチ | ホスト(完全一致 + *. ワイルドカード)とパス(* / /prefix/* / 完全一致)で照合 |
| ヘッダー注入 | injectionConfig に基づきプレースホルダーを実キーに置換。ホップバイホップヘッダーは除外 |
パターンマッチングルール
ホストパターン
| パターン | マッチする例 | マッチしない例 |
|---|---|---|
api.anthropic.com |
api.anthropic.com |
other.anthropic.com |
*.anthropic.com |
api.anthropic.com、sub.anthropic.com |
anthropic.com |
パスパターン
| パターン | マッチする例 | マッチしない例 |
|---|---|---|
* |
すべてのパス | なし |
/v1/* |
/v1/messages、/v1/、/v1 |
/v2/messages |
/v1/messages |
/v1/messages(完全一致のみ) |
/v1/messages/ |
null(未設定) |
すべてのパス | なし |
データ
概念モデル
| 要素名 | 説明 |
|---|---|
| User | システムへのアクセス主体。シングルユーザーモードでは自動生成、OAuth モードでは Google ログインで作成 |
| Agent | プロキシに接続するクライアント。アクセストークンで認証 |
| Secret | 暗号化された API クレデンシャル。ホスト・パスのパターンに基づき対象リクエストへ注入 |
| resolve-credentials | Agent がプロキシ経由でリクエストを送信する際に、Secret を解決・復号して注入する処理 |
情報モデル
| 要素名 | 説明 |
|---|---|
| User.id | ユーザーの一意識別子 |
| User.email | OAuth モードでのログインに使用するメールアドレス |
| User.authMode | 認証モード。local / oauth / cloud のいずれか |
| Agent.accessToken | プロキシ認証に使用するトークン。aoc_ プレフィックス付き |
| Agent.userId | 所有ユーザーへの外部キー |
| Secret.type | クレデンシャルの種別。anthropic または generic |
| Secret.encryptedValue | AES-256-GCM で暗号化されたクレデンシャル値 |
| Secret.hostPattern | マッチ対象ホストのグロブパターン(例: api.anthropic.com、*.example.com) |
| Secret.pathPattern | マッチ対象パスのグロブパターン(省略時は全パスにマッチ) |
| Secret.injectionConfig | 注入設定。headerName と valueFormat(例: Bearer {value})を持つ JSON。type = "anthropic" の場合は null |
injectionConfig の詳細
type = "generic" の場合のみ使用する JSON フィールドです。
type InjectionConfig = {
headerName: string; // 必須: 注入先の HTTP ヘッダー名
valueFormat: string; // 省略時は "{value}" がデフォルト
};
| ユースケース | headerName | valueFormat |
|---|---|---|
| OpenAI API Key | authorization |
Bearer {value} |
| カスタムヘッダー | x-custom-api-key |
{value} |
| Slack Bot Token | authorization |
Bearer {value} |
| Basic 認証 | authorization |
Basic {value} |
type = "anthropic" の場合、トークン種別に応じて自動決定されます。
| トークン形式 | 注入先ヘッダー |
|---|---|
sk-ant-oat...(OAuth トークン) |
Authorization: Bearer {value}(x-api-key を削除) |
| その他(通常 API キー) | x-api-key: {value}(Authorization を削除) |
暗号化ライフサイクル
| フェーズ | 内容 |
|---|---|
| 生成 | crypto.randomBytes(32).toString('base64') で 32 バイトキーを生成 |
| 保存 | SECRET_ENCRYPTION_KEY 環境変数または /app/data/secret-encryption-key ファイルに格納 |
| 暗号化形式 | {iv_b64}:{authTag_b64}:{ciphertext_b64}(IV: 12 バイト、Auth Tag: 16 バイト) |
| 復号 | ゲートウェイが CONNECT 解決時にオンデマンドで復号。メモリ内のみで保持し、ディスクへの書き込みは行わない |
| ローテーション | 自動ローテーション機構は未実装。キー変更時は既存シークレットの再暗号化が必要 |
| 消失時 | 全シークレットが復号不能(不可逆) |
構築方法
前提条件
| 方式 | 必要なツール |
|---|---|
| Docker(推奨) | Docker Engine 20.10 以上 |
| ローカル開発 | mise、Rust 1.75 以上、Node.js 22、pnpm 9.0.0 |
ポート 10254(ダッシュボード)と 10255(ゲートウェイ)を開放してください。
Docker Compose での起動
git clone https://github.com/onecli/onecli.git
cd onecli
docker compose -f docker/docker-compose.yml up
- PostgreSQL コンテナと OneCLI アプリコンテナを同時に起動
- 初回起動時に暗号化キーと PGlite データベースを自動生成
Docker 単体での起動
docker run --pull always \
-p 10254:10254 \
-p 10255:10255 \
-v onecli-data:/app/data \
ghcr.io/onecli/onecli
組み込み PGlite を使用するため、外部 DB は不要です。
ローカル開発環境の構築
git clone https://github.com/onecli/onecli.git
cd onecli
mise install # Node.js、pnpm などを自動インストール
pnpm install
cp .env.example .env
pnpm db:generate # Prisma クライアント生成
pnpm db:up # PostgreSQL を Docker で起動
pnpm db:migrate # マイグレーション実行
pnpm dev # Web + Gateway を同時起動
環境変数の設定
.env.example をコピーして .env を編集してください。
| 変数名 | 説明 | デフォルト |
|---|---|---|
DATABASE_URL |
PostgreSQL 接続文字列 | PGlite(組み込み) |
SECRET_ENCRYPTION_KEY |
AES-256-GCM 暗号化キー(32 バイト Base64) | 初回起動時に自動生成 |
NEXTAUTH_SECRET |
OAuth 認証有効化フラグ | 未設定(単一ユーザーモード) |
GOOGLE_CLIENT_ID |
Google OAuth クライアント ID | — |
GOOGLE_CLIENT_SECRET |
Google OAuth クライアントシークレット | — |
AUTH_MODE |
local(開発用)または oauth |
local |
NEXT_PUBLIC_GATEWAY_URL |
クライアントからのゲートウェイ URL | http://localhost:10255 |
暗号化キーの生成コマンドは以下のとおりです。
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
開発用コマンド一覧
| コマンド | 説明 |
|---|---|
pnpm dev |
Web + Gateway を開発モードで起動 |
pnpm dev:web |
Web のみ起動 |
pnpm build |
本番ビルド |
pnpm check |
Lint + 型チェック + フォーマット |
pnpm db:up |
PostgreSQL を Docker で起動 |
pnpm db:down |
PostgreSQL を停止 |
pnpm db:generate |
Prisma クライアント生成 |
pnpm db:migrate |
マイグレーション実行 |
pnpm db:studio |
Prisma Studio を起動 |
利用方法
エージェントの作成
- ダッシュボード(
http://localhost:10254)にアクセス - 「Agents」タブから「New Agent」を作成
- 生成されたアクセストークン(
aoc_プレフィックス)を保存
シークレットの登録
- ダッシュボードの「Secrets」タブで API キーを登録
- ホストパターン(例:
api.anthropic.com)とパスパターン(例:/v1/*)を指定 - 注入するヘッダー名(例:
x-api-key)と実際の API キー値を設定
エージェントからのリクエスト送信
エージェントはゲートウェイをプロキシとして使用します。
curl -x http://localhost:10255 \
-H "Proxy-Authorization: aoc_YOUR_TOKEN" \
-H "x-api-key: FAKE_KEY" \
https://api.anthropic.com/v1/messages \
-d '{"model": "claude-3-5-sonnet-latest", ...}'
Proxy-Authorizationにエージェントトークンを指定x-api-keyにはプレースホルダー(FAKE_KEYなど任意の値)を指定- ゲートウェイがホスト・パスパターンを照合し、実際の API キーを自動注入
SDK・フレームワークからの利用
HTTPS_PROXY 環境変数と CA 証明書を設定するだけで、コード変更なしに利用できます。
# CA 証明書のダウンロード(初回のみ)
curl http://localhost:10254/api/gateway/ca -o /tmp/onecli-gateway-ca.pem
# 環境変数の設定
export HTTPS_PROXY=http://x:aoc_YOUR_TOKEN@localhost:10255
export HTTP_PROXY=http://x:aoc_YOUR_TOKEN@localhost:10255
export NODE_EXTRA_CA_CERTS=/tmp/onecli-gateway-ca.pem
Python(openai SDK)の例
import os
import openai
import httpx
os.environ["REQUESTS_CA_BUNDLE"] = "/tmp/onecli-gateway-ca.pem"
client = openai.OpenAI(
api_key="FAKE_KEY",
http_client=httpx.Client(
proxies={"https://": "http://x:aoc_YOUR_TOKEN@localhost:10255"},
),
)
LangChain からの利用
import os
from langchain_anthropic import ChatAnthropic
os.environ["HTTPS_PROXY"] = "http://x:aoc_YOUR_TOKEN@localhost:10255"
os.environ["REQUESTS_CA_BUNDLE"] = "/tmp/onecli-gateway-ca.pem"
os.environ["ANTHROPIC_API_KEY"] = "sk-ant-placeholder"
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
response = llm.invoke("Hello!")
n8n からの利用
# docker-compose.yml の n8n サービスに追加
services:
n8n:
image: docker.n8n.io/n8nio/n8n
environment:
- HTTPS_PROXY=http://x:aoc_YOUR_TOKEN@onecli:10255
- NODE_EXTRA_CA_CERTS=/tmp/onecli-gateway-ca.pem
- NODE_USE_ENV_PROXY=1
- ANTHROPIC_API_KEY=sk-ant-placeholder
volumes:
- /tmp/onecli-gateway-ca.pem:/tmp/onecli-gateway-ca.pem:ro
Dify からの利用
# Dify の docker-compose.override.yml
services:
api:
environment:
- HTTPS_PROXY=http://x:aoc_YOUR_TOKEN@onecli:10255
- REQUESTS_CA_BUNDLE=/tmp/onecli-gateway-ca.pem
- ANTHROPIC_API_KEY=sk-ant-placeholder
volumes:
- /tmp/onecli-gateway-ca.pem:/tmp/onecli-gateway-ca.pem:ro
対応フレームワーク一覧
| フレームワーク | 連携方式 |
|---|---|
| Claude Code | 環境変数(HTTPS_PROXY) |
| OpenHands | Docker 環境変数 |
| n8n | Docker 環境変数 / Node.js SDK |
| Dify | Docker 環境変数 |
| 任意の HTTP クライアント | HTTPS_PROXY 対応であれば動作 |
Node.js SDK
import { OneCLI } from "@onecli-sh/sdk";
const onecli = new OneCLI({
url: "http://localhost:10254",
apiKey: "oc_your_api_key",
});
// Docker 引数にプロキシ設定を自動注入
const args = ["run", "-i", "--rm", "n8nio/n8n"];
await onecli.applyContainerConfig(args);
ポート構成
| ポート | サービス | 用途 |
|---|---|---|
| 10254 | Next.js ダッシュボード | UI、API、認証管理 |
| 10255 | Rust ゲートウェイ | クレデンシャル注入プロキシ |
運用
デプロイメント
Docker(本番推奨)
単一コンテナにダッシュボード・ゲートウェイ・ストレージを内包しています。初回起動時に暗号化キー・データベース・CA 証明書を自動生成します。
docker run -d \
-p 10254:10254 \
-p 10255:10255 \
-v onecli-data:/app/data \
ghcr.io/onecli/onecli
クラウドデプロイ
- 外部 PostgreSQL を
DATABASE_URLで指定 PROXY_SECRETは AWS Secrets Manager 等の外部シークレットマネージャーに格納
起動シーケンス
entrypoint.sh が以下の順で初期化を実行します。
/app/data/proxy-secret(32 バイト乱数 hex)を生成- PGlite データベースを
/app/data/pglite/に初期化(外部 DB 未指定の場合) SECRET_ENCRYPTION_KEYを生成・保存runtime-config.jsonに認証モードを書き込み- プロキシサービス(10255 番ポート)を起動
- Web サービス(10254 番ポート)を起動
ヘルスチェック
| エンドポイント | サービス | 確認コマンド |
|---|---|---|
GET /api/health |
Web ダッシュボード | curl http://localhost:10254/api/health |
GET /healthz |
Rust ゲートウェイ | curl http://localhost:10255/healthz |
Docker ヘルスチェックは両エンドポイントを 30 秒ごとに検証します。
データ永続化
/app/data ボリュームに以下を格納します。
| パス | 内容 |
|---|---|
/app/data/pglite/ |
組み込みデータベースファイル |
/app/data/proxy-secret |
プロキシ認証シークレット(消失で復旧不可) |
/app/data/secret-encryption-key |
AES 暗号化キー(消失で全シークレット消失) |
| CA 証明書 | MITM 傍受用 CA 証明書 |
ボリューム削除時にすべてのデータが永久消失するため、定期バックアップが必要です。
監査ログ
OSS 版ではデータベースへの監査ログテーブルは存在しません。ゲートウェイの tracing クレートによる標準出力ログで記録されます。
# ログの確認
docker logs onecli --follow
# 主要なログイベント
# CONNECT リクエスト
# INFO onecli_gateway::gateway: CONNECT peer=172.17.0.2:54321 host=api.anthropic.com:443 mode=mitm rule_count=1
# MITM フォワードリクエスト
# INFO onecli_gateway::gateway: MITM method=POST url=https://api.anthropic.com:443/v1/messages status=200 injections_applied=1
# トンネル終了
# INFO onecli_gateway::gateway: tunnel closed host=api.github.com:443 client_to_server=1024 server_to_client=2048
ログレベルは RUST_LOG 環境変数で制御します。
docker run ... -e RUST_LOG=debug ghcr.io/onecli/onecli
ダッシュボード操作
ダッシュボード(http://localhost:10254)のナビゲーション構成は以下のとおりです。
| 画面 | 主な機能 |
|---|---|
| Overview | エージェント数・シークレット数の統計カード、User API Key(oc_ プレフィックス)の表示・コピー |
| Agents | エージェント一覧、作成(名前入力 → aoc_ トークン自動生成)、トークン再生成、削除 |
| Secrets | シークレット一覧、作成(タイプ・値・ホスト/パスパターン・注入設定)、更新、削除 |
| Settings | プロフィール設定、User API Key の管理 |
認証モード
シングルユーザーモード(デフォルト)
NEXTAUTH_SECRET未設定時に有効- ログイン不要で全シークレットがデフォルトユーザーに帰属
- 開発・個人用途向け
OAuth モード(チーム利用)
NEXTAUTH_SECRET・GOOGLE_CLIENT_ID・GOOGLE_CLIENT_SECRETの全設定が必要- Google 認証によるマルチユーザー対応
- ユーザーごとにシークレットを分離
ベストプラクティス
セキュリティ
- エージェントには実際の API キーを渡さず、プレースホルダーキーを使用
# エージェントはダミーキーを設定する
OPENAI_API_KEY=FAKE_KEY
# プロキシが本物のキーに置換して転送する
- エージェントトークン(
aoc_プレフィックス)は厳重に管理 SECRET_ENCRYPTION_KEYは環境変数インジェクション経由で設定し、コードにハードコードしない- 秘密情報を含む
.envファイルは.gitignoreに追加
暗号化キー管理
SECRET_ENCRYPTION_KEYの変更は全シークレットの消失を招くため、ローテーション手順を事前に策定- クラウド環境では AWS Secrets Manager 等に格納してオーケストレーターから注入
アクセス制御
- 現状はユーザー単位でシークレットを管理(エージェント単位の制限は未実装)
- エージェントトークンは漏洩時に即時失効できる体制を整備
- グロブパターンでホスト・パスを絞り込み、意図しないシークレット注入を防止
スケーリング
- マルチインスタンス構成では外部 PostgreSQL を使用
- ゲートウェイとダッシュボードを分離デプロイする場合、それぞれのポートを個別に公開
トラブルシューティング
起動時エラー
| エラーコード | 原因 | 対処法 |
|---|---|---|
oauth-misconfigured |
NEXTAUTH_SECRET 設定済みだが Google OAuth 資格情報が未設定 |
3 変数すべて設定するか、NEXTAUTH_SECRET を削除 |
missing-encryption-key |
SECRET_ENCRYPTION_KEY が未設定 |
openssl rand -hex 32 | base64 で生成して設定 |
ポート競合
10254・10255 番ポートが使用中の場合は PORT / PROXY_PORT 環境変数で変更してください。
docker run -d \
-e PORT=8080 \
-e PROXY_PORT=8081 \
-p 8080:8080 \
-p 8081:8081 \
-v onecli-data:/app/data \
ghcr.io/onecli/onecli
データベース接続エラー
DATABASE_URLのフォーマットを確認- 開発環境では
pnpm db:upで PostgreSQL コンテナを起動 - PGlite のパーミッションエラーは
/app/dataディレクトリの権限を確認
エージェント接続失敗
- プロキシの
Proxy-Authorizationヘッダーにエージェントトークンが正しく設定されているか確認 - ゲートウェイ(10255 番ポート)への疎通を確認
curl http://localhost:10255/healthz
- シークレットのホスト・パスパターンがリクエスト先と一致しているか確認
ログ確認
docker logs <container_id>
初期化シーケンス・シークレット生成・サービス起動メッセージを確認してください。
資格情報注入の検証
# ダミーキーでプロキシ経由リクエストを送信し、本物のキーが注入されるか確認する
curl -x http://localhost:10255 \
-H "Proxy-Authorization: Bearer aoc_YOUR_TOKEN" \
https://api.target-service.com/endpoint
まとめ
OneCLI は、AI エージェントに API キーを直接渡さず、透過的な HTTP プロキシ経由でクレデンシャルを注入するゼロトラスト設計のクレデンシャル Vault です。Docker 1 コマンドでデプロイでき、HTTPS_PROXY 環境変数の設定だけで LangChain・n8n・Dify・Claude Code など任意のフレームワークと連携できます。
類似ツールとの主な違いを整理すると以下のとおりです。
| 観点 | OneCLI | Infisical / HashiCorp Vault |
|---|---|---|
| 主な対象 | AI エージェント | アプリケーション全般 |
| 注入方式 | HTTP プロキシ(MITM) | SDK / サイドカー / CLI |
| コード変更 | 不要(HTTPS_PROXY のみ) |
SDK 統合が必要 |
| セットアップ | Docker 1 コマンド | クラスタ構築が必要(Vault) |
| スコープ | クレデンシャル注入に特化 | シークレット管理全般 |
AI エージェントへのクレデンシャル管理を手軽に始めたい場合、OneCLI は有力な選択肢です。
この記事が少しでも参考になった、あるいは改善点などがあれば、ぜひリアクションやコメント、SNS でのシェアをいただけると励みになります!