Skip to content

Fix Render database names in API connection strings - #633

Merged
thomasluizon merged 4 commits into
mainfrom
fix/ticket-819-render-db-names
Sep 28, 2026
Merged

thomasluizon merged 4 commits into
mainfrom
fix/ticket-819-render-db-names

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Change

infra/configuration.tf now takes each database name from its Render Postgres internal connection URL when constructing both API connection strings. The username follows the corresponding resource's database_user. infra/README.md documents setting api_database = "render" during the production cutover and removing duplicated service variables after the linked group is verified.

Closes thomasluizon/orbit-tickets#819.

Test evidence

  • terraform fmt -check -recursive infra passed.
  • terraform -chdir=infra validate passed.
  • node tools/check-dashes.mjs --files infra/configuration.tf infra/README.md and node tools/check-timeless.mjs --staged passed.
  • dotnet build Orbit.slnx passed with zero errors. dotnet test passed all 6,813 tests.
  • A state-backed terraform -chdir=infra console check with api_database=render and both required web digest variables set to valid placeholder digests evaluated the connection string locals. The expression was { production = { for key, value in local.production_api_database_values : key => nonsensitive(regex("Database=([^;]+)", value)[0]) }, staging = { for key in local.database_keys : key => nonsensitive(regex("Database=([^;]+)", local.staging_render_npgsql)[0]) } }. Passwords were not printed. Result:
production ConnectionStrings__DefaultConnection = orbit_production_9g8l
production ConnectionStrings__SessionConnection = orbit_production_9g8l
staging ConnectionStrings__DefaultConnection = orbit_staging_jo8c
staging ConnectionStrings__SessionConnection = orbit_staging_jo8c
  • A live terraform plan -refresh=false with api_database=render could not complete because RENDER_API_KEY is unset in this shell. Terraform reported Error: Missing Render API Key. The plan therefore did not establish the web service change set.
  • No existing Terraform regression test covers this expression, so an unchanged test result and a failing strengthened test before the fix were unavailable. The state-backed console check verifies the changed expression against the saved Render resource values.
  • dotnet build Orbit.slnx: 0 errors.
  • dotnet test Orbit.slnx --no-build: 6,842 passed, 0 failed.
  • Terraform format and validation passed in both roots; 9 infrastructure tests passed.
  • Saved-state plans with -refresh=false showed no Postgres replacement or web-service update. The planned connection strings named orbit_production_9g8l and orbit_staging_jo8c, with passwords excluded from the output.
  • Orchestrator, refreshed read-only plans with live Render, Cloudflare and AWS credentials and the operator allow lists in the ignored local variable files: the main root plans no change to either render_postgres (no replacement, no allow list removal) and no web service update; the staging-database root plans only render_env_group.staging_database and its link (2 to add, 0 to change, 0 to destroy), keeps the allow list, and its connection string names Database=orbit_staging_jo8c (password redacted). The live GET /v1/postgres values are orbit_production_9g8l/orbit_production and orbit_staging_jo8c/orbit_staging. No existing database-name regression test supplied a pre-fix observation; the pre-fix plan of the staging root read database_name = "orbit_staging_jo8c" -> "orbit_staging" # forces replacement.

External interface evidence

The installed Render provider v1.9.1 schema reports render_postgres.connection_info.internal_connection_string as a computed sensitive string and render_postgres.database_user as a string. terraform state pull, filtered to the two Render Postgres resources, showed internal_connection_string and password in each connection_info object. Parsing only the database path from those saved URLs produced orbit_production_9g8l and orbit_staging_jo8c. This can be repeated without exposing credentials using:

terraform -chdir=infra state pull | jq -c '.resources[] | select(.type == "render_postgres") | {resource: .name, database_from_internal_url: (.instances[0].attributes.connection_info.internal_connection_string | capture("^[^:]+://[^/]+/(?<database>[^/?]+)").database)}'

Assumptions

  • The production switch belongs in the documented cutover setting in infra/local.tfvars; I did not change the api_database default because the Render group must be verified before production uses it.
  • I used Terraform ignore_changes for database_name and database_user so their configured creation values remain usable when staging is replaced. I rejected hard-coding the current suffixed names as creation inputs.
  • Operator IP lists belong in ignored local variables and the staging reseed Actions variable. I rejected committing a real address.

Manual steps

  • In Render Dashboard, open the orbit-staging Postgres connection screen. Before the staging API switches to the linked environment group, use that instance's connection details to dump database orbit_staging with pg_dump -Fc and restore it into orbit_staging_jo8c with pg_restore --clean --if-exists. Compare table counts in the PostgreSQL console, verify the staging API reads the restored data, then drop orbit_staging. A final database list should contain orbit_staging_jo8c and no orbit_staging.
  • In infra/local.tfvars, set the exact key api_database = "render" after confirming production data in the Render Dashboard orbit-production Postgres connection screen. With RENDER_API_KEY and the other documented credentials available, inspect a fresh plan: both production connection keys must name orbit_production_9g8l, and neither web service nor the imported API service may update in place. Apply that approved plan and verify the production API uses the linked orbit-production-api environment group.
  • In Render Dashboard, open service orbit-api > Environment. After verifying the linked group, remove direct service values that duplicate orbit-production-api, including ConnectionStrings__DefaultConnection and ConnectionStrings__SessionConnection, and leave ORBIT_TERRAFORM_ENV_GROUP. The service must remain healthy and use orbit_production_9g8l after those direct values are removed.
  • Copy orbit_staging into orbit_staging_jo8c using credentials from the staging database’s Render Connections screen before applying the staging environment-group change. Compare records, verify staging API reads from the new database, then drop orbit_staging.
  • Set production_postgres_ip_allow_list in infra/local.tfvars and staging_postgres_ip_allow_list in infra/staging-database/local.tfvars from each database’s Render Networking screen. Confirm the plans retain those exact entries.
  • Set STAGING_POSTGRES_IP_ALLOW_LIST in the API repository’s GitHub Settings > Secrets and variables > Actions > Variables to the staging list as JSON. Confirm a reseed plan retains it.
  • Apply the staging root, then the main root after the data copy and plan checks. For the later production cutover, set api_database = "render" in infra/local.tfvars, apply its reviewed plan, and verify the production API connection. Then remove duplicated direct connection variables on the Render production API Environment screen; confirm the linked group still supplies both connections.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important

