マキモノ
業務自動化無料✅ 公式検証済みv1.0.0 / 更新

共有秘密をローテーションしても端末を締め出さない鍵配布サーバ設計

全PCへAPIキーを自動配布する小さなサーバで、共有秘密の張り替え時に全端末が永久401になる事故を防ぐ設計と復旧手順。primary/legacy/enrollの3層認証、クライアント側の上書き更新分岐、ローテーションの正しい順序と実測方法

出品者: kim@orgiast.jp2 DL📖 読込 約2,470トークン (約4円)💰 コスパ 15
トークン節約メーター86%節約
ゼロからAIに作らせた場合4.2万トークン
このMDを読ませた場合6,000トークン

3.6万トークンの節約 (API料金換算で約54円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。

この巻物について

共有秘密をローテーションしても端末を締め出さない鍵配布サーバ設計」は、業務自動化カテゴリのAI指示書(MDファイル)です。全PCへAPIキーを自動配布する小さなサーバで、共有秘密の張り替え時に全端末が永久401になる事故を防ぐ設計と復旧手順。primary/legacy/enrollの3層認証、クライアント側の上書き更新分岐、ローテーションの正しい順序と実測方法この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約3.6万トークン(API料金換算で約54円)・86%のトークンを節約できます。

カテゴリ
業務自動化
対応AI
claude-code、cursor、codex-cli
ライセンス
商用利用可 (再販不可)
価格
無料
ゼロから開発時
約4.2万トークン
この巻物使用時
約6,000トークン
節約量
約3.6万トークン (約54円)
更新日
2026-08-26

使い方 (AIに渡す3つの方法)

いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。

⬇ .md をダウンロード
claude "https://makimono-md.vercel.app/api/v1/files/md-762bfe1e/raw を読み込んで、この指示書どおりに実装して"
claude-codecursorcodex-cliライセンス: 商用利用可 (再販不可)

中身

共有秘密をローテーションしても配布先PCを締め出さない鍵配布サーバの設計と復旧手順

チームの全PCへ AI/API のキーを自動配布する小さなサーバ(以下 keyserve)を運用するときの、 「共有秘密を張り替えた瞬間に全端末が永久に締め出される」事故を防ぐ設計と、 すでに締め出してしまった場合の復旧手順をまとめた指示書。

対象読者: AI エージェント(Claude Code / Codex 等)にこの作業をやらせる人。 前提スタック: Node.js のサーバレス関数(Vercel/Cloudflare/Lambda いずれでも可)+ 各PCで動く配布クライアント(Node スクリプト)。


1. 最小構成(これだけで動く)

  • サーバ: POST /api/keys のみ。認証は HMAC-SHA256(共有秘密, unixtime) をヘッダで受ける。
    • x-<prefix>-ts(unix 秒)と x-<prefix>-auth(hex 64桁)。|now - ts| <= 300 秒、比較は crypto.timingSafeEqual
    • 秘密は環境変数に置く。配布リポジトリが public なら暗号文であっても鍵をリポジトリに置かない
  • サーバの返り値: { files: { "<ファイル名>": "<中身>", ... } }。クライアントは ~/.<app>/ 配下へ書く。
  • クライアント: 1日1回だけ実行(前回実行時刻を state ファイルに保存して間引く)。失敗しても起動処理を止めない。

認証の受け口は必ず「複数本」にする

const candidates = [
  ...(process.env.SHARED_SECRET ? [{ secret: process.env.SHARED_SECRET, label: 'primary' }] : []),
  ...list(process.env.SHARED_SECRET_LEGACY).map((s, i) => ({ secret: s, label: `legacy#${i + 1}` })),
  ...list(process.env.ENROLL_SECRETS).map((s, i) => ({ secret: s, label: `enroll#${i + 1}` })),
];
// list() は JSON 配列 / 改行 / カンマ区切りのどれでも受ける
  • primary: 現行の正規の秘密。サーバは応答に必ず keyserve.env(= primary の入ったファイル)を含めて返す。 こうすると「一時トークンで1回だけ叩けば、以後は正規の秘密で通る」状態を自動で作れる。
  • legacy: 移行期間だけ有効な旧秘密。ローテーションの生命線
  • enroll: 新規端末・締め出された端末の受け入れ用の使い捨てトークン。使い終わったら消す。
  • 一致したラベルだけをログに出す(値は絶対に出さない)。

2. 事故の型: 「既存ならスキップ」に更新対象を乗せてはいけない

配布クライアントは事故防止のため、こう書かれがち:

if (fs.existsSync(destination)) continue;              // ← 既存は絶対に上書きしない
fs.writeFileSync(destination, contents, { flag: 'wx', mode: 0o600 });

手で編集した API キーを潰さないという意味では正しい。だが、この既定に 「サーバ側で変わりうる値」= 共有秘密そのもの(keyserve.env)を乗せると詰む

サーバの primary を張り替えた瞬間に、各PCは古い秘密で叩き続ける →401 → 新しい秘密を受け取る手段が無い → 永久に自己回復できない。しかも失敗はログにしか出ないので数日誰も気付かない。

正しい実装(更新対象だけ明示的に上書き分岐)

const cleaned = contents.replace(/^/, '');
if (fs.existsSync(destination)) {
  // 秘密ファイルだけは中身が変わっていたら更新する。他の配布ファイルは従来どおりスキップ。
  if (name !== 'keyserve.env' || fs.readFileSync(destination, 'utf8') === cleaned) continue;
  fs.writeFileSync(destination, cleaned, { encoding: 'utf8', mode: 0o600 });
  log(`refreshed: ${name}`);                            // 値は出さない
  continue;
}
fs.writeFileSync(destination, cleaned, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
  • 中身が同じなら書かない(mtime を動かさない)= 無駄な差分と再起動を誘発しない。
  • 401 かつ秘密ファイルが存在する場合は、可視メッセージに 「サーバ側で秘密がローテーションされた可能性があります」を添える。原因に直結する一文があるだけで復旧が早い。
  • 失敗は必ず人の見る場所(チャットの通知先)へ1行出す。ログファイルだけに出す実装が「数日誰も気付かない」の原因。 ただし24時間に1回に抑止し、通知本文に秘密値を含めない。

3. ローテーションの正しい順序(②から始めない)

  1. クライアント修正を先に配布(上の上書き分岐)。配布経路は git pull / zip 取得など、 鍵の認証に依存しない経路であること。ここが認証に依存していると鶏卵になる。
  2. primary を新しいランダム値へ張り替える(例: crypto.randomBytes(48).toString('base64url'))。
  3. 同時に LEGACY各端末が今持っている旧値を入れる。これが移行期間の唯一の生命線。
  4. 再デプロイ(環境変数はデプロイ時にスナップショットされる基盤が多い。変更しただけでは効かない)。
  5. 各端末が「旧値で 200 → 新 primary を受領して上書き」まで進んだことを実測する。
  6. 確認できてから LEGACY を削除して再デプロイ。使い捨ての ENROLL もここで消す。

実測の仕方(ここを省くと直ったつもりで終わる)

  • 端末側: 配布クライアントのログに refreshed: <秘密ファイル> が出ているか。
  • サーバ側: ランタイムログに auth via legacy が残っていないか。
  • 疎通テスト(値を出力しないスクリプトで): 新 primary=200 / 旧値(legacy)=200 / でたらめな秘密=401 の3点を必ず確認する。 「200 が返った」だけでは不十分で、認証が効いていること(401 側) まで見る。

4. 環境変数の落とし穴

  • 暗号化された環境変数は CLI で読み戻せないことがある(値が空で返る)。 「既存値を読んで merge して置き換える」は成立しない。新しい変数名を足す設計にする (KEYS_JSON, KEYS_JSON_EXTRA, KEYS_JSON_EXTRA2 … をサーバ側で全部 merge する)。 読み戻せると誤解したまま rm → add すると、配布中の鍵一式を丸ごと失う
  • 削除→追加の間に失敗すると primary が消えた状態になる。新しい値は必ず先にローカルへ書いてから張り替える (復旧できる状態を作ってから壊す)。
  • 一時トークンは「発行 → 渡す → 使わせる → 消す」までを1セットにする。消し忘れると、 チャット履歴や作業ログに平文で残った1本で全社のキー一式が引ける状態が続く。

5. AI に作業させるときの指示テンプレ

目的: <サービス名> の共有秘密をローテーションする。端末を1台も締め出さないこと。

前提として先に調べて報告すること:
- 配布クライアントは新しい秘密を「上書き」で受け取れる実装になっているか(既存ならスキップになっていないか)
- 旧秘密を残す LEGACY 相当の受け口がサーバ側にあるか
- 現在どの端末が認証できているか(ログの 401 を実測する)

そのうえで ①クライアント修正 → ②新値へ張り替え+旧値を LEGACY へ → ③再デプロイ →
④新primary=200 / legacy=200 / でたらめ=401 を実測 → ⑤移行確認後に LEGACY と一時トークンを削除、の順で行う。
秘密値は出力しない(指紋 sha256 の先頭8桁と HTTP status だけ報告する)。
  • 秘密を扱うスクリプトは 値を print しない設計にし、確認は「指紋・件数・status」で行う。
  • 一時ファイルに書いた秘密は作業後に削除する。

6. チェックリスト

  • サーバは primary / legacy / enroll の複数本を受け付け、一致ラベルだけをログに出す
  • 応答に「現行 primary 入りのファイル」を必ず含める(端末が自動で正規化される)
  • クライアントは秘密ファイルだけ上書き更新、他は既存スキップ
  • 失敗(401)は人の見る通知先へ1行、24時間抑止つき、秘密値なし
  • ローテーションは「クライアント修正 → 張り替え+LEGACY → 再デプロイ → 実測 → LEGACY 削除」の順
  • 暗号化 env は読み戻せない前提で、追加は新しい変数名で行う
  • 一時トークンは削除まで完了して初めて「閉じた」と言う

よくある質問

「共有秘密をローテーションしても端末を締め出さない鍵配布サーバ設計」とは何ですか?

全PCへAPIキーを自動配布する小さなサーバで、共有秘密の張り替え時に全端末が永久401になる事故を防ぐ設計と復旧手順。primary/legacy/enrollの3層認証、クライアント側の上書き更新分岐、ローテーションの正しい順序と実測方法

どれくらいトークン(費用)を節約できますか?

ゼロから開発すると約4.2万トークンかかりますが、この巻物を使えば約6,000トークンで済みます。差し引き約3.6万トークン(API料金換算で約54円)・86%の節約です。

どうやって使いますか?

無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。

どのAIツールに対応していますか?

claude-code、cursor、codex-cli に対応しています。

商用利用できますか?

ライセンスは「商用利用可 (再販不可)」です。

🤝 自分でAIを動かすのは、まだ不安…という方へ

この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。

AI代行堂を見る →

関連する巻物

この巻物、誰かのトークンも救えます

𝕏 で節約レシートをシェア