CLAUDE LABEN
RUNNER — claude self-hosted-runner で、自前のマシンやコンテナを web・モバイル・デスクトップセッションの実行先にできるようになりました(Team / Enterprise)PLUGIN — HTTPS 経由の zip からプラグインを導入できます。git も npm も不要で、任意で SHA-256 のピン留めが可能ですBEDROCK — ANTHROPIC_BEDROCK_REGION_PREFIX により、AWS_REGION 由来ではない特定のクロスリージョン推論プロファイルを優先できますSTATUS — サンドボックスや MCP 入力の応答待ちセッションが、Working ではなく Needs input と表示されるようになりましたDLP — Enterprise の Inference Hooks がベータで提供中です。プロンプトはモデルに届く前に組織のサーバへ回り、通常5秒以内の allow / deny 判定を待ちますWORKBENCH — 旧 Workbench と実験的なプロンプトツール API の廃止は8月17日、Sonnet 5 のプロモ価格は8月31日までですRUNNER — claude self-hosted-runner で、自前のマシンやコンテナを web・モバイル・デスクトップセッションの実行先にできるようになりました(Team / Enterprise)PLUGIN — HTTPS 経由の zip からプラグインを導入できます。git も npm も不要で、任意で SHA-256 のピン留めが可能ですBEDROCK — ANTHROPIC_BEDROCK_REGION_PREFIX により、AWS_REGION 由来ではない特定のクロスリージョン推論プロファイルを優先できますSTATUS — サンドボックスや MCP 入力の応答待ちセッションが、Working ではなく Needs input と表示されるようになりましたDLP — Enterprise の Inference Hooks がベータで提供中です。プロンプトはモデルに届く前に組織のサーバへ回り、通常5秒以内の allow / deny 判定を待ちますWORKBENCH — 旧 Workbench と実験的なプロンプトツール API の廃止は8月17日、Sonnet 5 のプロモ価格は8月31日までです
記事一覧/Claude Code
Claude Code/2026-08-08上級

同じコミットから作った zip なのに SHA-256 が変わる — プラグイン配布のピン留めを壊さない正規化

HTTPS 経由の zip からプラグインを導入できるようになり、SHA-256 のピン留めも選べるようになりました。同一内容の zip を2回作って測ったところ、22,669 バイト中 50 バイトだけが異なりハッシュが毎回変わりました。原因の測定と、20回連続で同一ハッシュになる正規化手順をまとめます。

Claude Code214プラグイン4SHA-256再現可能ビルド配布

プレミアム記事

手元のプラグインを社内配布の形にしてみようと思い立ったのは、zip からの導入が使えるようになったと知った直後でした。git も npm も挟まずに、置き場所を1つ決めて、中身は SHA-256 で確かめる。個人開発の小さな配布にはちょうどよい粒度に思えました。

作った zip のハッシュを控えて、少し手を止めて、もう一度同じディレクトリから zip を作り直しました。中身は1文字も変えていません。

ハッシュが違っていました。

バイト数は同じ 22,669。展開してみると、ファイルの内容も構成も完全に一致しています。それでも SHA-256 は別物でした。ピン留めという仕組みを、私は最初の10分で自分から壊していたわけです。

以下は、その差分がどこにあるのかをバイト単位で測り、20回連続で同じハッシュが出る形まで詰めた記録です。すべてサンドボックス上の Linux(Info-ZIP の zip 3.0、Python 3)で実際に走らせた数値を載せています。

50 バイトの正体

まず、独立した2回のチェックアウトを模しました。git はファイルの mtime を保存しません。クローンし直すたびに、全ファイルのタイムスタンプは「そのとき」になります。CI で毎回クリーンチェックアウトしている構成は、まさにこれに当たります。

# 同一内容のツリーを2秒あけて2つ用意する(git clone を2回した状態に相当)
cp -r src co1 && sleep 2 && cp -r src co2
( cd co1 && zip -qr ../co1.zip . )
( cd co2 && zip -qr ../co2.zip . )
sha256sum co1.zip co2.zip

結果はこうなりました。

アーカイブSHA-256(先頭16桁)サイズ
co1.zipe550b24486bb82f922,669 バイト
co2.zipc35df325dd91365522,669 バイト

差分を数えます。

a = open('co1.zip', 'rb').read()
b = open('co2.zip', 'rb').read()
diff = [i for i, (x, y) in enumerate(zip(a, b)) if x != y]
print(len(diff), "/", len(a))          # -> 50 / 22669
runs = []
for i in diff:                          # 連続領域にまとめる
    if runs and i == runs[-1][1] + 1:
        runs[-1][1] = i
    else:
        runs.append([i, i])
