エミリアラボ電脳ジャンク
AI Credential Broker · credential-broker v0.1

鍵を渡さない管理人

AIに本物の鍵(APIキー)は渡さない。期限つきの「入館証」を渡して、許可した用事だけを代わりにこなす部品。

型番
credential-broker v0.1
つなぎ方
HTTP / MCP(stdio)
入館証の期限
既定 300 秒(最大 900 秒)
対応先
GitHub(読み取りのみ)
ライセンス
MIT
入手
GitHub

これは何?

マンションの管理人さんみたいなもの。
業者さんに合鍵は渡さず、「今日の15時まで・点検だけ」の入館証を渡して、用事は管理人さんが代わりに済ませてくれる。

AIに外のサービス(GitHub など)を使わせるには、ふつうは「APIキー」という鍵を渡します。でも、AIがその鍵を見てしまうと、だまされて誰かに教えてしまったり、ログに書き残してしまったりする危険があります。

Credential Broker は、鍵を自分だけで預かります。AIが受け取るのは、数分で使えなくなる「入館証」の番号だけ。AIが「この操作をしたい」と頼むと、管理人が鍵を使って代わりに操作し、結果だけを返します。

1

AI「GitHub を読みたいです」

2

管理人「はい、入館証。期限つき(既定は5分)で、頼めるのは読むことだけです」

3

AIが入館証で頼むと、管理人が本物の鍵で代わりに操作する。

4

AIには必要な結果だけが届く。管理人は鍵そのものをAIに返さない。

この部品がすること

  • 鍵を預かり、AIには期限つきの入館証だけを渡す
  • 決めた操作(いまは GitHub の読み取り4種)だけを代わりに行う。読める範囲は、預けた GitHub トークン自体の権限にもよる
  • 返す結果から、鍵に見える文字列を伏せる
  • 怪しい使い方(存在しない鍵を探し回る など)を記録して知らせる

この部品がしないこと

  • AIに鍵そのものを見せること
  • 決めていない操作(書き込み・削除など)を通すこと
  • 怪しい使い方を見つけて自動で止めること(v0.1 は知らせるだけ)
  • 外のネットワークに窓口を開くこと(自分のPCの中だけで待つ)

01v0.1 の範囲

Secret の出どころ.env(tmpfs・secret store 由来)のみ
ConnectorGitHub REST v3(読み取りのみ)
Capabilitygithub_reader
Actionslist_repositories / get_repository / list_issues / get_issue
TTL300 秒(最大 900 秒)
Storeプロセスメモリ
bind127.0.0.1:8787 固定

02取り付け

git clone https://github.com/kagioneko/credential-broker
cd credential-broker
python3 -m venv .venv
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/pytest -q   # 84 tests
環境変数説明
BROKER_API_TOKENエージェント ⇔ Broker の Bearer トークン(HTTP 用。未設定なら起動失敗)
CONFIG_PATH既定 config/credentials.yaml
DOTENV_PATHDotEnvProvider が読む .env。既定 .env
AUDIT_LOG_PATH設定すると全監査イベントを JSONL で追記(ローテート付き)

本番は systemd で動かし、ExecStartPre が secret store から tmpfs(/run/credential-broker/)に秘密を書き出し、ExecStopPost で消します。秘密ファイルは通常の永続ディスクではなく tmpfs に置く構成です。手順はリポジトリの deploy/ を参照。

03設定ファイル

AI に見えるのは credential_id(下の github_reader)と説明だけ。secret_name は .env 側のキー名で、AI には返りません。

default_ttl: 300
max_ttl: 900
rate_limit:
  request_per_min: 20
  execute_per_min: 60

credentials:
  github_reader:
    provider: dotenv
    secret_name: GITHUB_TOKEN        # .env のキー。AI には見せない
    connector: github
    description: "Read-only access to GitHub repositories and issues"
    ttl: 300
    allowed_actions:
      - list_repositories
      - get_repository
      - list_issues
      - get_issue

04MCP(AI CLI 向け)

Claude Code・Codex・Antigravity などの AI CLI からは MCP stdio で使います。AI に見えるツールは4つだけで、引数は構造的に credential_id / action / parameters に固定されていて、実キーを渡すための引数はありません。stdio のプロセス境界が信頼境界なので Bearer トークンは不要です。

