kikushun.com の初週

iPhone から放り込んだ文字・写真・音声・動画を Claude Code が読んで、記事と SNS の下書きにするところまでを 2 日で作りました。作ったものより、途中でぶつかった R2 の上限と、2.55GB の動画が 32 パート目で止まった話のほうが役に立つと思うので、そっちを書きます。

全体の構成

iPhone の PWA から Inbox Worker、Claude Code、GitHub を経て公開サイトに届くまでの構成図 iPhone の PWA 文字・写真・音声・動画をそのまま投げる Inbox Worker 非公開・Cloudflare Access で保護 D1(メタデータ) R2(生メディア) MacBook の Claude Code 取得・分類・下書き生成・検証 ここから公開 GitHub(main) 公開コンテンツの正本 GitHub Actions check・test・validate・build kikushun.com Astro の静的サイト media.kikushun.com 公開用の画像
Worker は 2 つ。非公開の Inbox と公開サイトを分けて、間に Claude Code と GitHub を挟んでいます。

生のメディアは GitHub に置かず R2 に入れて、公開する画像だけ別のバケットに出しています。Claude Code は MacBook から Inbox を読み、GitHub への push で Actions が起動してデプロイされる仕組みです。

ぶつかった上限

先に知っていれば設計が変わっていた数字です。

対象 値 効いてくるところ
R2 の multipart 1 パート 5 MiB〜5 GiB、最大 10,000 パート 送れる最大サイズはパート長しだい(20 MiB なら 195 GiB)
1 パートの大きさ 20 MiB パートは Worker を経由して一度メモリに載るため
添付の上限 5 GiB R2 の制約ではなく、こちらで決めた値

最初は 2 GiB にしていて、2.55GB の動画が validation_error の一言で弾かれました。zod のメッセージが英語のまま画面に出ていなかったので、何が起きたのか不明でした。。。上限を 5 GiB に上げ、/api/health で limits を返して、PWA 側はファイルを選んだ時点で上限を見るようにしました。

2.55GB の動画が 32 パート目で止まる

上限を上げたら、今度は送信が途中で止まりました。画面は送信中の表示のままで、遅いのか死んだのかも分かりませんでした。

130 パート中 32 で止まった送信と、タイムアウトと再送を入れて完走したあとの比較 直す前 1 32 130 32 パート目で止まったまま数時間 直したあと 120 秒でタイムアウト → 再送 最大 6 回の指数バックオフで再送して、130 パートまで完走
バーは送信済みのパート数です。

原因は、パートの PUT にタイムアウトも再送も無かったことです。iPhone の画面ロックで fetch が宙に浮くと、そのまま永遠に待っていました。

直したのは 4 点です。

再送して大丈夫なのは、R2 の multipart が同じパート番号の上書きを許すからです。途中まで送ったパートを捨てて、最初からやり直す必要はありません。

2 日間の内訳

9/13 は土台。Astro 7 の静的サイト(Blog / Projects / Log / About)、Hono の Inbox Worker(D1 / R2 / Cloudflare Access / Whisper 文字起こし)、iPhone から投げる PWA、GitHub Actions の CI と Deploy、写真の公開経路(exiftool で EXIF 除去を検証してから配信)、動画加工スクリプト、YouTube の軽量埋め込みまで。

9/14 は運用の作り直し。develop ブランチを廃止して、生成物に初見レビュー必須の品質ゲートを付けました。そのあとが上の 2.55GB の話です。