The staging connection switch needs a documented data migration prerequisite before the first apply.

Reviewed changes The Render database connection strings and cutover instructions were reviewed against the Terraform resources and staging startup settings.

  • Database names: Both API connection strings now use the suffixed database names from the corresponding Render internal URLs and the resource database users.
  • Cutover sequence: The runbook separates initial production environment-group linkage from a later production database switch and direct-variable cleanup.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread infra/configuration.tf Resolved

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important

The staging database name change makes the reseed workflow reject its own replacement database before migration or seeding.

Reviewed changes Reviewed the new Terraform and runbook changes since the prior Pullfrog review.

  • Staging cutover: Added a prerequisite to copy and verify staging data before linking the new database environment group.
  • Database lifecycle: Moved staging connection construction into the staging Terraform root, used the Render URL database name, and preserved operator IP allow lists across both roots and reseeds.
  • Production cutover: Kept the production database switch behind api_database = "render" and documented checks before removing direct service variables.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread infra/staging-database/main.tf Resolved
pullfrog[bot]
pullfrog Bot previously approved these changes Sep 28, 2026

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes Reviewed the staging seed target fix and merged base changes since the prior Pullfrog review.

  • Seed target validation: Accepted the Render-suffixed staging database name while retaining the staging host and user checks, with tests for permitted and rejected targets.

Pullfrog  | View workflow run | Using GPT Sol | 𝕏

SonarCloud S6444 and SYSLIB1045 on the seed target check: the pattern is now
a GeneratedRegex with a one second timeout.

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

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes Reviewed the staging seed database-name matcher change since the prior Pullfrog review.

  • Bounded matcher: Replaced the dynamic regex with an equivalent source-generated matcher with a one-second timeout; the staging host and user checks remain unchanged.

Pullfrog  | View workflow run | Using GPT Sol | 𝕏

@sonarqubecloud

Copy link
Copy Markdown

@thomasluizon
thomasluizon merged commit 0fd416f into main Sep 28, 2026
27 checks passed
@thomasluizon
thomasluizon deleted the fix/ticket-819-render-db-names branch September 28, 2026 06:03
thomasluizon added a commit that referenced this pull request Sep 28, 2026
…) (#635)

* Guard web service Terraform plans before apply (#631)

* Guard Terraform web service updates before apply

* Guard Terraform web service replacement and deletion

(cherry picked from commit 7a1e26a)

* Add manual API release workflow for production and staging (#632)

(cherry picked from commit 498015a)

* Add Amazon SES delivery and signed event handling (#628)

* Add SES identities, DNS, and delivery infrastructure

* Add SES sender and signed SNS event handling

* Classify SES webhook and clarify email contract

* Fix SES cutover controls and webhook verification

* Extract shared email composition for SES and Resend

* Isolate staging SES events and sender credentials

* Escape interpolated names in the SES isolation test patterns

CodeQL js/incomplete-hostname-regexp flagged the tuple values flowing into
new RegExp; every interpolated value is now escaped as a literal.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f770b47)

* Keep staging awake and reseed its free database (#626)

* Add staging keepalive and database reseed lifecycle

* Document and strengthen staging seed command

* Keep Terraform plan details out of workflow logs

* Require successful health responses in staging workflows

* Parse Render creation time portably

* Isolate staging database state and gate reseed on deploy

* Read the staging database id from the main state before moving it

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

* Correlate staging reseed deploy with live commit

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ee40ae9)

* fix: point apex DNS at Render landing site (#634)

(cherry picked from commit 65d355f)

* Move uploads to private S3 with stable read links (#625)

* Add configurable S3 upload signing

* Provision private S3 upload buckets and scoped credentials

* Expose storage provider cutover as Terraform variables

* Add stable read redirects for private S3 uploads

* Map public upload reads in agent catalog

* Fix upload read validation and S3 bucket safeguards

* Keep S3 upload reads available across provider rollback

* Bound anonymous upload reads per client IP

(cherry picked from commit 395239f)

* Fix Render database names in API connection strings (#633)

* fix: derive Render database names from connection URLs

* Generate the staging database name pattern with a match timeout

SonarCloud S6444 and SYSLIB1045 on the seed target check: the pattern is now
a GeneratedRegex with a one second timeout.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 0fd416f)

* Complete carried error copy and generated gating matrix

* Keep SES event notifications recoverable (#746)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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