python -m app.mcp_server          # 直接起動
deploy/mcp-launch.sh              # secret store → 一時ファイル → 子プロセス のラッパー
{ "mcpServers": { "credential-broker": {
    "command": "/opt/credential-broker/deploy/mcp-launch.sh"
} } }
MCP ツールやること
list_capabilities使える credential の一覧(物理的な Secret 情報は返さない)
request_credential入館証(handle)と残り秒数をもらう
executehandle で許可された action を実行してもらう
revokehandle をその場で無効にする

05HTTP API

cron や他ツール向け。全エンドポイントで Authorization: Bearer $BROKER_API_TOKEN が必須です。

BROKER_API_TOKEN=xxx DOTENV_PATH=.env \
  .venv/bin/uvicorn --factory app.main:create_app --host 127.0.0.1 --port 8787 --workers 1
methodpath入出力
GET/capabilities使える credential の一覧
POST/credentials/request{"credential_id"} → {"handle","expires_in"}
POST/execute{"credential_handle","action","parameters"} → {"status","data"}
POST/credentials/revoke{"credential_handle"} → {"status":"revoked"}

action ごとの主な parameters:get_repository は owner / repo、list_issues は owner / repo に加えて state(open / closed / all)など、get_issue は owner / repo / number。

06エラーコード

エラーは常に {"error": "<CODE>"} の形です。

codeHTTP意味
UNAUTHORIZED401Bearer トークンがない・違う
RATE_LIMITED429レート制限
UNKNOWN_CREDENTIAL404設定にない credential_id(../../.env などのパスも含む)
EXPIRED_HANDLE410入館証の期限切れ・無効化済み
ACTION_NOT_ALLOWED403許可されていない action
INVALID_PARAMETER422parameters が不正
CREDENTIAL_PROVIDER_ERROR / PROVIDER_ERROR502秘密の取り出し・上流 API の失敗

07異常利用の検知

全操作が監査イベントを通るので、そこで疑わしいパターンを閾値で判定します。ブロックはせず、検知のみです。閾値を超えると anomaly_suspected イベントを監査ログに書き戻し、NekoGuard がそれを拾って Discord に通知できます。

パターン既定閾値
credential_enumeration未知の credential_id を 5 種 / 60 秒(同一 client)
denied_action_streak禁止アクションの拒否 5 回 / 60 秒
expired_handle_replay失効した handle での execute 5 回 / 60 秒
rate_limit_hitsレート制限での拒否 3 回 / 60 秒

08セキュリティ設計

  • 未知の credential_id は設定の照合で UNKNOWN_CREDENTIAL。パスとして解釈されない
  • レスポンスは allowlist で必要な項目だけを抜き出し、トークンの形の文字列をマスクし、サイズ・件数に上限。上流のヘッダは転送しない
  • 期限の判定は time.monotonic()(NTP による時刻補正の影響を受けない)
  • 監査ログは決まったフィールドだけを受け付ける(自由な文字列を渡せない)。ログの redaction filter も併用
  • GitHub の base URL は固定、リダイレクトは追わない(SSRF のリスクを抑える)
  • 上流 API への接続は TLS 証明書を検証し、環境変数のプロキシ(HTTP(S)_PROXY)や TLS 鍵ログ(SSLKEYLOGFILE)を無視する。実クレデンシャルを載せた通信を途中で横取り・傍受されにくくする設計

受け入れテスト TEST-001〜008(Secret 非開示 / TTL / Revoke / 未認可 Action / Secret 列挙 / Path Traversal / Prompt Injection / ログ漏洩)に対応するテストがあります。

09既知の制限

v0.1 の制限(v0.2 で対応予定)

  • 上流レスポンスの 8 MiB 上限は、全体を受信してからの判定。本当のストリーミング上限は未実装
  • 8 文字未満の Secret は、結果からの伏せ字処理の対象外
  • SecretRegistry はスレッド同期なし(単一 event loop・1 worker 前提)
  • レート制限は client ごとではなく全体で共通(v0.1 は単一の Broker トークン設計)

補助として、Claude Code の PreToolUse(Bash) フック deploy/hooks/bash-secret-guard.sh が同梱されています。登録すると、Broker を通らないコマンドについて、決まった漏洩パターン(Vault からの取得でフィールドを指定しない、トークンをコマンドラインに直書きする、取り出した値を表示する など)を拒否します。すべての漏洩経路を防ぐものではありません。