Skip to content

Carry release, SES, S3 and database name fixes into redesign main (#746) - #635

Merged
thomasluizon merged 9 commits into
redesign/mainfrom
fix/ticket-746-carry-release-ses
Sep 28, 2026
Merged

thomasluizon merged 9 commits into
redesign/mainfrom
fix/ticket-746-carry-release-ses

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Refs thomasluizon/orbit-tickets#746. This is the standing API sync ticket; keep it open.

Change

Carry the seven specified main fixes into redesign/main by cherry-pick with source references:

The followup 0ab1112d adds the redesign error copy required by the carried staging habit guard and regenerates gating-matrix.json. src/Orbit.Api/openapi.json was regenerated by the API build after the picks. EF reports no pending model changes; these picks add no migration. There is no migration ordering question.

Conflict resolution

  • src/Orbit.Infrastructure/Services/ResendEmailService.cs: kept main's shared EmailServiceBase extraction so SES and Resend use one composer, while retaining the redesign subject copy, HTML layout, marketing styling, support labels, Resend retry behavior, unsubscribe headers, and test account handling in EmailMessageComposer and the provider service.
  • tests/Orbit.Infrastructure.Tests/Services/ResendEmailServiceTests.cs: retained the full redesign suite, including localized copy, plain text, HTML escaping, and layout checks. The main provider and composer cases remain covered by the new EmailMessageComposerTests and SesEmailServiceTests; their subject expectations now match the redesign copy. No test behavior was dropped.

Test evidence

  • env -u LANG LC_ALL=en_US.UTF-8 dotnet build Orbit.slnx --verbosity quiet: exit 0, zero errors.
  • env -u LANG -u LC_ALL dotnet test --verbosity quiet: exit 0, 7,995 passed.
  • env -u LANG LC_ALL=en_US.UTF-8 dotnet test --verbosity quiet: exit 0, 7,995 passed.
  • terraform fmt -check -recursive infra: exit 0. After backendless provider initialization, terraform -chdir=infra validate -no-color and terraform -chdir=infra/staging-database validate -no-color: exit 0.
  • dotnet ef migrations has-pending-model-changes --project src/Orbit.Infrastructure --startup-project src/Orbit.Api: exit 0, no pending model changes.
  • node --test infra/check-web-plan.test.mjs infra/ses-isolation.test.mjs: 9 passed. Focused email and event tests: 62 passed. Focused S3 and staging tests: 25 passed.
  • node tools/arch-map.mjs: 198 endpoints, zero orphan endpoints, zero untested feature folders. node tools/gating-matrix.mjs regenerated the tracked matrix.
  • The unchanged ErrorCopyTests.EveryDomainErrorConstantHasCopy failed with INVALID_CREATED_AT_UTC after the staging carry. After adding its bilingual copy, dotnet test tests/Orbit.Application.Tests --filter 'FullyQualifiedName~ErrorCopyTests' passed all 716 cases. The existing test caught the defect, so no strengthened test was needed.
  • Dash, timeless, and git diff --check checks passed.
  • Existing SES processor tests passed with the defect present: 9 passed.
  • Strengthened CertificateFetchFailureIsRetryable failed with the defect present for both HTTP and transport failures: no exception was thrown. After the fix, the focused SES and controller tests passed: 16 passed.
  • dotnet build Orbit.slnx: zero errors in both requested locale settings. Full dotnet test: 8,001 passed in each setting.
  • Terraform format and validation passed. EF reported no pending model changes. The worktree is clean.

External interface evidence

The source PRs record the observed external fields and reproduction commands. Their evidence was checked with gh pr view <number> --json body before this carry:

  • #631 and #632 cover the Terraform plan JSON, Render service and deploy fields, and GitHub deployment fields.
  • #628 covers the installed SES SDK and Terraform provider fields plus SNS and SES event shapes.
  • #626 covers Render Postgres and deploy fields, Terraform state and plan fields, and GitHub OIDC fields.
  • #634 records the DNS answers consumed by the cutover check.
  • #625 covers the installed S3 SDK signed URL method and an observed offline signed URL.
  • #633 covers the Render provider connection fields and observed database names from saved state.

Manual steps

These carried infrastructure changes retain the activation steps in their source PRs. Before enabling them on redesign deployments:

  • For SES, use AWS SES in us-east-2 to confirm production access and verified send.useorbit.org and updates.useorbit.org identities. In infra/local.tfvars, set production_email_provider or staging_email_provider to Ses only after the reviewed Terraform apply. Confirm Email__Provider=Ses in the corresponding Render API Environment Group and a confirmed SNS HTTPS subscription. See #628.
  • For staging reseed, configure RENDER_API_KEY in GitHub Actions Secrets and SEED_OWNER_EMAIL, STAGING_ENVIRONMENT_ID, STAGING_API_SERVICE_ID, and STAGING_POSTGRES_IP_ALLOW_LIST in GitHub Actions Variables. Complete the documented Terraform state transfer and apply before running Actions > Staging reseed. A successful migration, live deploy, and seed run prove readiness. See #626 and #633.
  • For apex DNS, set dns_apex_target = "orbit-landing-aaa7.onrender.com" in infra/local.tfvars and apply only a reviewed apex plan. Confirm the flattened A answers on both Cloudflare nameservers and the verified useorbit.org custom domain in Render > orbit-landing > Settings > Custom Domains. See #634.
  • For S3, apply the reviewed infra/ plan, then set production_storage_provider or staging_storage_provider to S3 only after the web client dependency is deployed. Confirm Storage__Provider=S3 and the Storage__S3__Bucket, Storage__S3__Region, Storage__S3__PublicBaseUrl, Storage__S3__AccessKeyId, and Storage__S3__SecretAccessKey keys in Render > Environment Groups. A signed PUT and read through publicUrl prove the cutover. See #625.
  • For Render database cutover, complete the staging data copy described in #633. Set api_database = "render" in infra/local.tfvars only after verifying the production database in Render > Postgres > Connections. Confirm both ConnectionStrings__DefaultConnection and ConnectionStrings__SessionConnection in the linked Render API environment group name the correct database and that the API remains healthy.
  • The orchestrator must push this commit and complete review on the existing PR. This worker did not push or open a PR.
  • Before enabling SES, apply the reviewed infra Terraform plan. In AWS SQS Queues, verify orbit-ses-events-dead-letter and orbit-ses-events-staging-dead-letter; in SNS Subscriptions, verify each webhook’s redrive policy points to its matching queue.
  • Deploy the carrying API commit through GitHub Actions Release API (release.yml) for each intended environment. Verify the Render orbit-api or orbit-api-staging service runs that release.
  • For an approved SES cutover, set production_email_provider or staging_email_provider to Ses in infra/local.tfvars, apply the reviewed plan, and verify the corresponding Render environment has Email__Provider=Ses and its signed SNS webhook succeeds.

Assumptions

  • Certificate HTTP errors, transport failures, and timeouts are retryable. Alternative rejected: returning 400 for those dependency failures.
  • Replay republishes the original event through its matching SNS topic after inspecting the actual queue message. Alternative rejected: posting queue content directly to the signed webhook.

thomasluizon and others added 8 commits September 28, 2026 03:21
* Guard Terraform web service updates before apply

* Guard Terraform web service replacement and deletion

(cherry picked from commit 7a1e26a)
* 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)
* 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)
* 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: 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)

