Guide

Rotate or revoke R2 keys — controlled credentials, one place

Rotate the access key and every outstanding pre-signed URL dies at once. The clean, operator-controlled rotation procedure across apps/cf-sandbox, apps/mcp, apps/web.

5/18/2026 · connect0 · 3 min

Credentials live in one governed place, so rotating a key revokes every outstanding upload URL in a single controlled move.

If you suspect the R2 access keys have leaked — or as part of routine rotation hygiene — you can rotate them. This is an operator task; ordinary users never touch it.

What rotation invalidates

R2 pre-signed URLs are signed with a specific access key. Rotate the access key and every outstanding URL stops working immediately. This is the closest thing to "revoke all upload links" we have.

The procedure

  1. Mint new credentials. Cloudflare dashboard → R2 → API tokens → create new User API Token scoped to connect0-sandbox-fs with Object Read & Write. Save the new Access Key ID + Secret Access Key.

  2. Push to GCP Secret Manager. Add a new version on each of prod-r2-access-key-id + prod-r2-secret-access-key:

    printf '%s' '<new-id>'     | gcloud secrets versions add prod-r2-access-key-id     --project=c0-p-workloads --data-file=-
    printf '%s' '<new-secret>' | gcloud secrets versions add prod-r2-secret-access-key --project=c0-p-workloads --data-file=-
    

    apps/mcp and apps/web read :latest — next deploy picks them up.

  3. Push to Cloudflare Worker secrets (for apps/cf-sandbox, which mounts R2 via FUSE):

    cd apps/cf-sandbox
    printf '%s' '<new-id>'     | pnpm exec wrangler secret put R2_ACCESS_KEY_ID
    printf '%s' '<new-secret>' | pnpm exec wrangler secret put R2_SECRET_ACCESS_KEY
    pnpm --filter @repo/cf-sandbox cf-release
    
  4. Re-deploy apps/mcp and apps/web so they pick up the new Secret Manager versions:

    git tag vX.Y.Z+1
    git push origin vX.Y.Z+1
    ./scripts/watch-deploy.sh
    
  5. Revoke the old token in Cloudflare. Dashboard → R2 → API tokens → delete the old one. From this moment, every still-in-flight pre-signed URL signed with the old key returns 403.

Coordination matters

You need step 3 (Worker) and step 4 (Cloud Run) to happen close in time, because apps/cf-sandbox reading R2 with the old key + apps/mcp signing URLs with the new key will fail (the user PUTs, R2 accepts under new key, sandbox can't read because it's still mounting with old). Steps 3 and 4 can be done in either order, both before step 5.

How long is "down" during rotation?

Effectively zero if you sequence it correctly:

  • Add new Secret Manager versions (step 2) — no impact, services still read :latest = the previous version on the existing revisions.
  • Worker deploy (step 3) — single deploy, ~10 sec window.
  • Cloud Run deploys (step 4) — gradual via Cloud Run's traffic-switch; old revisions still serving until cut.
  • Old token delete (step 5) — instant invalidation of old credentials.

If you delete the old token before the new versions are in use, you'll break uploads until the deploys settle. Sequence carefully.

What rotation doesn't invalidate

  • Existing files in R2. Storage is unaffected.
  • Audit rows. upload_link continues to function.
  • The HMAC secret for the events path. That's a separate secret (prod-r2-events-hmac-secret) — rotate it independently.

Next steps

Where this fits

This is the One system of record pillar of connect0 — the core that connects everything your company runs on. Because the core holds the keys that sign every upload URL, one operator-controlled rotation governs access across the whole surface — invalidate once, everywhere. Start building → · All guides →