Vector Image Detection
GitHub リポジトリ

検索したい単語を入力

いつでも検索バーを開ける

運用、seed、リリース

後日 provisioning するための順序、remote target、acknowledgement と復旧

この checklist は「何を確認するか」の正本です。実際に打つ command の完全版(copy-paste できる順序)は operator runbook を参照してください。

Site 全体は password wall の背後にあり、それ自体が public writes を有効化する前提条件です — AUTH_PASSWORD/AUTH_PASS_COOKIE の 2 つの secret が production Worker に届いていない限り、Worker は serve 自体を拒否します(詳細は step 9)。password wall は認証済み運用者 API(/api/v1/operator/** prefix — readiness と purge)には適用されません。この bearer-auth な exemption は password gate より厳格な認証であることに加え、readiness preflight と purge tooling がこの経路に到達できる必要があるため、そして gate 自体が misconfigured な状態でも readiness がその問題を report できるよう、gate の状態に関わらず常に有効です。

公開前チェックリスト(順序を変えない)

  1. 権限を分離する。 専用 Cloudflare API token を作り、必要な account/zone と Workers deploy、D1、R2、Queues、Vectorize、Workers AI、rate-limit binding、route の最小権限だけを与えます。token、account ID、resource ID、preflight bearer token を git・ブラウザー・ログに入れません。

  2. 資源を作る。 production 用の D1、private R2 bucket、photo Queue、別の DLQ、Workers AI access、rate-limit namespace、Vectorize index を作ります。Vectorize は作成時に固定なので必ず --dimensions=768 --metric=cosine を指定します。Queue consumer は photo Queue に接続し、DLQ を設定します。

  3. 実名を CI variables/secrets に置く。 DEMO_D1_DATABASE_NAME/ID、DEMO_R2_BUCKET_NAME、DEMO_QUEUE_NAME、DEMO_DLQ_NAME、DEMO_VECTORIZE_INDEX_NAME、DEMO_RATE_LIMIT_NAMESPACE_ID を repository に書き込みません。render-production-config.mjs が deployment 時だけ生成 config に差し込みます。

  4. remote migration を明示して適用する。 committed の wrangler.production.jsonc に直接 --config を向けてはいけません — placeholder database_id(00000000-0000-0000-0000-000000000000)が非空のため、wrangler の D1 lookup は account への名前解決をせずこの placeholder をそのまま使い、--remote を付けても実 DB には届きません。先に手元で DEMO_D1_DATABASE_NAME などの env var を揃えて node apps/demo/scripts/render-production-config.mjs を実行し、生成された .wrangler.production.generated.json に対してのみ pnpm --filter @vector-image-detection/demo exec wrangler d1 migrations apply DB --remote --config .wrangler.production.generated.json を実行します。--remote のない command は local D1 なので production migration に使いません。手順の全体は operator runbook を参照してください。

  5. 構成を検証する。 pnpm --filter @vector-image-detection/demo run deploy:dry-run:production は placeholder config の構文確認で、PR CI が credential なしで実行します。deploy workflow のほうは .wrangler.production.generated.json を一度だけ render し、その同じ file を dry-run して deploy するため、dry-run が実 resource 名と writes-on 設定まで含めて検証されます。migration、D1 metadata の二つの pinned model ID、768/cosine index、Queue/DLQ、private R2、rate limit、production environment を readiness が pass する必要があります。

  6. seed を準備する。 node apps/demo/scripts/seed-manifest.mjs は local manifest を検証します。remote を選ぶなら必ず node apps/demo/scripts/seed-manifest.mjs --remote --target production と明記します。これは manifest/target 検証のみで、resource に書き込みません。pnpm run demo:seed:local は deterministic local Worker resources で 100 thumbnails を同じ validation、D1 upload-operation、private R2、outbox/enrichment path に通し、attribution と unchanged rerun を確認します。この credential-free command は remote target を明示的に拒否します。remote seed は provision 後の authenticated operator action として deferred のままです。

  7. write をまだ off のまま deploy する。 production template は PUBLIC_WRITES_ENABLED: "false" です。read-only gallery/search と operator readiness を確認します。docs job はこの demo gate とは独立して deploy できます。

  8. 3 つの risk acknowledgement を人が承認する。 次の完全一致 values を secure CI variables に入れます。匿名 public writes、元画像 metadata の残留、reactive purge only moderation を理解していることが条件です。

    ACK_ANONYMOUS_PUBLIC_WRITES=I_ACKNOWLEDGE_ANONYMOUS_PUBLIC_WRITES
    ACK_RETAINED_IMAGE_METADATA=I_ACKNOWLEDGE_RETAINED_IMAGE_METADATA
    ACK_REACTIVE_PURGE_ONLY=I_ACKNOWLEDGE_REACTIVE_PURGE_ONLY_MODERATION
  9. 認証済み preflight を pass させる。 provisioning 中は repository variable DEMO_DEPLOYMENT_ENABLED を unset のままにし、demo deploy job を安全に skip させます。resource、read-only Worker、secret、acknowledgement がすべて用意できた最後の段階でだけ DEMO_DEPLOYMENT_ENABLED=true を設定します。DEMO_PREFLIGHT_URL と secret DEMO_PREFLIGHT_TOKEN を用意し、pnpm run cloudflare:demo-preflight を実行します。/api/v1/operator/readiness がすべての required check を pass、production、public writes enabled、正しいモデル/Vectorize invariant と返さない限り CI の demo deploy は停止します。 deploy 用 secret も同時に用意します。AUTH_PASSWORD、AUTH_PASS_COOKIE、DEMO_PREFLIGHT_TOKEN(deploy 後の OPERATOR_PREFLIGHT_TOKEN)の 3 つを repository secret に入れます。gate secret 2 つは prefix を付けません。すでにその名前で repository secret に存在し、local の .env でも同じ key を使うためです。CI は wrangler deploy --secrets-file で version と一緒に upload するので、dashboard での手動設定は不要です。gate secret が欠けた production Worker は serve を拒否します。 deploy 前の gate は「この Worker の readiness endpoint がまだそこに無い」状態を許容します。name が解決しない、connection が拒否される、Cloudflare の 1000 番台 edge error が返る場合に加えて、readiness JSON として parse できない body を返す 200(旧 build や SPA shell が応答している状態)も同じ扱いで ::warning:: にして先へ進み、deploy がその Worker を置き換えます。境界は parse できるかどうかです。parse できる body はこの Worker が応答した証拠なので、check が fail していれば従来どおり job を止めます。401 や 404 などの応答も止めます。初回 deploy だけは verification より先に traffic が切り替わりますが、password wall が公開を防ぎ、DEMO_DEPLOYMENT_ENABLED が人による opt-in として残り、deploy 後の strict gate が必ず走って失敗時に rollback command を出力します。

  10. public writes を有効化する。 preflight と acknowledgement 後にのみ generated production config が true を設定し deploy します。deploy 後は一つの upload、処理状態、media authorization、human tag exact search、AI-word exact search、related degradation を運用者が記録して確認します。この live smoke test は provisioning 前には「完了」と宣言しません。

credential-free CI の Worker e2e leg は AI/Vectorize binding を持たないため(wrangler.e2e.jsonc)、GET /api/v1/photos/:photoId/related が実際の neighbours を返すかどうかは自動テストで確認できません。デプロイ済みの production/preview に対して、以下を運用者が手動で実行します。

# 0. cookie jar は checkout の外、mode 600 の使い捨てにする
#    ("vid_demo_pass" は 1 年有効な bypass credential — repo に残してはいけない)
COOKIE_JAR=$(mktemp)
chmod 600 "$COOKIE_JAR"
trap 'rm -f "$COOKIE_JAR"' EXIT

# 1. password wall を通過して session cookie を保存
#    AUTH_PASSWORD は運用者が保持する production secret
#    --data-urlencode: password に + や & などの reserved 文字があっても壊れない
curl -sS -c "$COOKIE_JAR" --data-urlencode "password=$AUTH_PASSWORD" \
  "https://<deployed-host>/__auth" -o /dev/null -w '%{http_code}\n'
# → 302 を確認

# 2. state=ready な既存写真の photoId を選ぶ(gallery か GET /api/v1/photos から)
PHOTO_ID="<既存の ready photo の id>"

# 3. related endpoint を叩く
curl -sS -b "$COOKIE_JAR" \
  "https://<deployed-host>/api/v1/photos/$PHOTO_ID/related?version=v1&limit=8" | jq .

期待される shape(RelatedPhotosResponse、apps/demo/src/worker/contracts/api.ts):

{
  "version": "v1",
  "photoId": "<PHOTO_ID>",
  "items": [
    {
      "photo": { "id": "...", "state": "ready", "...": "..." },
      "reason": {
        "tier": "semantic",
        "score": 0.83,
        "vectorId": "<neighbourPhotoId>:<revision>",
        "indexedDocumentRevision": 1
      }
    }
  ],
  "nextCursor": null,
  "degraded": false,
  "degradedReason": null
}

判定基準:

  • degraded: false かつ items が非空 — index は生きており、実 neighbours を返している。これが合格ライン。

  • degraded: true, degradedReason: "vector_pending" — その写真の canonical vector がまだ Vectorize に反映されていない一時的な状態(eventually consistent)。数秒待って再試行する。

  • degraded: true, degradedReason: "provider_unavailable" — Vectorize 参照そのものが失敗した障害状態。恒久的に続くなら Vectorize index の health/binding を調査する。

  • items: [] かつ degraded: false — index は生きているが、この写真の AI description に近い他の写真が存在しない、正当な「該当なし」。

Cloudflare CLI は認証済み account を操作します。wrangler r2 bucket create <name>、wrangler queues create <name>、wrangler vectorize create <name> --dimensions=768 --metric=cosine の対象名は各環境専用にし、production と local/preview を混ぜません。Queue/DLQ の consumer 構成は Wrangler config でも確認します。

100 thumbnails の seed と帰属

apps/demo/fixtures/bundle/thumbs/ に 100 件(Oxford-IIIT Pet 60、Wikimedia Commons components 40)の明示的な collection があります。source、license、author は fixture credits と manifest.json に残します。配布・公開では各 source license を再確認してください。repository にあるのは thumbnail だけで、upstream full-resolution original はありません。

internal importer は同じ byte validation、durable upload operation、private R2、outbox/enrichment を使います。path 由来 stable seed ID と SHA-256 で completed/unchanged を skip するため再実行は idempotent、途中中断は再開可能、checksum 変更は旧写真を purge して置換します。削除は operator tombstone/purge を使い、R2、全 vector generation、D1 を成功まで再試行します。knownLabel は test expectation だけで、AI word/human tag として import してはいけません。legacy embeddings.bin も import しません。

日常運用と障害対応

  • 誤用/緊急事態: PUBLIC_WRITES_ENABLED を false にして deploy し、新規 anonymous write を止めます。既存の ready media/search は別の明示的な公開停止を行わない限り読めることに注意します。

  • 不適切/削除要求: 公開または browser admin UI は作らず、認証済み運用者 purge を実行します。DEMO_PURGE_URL、secret DEMO_PURGE_TOKEN(deploy 済み Worker の OPERATOR_PREFLIGHT_TOKEN と同じ値)、DEMO_PURGE_PHOTO_ID、non-empty DEMO_PURGE_REASON を shell/secret manager から渡して pnpm run cloudflare:demo-purge を実行します。HTTPS の operator endpoint が durable tombstone/outbox を 202 で受理した後、Queue/repair が R2、全 vector generation、D1 relations を削除し、terminal tombstone diagnostics を保持します。reactive-only であり、自動検出・審査・通報ではありません。

  • pending / enqueue_failed: outbox drain と upload-operation/R2 repair が durable record から再送・cleanup します。

  • processing retryable / DLQ: lease expiry、attempt/backoff、DLQ diagnostics を見て原因を直し、repair を再実行します。terminal error は無条件に公開しません。

  • 検索が古い/related だけ失敗: D1 exact tiers は正本なので残ります。required revision、canonical revision、photoId:revision、stale generation cleanup を確認します。

  • retention: 期限切れは tombstone と retryable deletion で処理します。R2 lifecycle を追加するならアプリの purge record と矛盾させず、削除失敗を調査できるようにします。