@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 SES event path can permanently lose valid bounce and complaint notifications during transient certificate failures or an API outage. Please address both delivery paths before enabling SES.

Reviewed changes Carried release automation, staging database lifecycle, DNS and Terraform guards, SES delivery and event handling, S3 upload storage, the email composer integration, and their tests into redesign/main.

  • Releases and infrastructure: Manual production and staging Render releases, a web service plan guard, apex DNS cutover, staging keepalive and reseed, and database connection name fixes.
  • Email: SES identities, credentials and event routing, signed SNS processing and marketing suppression, with shared composition for the SES and Resend providers.
  • Uploads: Private S3 uploads and stable API read redirects alongside the existing Supabase write path, plus provider configuration and focused tests.
  • Redesign carry: Historical habit seed guards, bilingual error copy, OpenAPI output and gating matrix regeneration.

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

Comment thread infra/ses.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.

✅ No new issues found.

Reviewed changes Re-reviewed the SES event recovery changes in 80d8e7d7 against the full PR diff.

  • Retryable verification: Distinguished temporary SNS certificate fetch failures from invalid messages and returned HTTP 503 for the former, with focused tests for HTTP, transport, and timeout failures.
  • Recoverable delivery: Added separate encrypted SQS dead-letter queues and SNS redrive policies for production and staging, plus a documented replay procedure and Terraform checks.

Pullfrog  | View workflow run | Using GPT Sol | 𝕏

@thomasluizon
thomasluizon merged commit 180e9dc into redesign/main Sep 28, 2026
22 checks passed
@thomasluizon
thomasluizon deleted the fix/ticket-746-carry-release-ses branch September 28, 2026 07:40
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