print(len(runs), sorted({r[1] - r[0] + 1 for r in runs}))   # -> 50 [1]

食い違っていたのは 22,669 バイト中の 50 バイト、割合にして 0.22% です。しかも 50 箇所すべてが長さ1の孤立したバイトでした。連続した塊ではありません。

一方、展開後のツリーは完全に同じです。

( cd x1 && find . -type f | LC_ALL=C sort | xargs sha256sum | sha256sum )
# 87f1d6cc4b71348a23ab7237f92d07fc7e1b04d3c993b5054ac67b75225df440
( cd x2 && find . -type f | LC_ALL=C sort | xargs sha256sum | sha256sum )
# 87f1d6cc4b71348a23ab7237f92d07fc7e1b04d3c993b5054ac67b75225df440

正体はタイムスタンプです。このアーカイブにはエントリが 11 個(うちディレクトリ 5 個)あり、それぞれが3種類の時刻を持っていました。

格納場所形式1エントリあたり
ローカルファイルヘッダMS-DOS 時刻・日付4 バイト
セントラルディレクトリMS-DOS 時刻・日付4 バイト
拡張フィールド UTUnix time(32bit)4 バイト × 2 箇所

UT は Info-ZIP が既定で足す拡張タイムスタンプで、この構成では拡張フィールドの合計が 264 バイトありました。秒単位まで持つので、DOS 時刻より細かく差が出ます。2秒差のビルドで動くのは主に下位バイトだけ。だから「孤立した1バイトが50箇所」という散らばり方になります。

内容ではなく、いつファイルを取り出したかを、zip は律儀に記録している。ハッシュはそれを含めて計算されます。当たり前と言えば当たり前なのですが、ピン留めを設定する側の頭には、まず浮かびません。

間を空けずに2回ビルドすると一致してしまう

ここが今回いちばん厄介だと感じた落とし穴です。

再現性を疑ったとき、多くの人はまず「続けて2回ビルドして、ハッシュを見比べる」はずです。私もそうしました。そして、そのやり方では問題が出ません。

MS-DOS 時刻の粒度は2秒です。同じ2秒窓に収まったビルドは、DOS 時刻も Unix time も同じ値になり得ます。間隔を変えて測りました。

2回のツリー生成間隔1回目(先頭12桁)2回目判定
0 秒034179b40720034179b40720一致
1 秒fd37aad07691f83beb9efdb4不一致
3 秒bdc8402f83a5d978834c43c9不一致

事前の予想は逆でした。「タイムスタンプが入るなら毎回必ず変わるはずだ」と思っていたのです。実際には、素早く2回試すと一致してしまう。手元の確認は通り、翌日の CI で落ちる。再現条件が時間に依存するせいで、原因にたどり着くまでに遠回りをしがちな類の不具合です。

回避策は単純です。再現性を検証するなら、2回のビルドの間に最低3秒は空けるか、後述するように mtime を意図的に撹乱してから比較してください。私はこの撹乱を検証手順そのものに組み込むことを推奨します。連続実行の一致は、何の保証にもなりません。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
同一内容の zip を独立に2回作ると 22,669 バイト中 50 バイトだけが食い違い、展開後のツリーは完全に一致したまま SHA-256 だけが変わる仕組みを、バイト差分の実測で分解しました
DOS タイムスタンプは2秒粒度のため、間を空けずに2回ビルドすると偶然ハッシュが一致します。再現性の確認がすり抜ける条件を gap=0 / 1 / 3 秒で測りました
mtime 正規化・LC_ALL=C での順序固定・-X・圧縮レベル明示の4点で 20/20 回同一ハッシュになる pack.sh と、取得先を固定して検証してから展開する verify.sh をそのまま置いています
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

Claude Code2026-06-14
Claude Code のプラグインをマーケットプレイスから探して入れる — 中身を読んでから信頼する手順
Claude Code のプラグインをマーケットプレイスから探して導入する実践手順です。/plugin の使い方に加えて、install 前に束ねられた中身を読んで安全性を確かめる勘所と、User/Project スコープの選び分けまで整理します。
Claude Code2026-03-22
Claude Code Channels — TelegramやDiscordからコーディングを指示する新しい方法
Claude Code Channelsを使えば、TelegramやDiscordからClaude Codeセッションにメッセージを送信し、外出先からもコーディング作業を指示できます。セットアップ手順と実践的な活用法を解説します。
Claude Code2026-03-20
Claude Code カスタムスキル開発の実装パターン — SKILL.md 設計とテスト・配布
Claude Code のカスタムスキルをゼロから設計・実装する実践チュートリアル。SKILL.md の構造、エージェントタイプ、コンテキスト注入、テスト手法まで、3つの実用スキルを作りながら学びます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →