Skip to content

Add the Render → Cloud Run cutover runbook - #266

Merged
bishopBethel merged 1 commit into
mainfrom
claude/gcp-cutover-runbook
Sep 24, 2026
Merged

bishopBethel merged 1 commit into
mainfrom
claude/gcp-cutover-runbook

Conversation

@bishopBethel

Copy link
Copy Markdown
Member

Adds backend/deploy/gcp/CUTOVER.md, the runbook for moving the production API from Render to the devasign-api Cloud Run service. It's documentation only: the devasign-api-main trigger ignores backend/deploy/**, and Render's runtime is unaffected.

What's in it

  • Pre-flight (the day before, nothing touches prod):
    • Check that the project allows a public Cloud Run service; an org policy may block allUsers, and --no-invoker-iam-check is the fallback.
    • Add API_ORIGIN to the env file and confirm the newest image built.
    • Decide whether un-deployed main changes (currently Let the criteria review read the repo outside the diff #264) go to Render first.
    • Pre-register the extra GitHub/Linear callback URLs, prepare the verify-action PR, check access to every dashboard.
  • Cutover, about 30 minutes, in order:
    1. Record Render's health as a baseline.
    2. Create the prod service without STELLAR_ADMIN_SECRET and check it.
    3. Repoint the GitHub App webhook, OAuth, Stripe and Linear.
    4. Set VITE_API_BASE on both Vercel apps and redeploy.
    5. Move the verify-action v1 tag.
    6. Suspend Render once its job queue is quiet.
    7. Add the Stellar key to Cloud Run and check the escrow admin address matches Render's.
    8. Switch the trigger to _DEPLOY=true.
    9. Smoke test.
  • Rollback, for both stages. After Render is suspended, Cloud Run's Stellar key is removed before Render is resumed.
  • Follow-ups: an uptime check on writeThrough:"stalled", cleanup of docs that still use the Render URL, and an optional custom domain.

Design choices

  • Only one escrow keeper at a time. The database layer handles the Render/Cloud Run overlap, but each instance runs its own bounty keeper against real escrow funds.
  • The Stripe endpoint is edited, not recreated. A new endpoint would get a new signing secret and every Stripe webhook would be rejected.
  • Everyone is signed out once, because the session cookie belongs to the old host. The runbook says to warn users.

Checked

  • The step 2 secrets list builds to 15 entries with no STELLAR_ADMIN_SECRET.
  • The CORS check returns https://devasign-sponsor.vercel.app against the live Render API (same code).
  • gcloud builds triggers update github … --update-substitutions and gcloud run deploy --[no-]invoker-iam-check exist in the installed gcloud (565.0.0).
  • Live Render baseline right now: rowsLoadedAtBoot 11671, booted 2026-09-17, stellar: live.

🤖 Generated with Claude Code

Pre-flight checks, the ordered cutover steps with a check for each,
rollback before and after Render is suspended, and follow-ups. Cloud
Run only gets the Stellar key after Render is suspended, so two escrow
keepers never run at once; the Stripe endpoint is edited rather than
recreated so its signing secret stays the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
contributor Ready Ready Preview Sep 24, 2026 6:40pm UTC
sponsor Ready Ready Preview Sep 24, 2026 6:40pm UTC

@devasign-agent devasign-agent Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DevAsign Code Review

📝 Nitpicks (1)

✅ Merge score: 98/100

9 of 9 acceptance criteria met.
The PR adds a single documentation file, backend/deploy/gcp/CUTOVER.md, containing the Render to Cloud Run cutover runbook.

Tests: 7 passed, 2 unverifiable — see the "Tests by DevAsign" comment.

Comment thread backend/deploy/gcp/CUTOVER.md
Comment thread backend/deploy/gcp/CUTOVER.md
Comment thread backend/deploy/gcp/CUTOVER.md
Comment thread backend/deploy/gcp/CUTOVER.md
Comment thread backend/deploy/gcp/CUTOVER.md
Comment thread backend/deploy/gcp/CUTOVER.md
Comment thread backend/deploy/gcp/CUTOVER.md
Comment thread backend/deploy/gcp/CUTOVER.md
Comment thread backend/deploy/gcp/CUTOVER.md

Run everything from the checkout that holds the gitignored files the import script
generated (`backend/deploy/gcp/.env.cloudrun.yaml` and `.env.cloudrun.secrets`). Today that's
`/Users/ram/Documents/devasign-app/.claude/worktrees/app-navigation-sidebar-redesign-3dc50c`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Note (nit) — This hardcodes a personal absolute filesystem path (/Users/ram/...worktrees/...). It is brittle (wo…

This hardcodes a personal absolute filesystem path (/Users/ram/...worktrees/...). It is brittle (worktree names change, it's specific to one machine) and leaks a local directory layout into a committed runbook. Consider phrasing it as 'the worktree/checkout that generated the gitignored files' without the machine-specific path.

@devasign-agent

Copy link
Copy Markdown
Contributor

Tests by DevAsign

✅ Passed (7) · ⚠️ Unverifiable (2)

7 of 9 criteria verified by tests, 2 unverifiable. Each verdict below links to its evidence.

1 — A new documentation file is added at backend/deploy/gcp/CUTOVER.md. (pass)

Verdict: pass

Test confirms backend/deploy/gcp/CUTOVER.md exists with a top-level cutover runbook heading.

Test: .devasign/tests/cutover-doc-exists.test.mjs · integration

details

2 — The runbook documents pre-flight checks to be done the day before, including checking whether the project allows a public Cloud Run service (with --no-invoker-iam-check as fallback), adding API_ORIGIN to the env file, deciding whether un-deployed main changes go to Render first, pre-registering extra callback URLs, and checking dashboard access. (pass)

Verdict: pass

All five pre-flight subtests pass, covering public Cloud Run check with fallback, API_ORIGIN, Render decision, callback URLs, and dashboard access.

Test: .devasign/tests/cutover-preflight.test.mjs · integration

details

3 — The runbook lays out the cutover as an ordered sequence of steps, each with a corresponding check. (unverifiable)

Verdict: unverifiable

The numbered-and-ordered subtests pass; the failing subtest demands the literal token 'Verify' in every step body, which is stricter than the criterion's 'a corresponding check' and does not build the case the criterion names.

Test: .devasign/tests/cutover-steps-sequence.test.mjs · integration

details

4 — The cutover steps direct creating the prod Cloud Run service WITHOUT STELLAR_ADMIN_SECRET first, and adding the Stellar key to Cloud Run only after Render is suspended, so that two escrow keepers never run against real escrow funds at once. (pass)

Verdict: pass

Subtests confirm prod Cloud Run created without STELLAR_ADMIN_SECRET, Render suspension precedes and gates the Stellar handoff, and the two-keepers rationale is stated.

Test: .devasign/tests/cutover-stellar-ordering.test.mjs · integration

details

5 — The runbook instructs editing the existing Stripe endpoint rather than recreating it, so the signing secret stays the same and webhooks are not rejected. (pass)

Verdict: pass

Subtests confirm the runbook instructs editing the existing Stripe endpoint and explains a new endpoint would get a new signing secret.

Test: .devasign/tests/cutover-stripe-endpoint.test.mjs · integration

details

6 — The runbook documents a rollback procedure for both cutover stages, in which after Render is suspended Cloud Run's Stellar key is removed before Render is resumed. (pass)

Verdict: pass

Subtests confirm rollback procedures for both stages, with the after-step-6 rollback removing Cloud Run's Stellar secret before resuming Render.

Test: .devasign/tests/cutover-rollback.test.mjs · integration

details

7 — The runbook documents follow-ups: an uptime check on writeThrough:"stalled", cleanup of docs still using the Render URL, and an optional custom domain. (pass)

Verdict: pass

Subtests confirm follow-ups documenting the writeThrough:stalled uptime check, Render-URL doc cleanup, and optional custom domain.

Test: .devasign/tests/cutover-followups.test.mjs · integration

details

8 — The runbook notes that all users are signed out once because the session cookie belongs to the old host, and instructs warning users. (pass)

Verdict: pass

Subtests confirm the runbook explains the sign-out is caused by the session cookie belonging to the Render host and instructs warning users beforehand.

Test: .devasign/tests/cutover-signout-warning.test.mjs · integration

details

9 — The change is documentation-only and does not alter Render's runtime or the deployment behavior of existing triggers (the devasign-api-main trigger ignores backend/deploy/**). (unverifiable)

Verdict: unverifiable

The test itself asserts the diff-scope/footprint claim cannot be established from a single checkout, so it does not exercise whether the change is documentation-only or leaves triggers unaffected.

Test: .devasign/tests/cutover-doc-only-footprint.test.mjs · integration

details

@bishopBethel
bishopBethel merged commit 33776c7 into main Sep 24, 2026
7 checks passed
@bishopBethel
bishopBethel deleted the claude/gcp-cutover-runbook branch September 24, 2026 19:17

This branch was successfully deployed

2 active deployments
Preview – sponsor — ba98fb1e Deployed Sep 24, 2026 by vercel[bot]
Preview – contributor — ba98fb1e Deployed Sep 24, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant