Skip to content

Carry main infrastructure and auth fixes into redesign main (#746) - #630

Merged
thomasluizon merged 8 commits into
redesign/mainfrom
fix/ticket-746-carry-batch-m
Sep 28, 2026
Merged

thomasluizon merged 8 commits into
redesign/mainfrom
fix/ticket-746-carry-batch-m

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Change

Carries the seven specified main commits, in order, into redesign/main with git cherry-pick -x. Links thomasluizon/orbit-tickets#746. The standing ticket stays open.

None of the seven commits was already carried: each showed + in git cherry -v immediately before its cherry-pick, and each corresponding feature or file was absent from the current redesign tree. All seven resulting commits retain their source hash in a cherry picked from commit trailer.

Conflict verdicts

Both content conflicts came from the redesign error copy change in 23396af4 (#532):

  • src/Orbit.Application/Waitlist/Commands/ConfirmWaitlistCommand.cs: kept redesign's InvalidWaitlistConfirmation error and added main's signed language extraction, canonicalization, and contact language persistence. Both invalid token and invalid language return the shared error.
  • src/Orbit.Application/Common/ErrorMessages.cs: kept redesign's ErrorCopy backed catalog and added GoogleRedirectUriNotAllowed and GoogleCodeExchangeFailed. Added the two corresponding entries in ErrorCopy.cs so English and Portuguese lookups remain complete.

The new assertion in tests/Orbit.Infrastructure.Tests/Controllers/AuthControllerTests.cs expected lower case object members. It was corrected to assert the existing PascalCase error response and HTTP 401. The controller behavior remains the one already used by GoogleAuth.

Migration order

20260927211258_AddMarketingContacts follows the redesign and previously carried migrations, ending with 20260927014645_DropFoldDueTimeScheduledRemindersTrigger. dotnet ef migrations list returned one ordered history and exited 0. The local worktree has no database connection, so applied status was unavailable. dotnet ef migrations has-pending-model-changes exited 0 and reported no model changes. The migration uses EF's CreateTable and CreateIndex methods; no raw index SQL was added.

External interface evidence

  • PR #622 records the installed Google SDK package XML for TokenResponse.FromHttpResponseAsync, token properties, signature validation settings, and email claims. The exchange service delegates response parsing to that SDK.
  • PR #621 records the Render and AWS provider schemas and a read only Render service response. PR #623 records the Cloudflare, AWS, and Render provider fields used by the DNS and Turnstile resources. PR #627 records the installed Render image digest schema used by ignore_changes. This worktree installed the pinned providers and terraform validate exited 0.
  • PR #624 records the Render deploy response and status fields, live deploy list projections, GitHub Deployment schema, and migration bundle CLI evidence used by the release workflow.
  • PR #629 records the installed Google Android Publisher SDK TestPurchase marker and AWS SSM value schema used by the carried billing change.

Assumptions

  • Added the new Google errors to redesign's localized ErrorCopy catalog; rejected restoring main's inline English error catalog because it would replace redesign behavior.
  • Kept the source Terraform configuration as a code carry; rejected a live infrastructure apply from this worktree because that operation needs the operator's credentials, state, and plan review.

Manual steps

  • Before the staging API uses Google code sign in, confirm Google__AllowedRedirectUris__0 is https://staging.useorbit.org/auth-callback and Google__ClientId and Google__ClientSecret are configured in Render > orbit-staging-api > Environment. Add that callback in Google Cloud Console > APIs & Services > Credentials > OAuth 2.0 client > Authorized redirect URIs. A successful code exchange for that exact URI proves both settings.
  • In AWS Systems Manager Parameter Store in us-east-2, confirm /orbit/staging/api/Stripe__ProProductId, /orbit/staging/api/Stripe__MonthlyPriceIdUsd, /orbit/staging/api/Stripe__YearlyPriceIdUsd, /orbit/staging/api/Stripe__MonthlyPriceIdBrl, and /orbit/staging/api/Stripe__YearlyPriceIdBrl exist as String parameters, with /orbit/staging/api/Stripe__SecretKey and /orbit/staging/api/Stripe__WebhookSecret as SecureString test values. A Terraform plan resolving all seven and mapping them to orbit-staging-api proves the inputs exist.
  • In the operator shell, provide RENDER_API_KEY, CLOUDFLARE_API_TOKEN, AWS credentials, and ignored infra/local.tfvars; review and apply the Terraform plan. Verify Render's orbit-staging-api environment group has Database__MigrateOnStartup=true, the new billing keys, and BotProtection__SecretKey. Verify the turnstile_site_key and cloudflare_name_servers Terraform outputs. The production API environment must retain Database__MigrateOnStartup=false and ./efbundle as its predeploy command.
  • If the redesign staging audience has confirmed contacts in Resend, export their verified fields through the Resend Contacts API and backfill the staging MarketingContacts table in the Render Postgres database, preserving opt outs. Matching distinct normalized email counts and sampled unsubscribe and suppression timestamps prove the import.

Test evidence

  • env -u LANG LC_ALL=en_US.UTF-8 dotnet build Orbit.slnx -v quiet: exit 0, zero errors.
  • env -u LANG LC_ALL=en_US.UTF-8 dotnet test: exit 0, 7,917 passed, zero failed across four projects. An earlier run found one failing carried controller assertion; its focused rerun passed 1/1 after the response shape correction.
  • terraform fmt -check -recursive infra: exit 0. terraform -chdir=infra init -backend=false -input=false: exit 0. terraform -chdir=infra validate: exit 0.
  • dotnet ef migrations list --project src/Orbit.Infrastructure --startup-project src/Orbit.Api --no-build: exit 0. dotnet ef migrations has-pending-model-changes with the same project options: exit 0, no pending changes.
  • node tools/arch-map.mjs, actionlint over the three changed workflows, root allowlist, dash baseline, timeless, suppression allowlist, and git diff --check: all passed. dotnet build regenerated src/Orbit.Api/openapi.json after the last cherry-pick, with no further diff.

thomasluizon and others added 8 commits September 27, 2026 21:35
* Add Terraform for Render production and staging

* Handle Render database URLs without explicit ports

* Keep production intact through the first apply and isolate staging integrations

The imported API service ignores its own env_vars so the first apply only
adds and links the environment group, and the existing Render project is
imported (environments keyed by their live names) so the API stays in its
Production environment while a Staging environment is added. Staging gets
placeholder Stripe identifiers, staging return URLs, its own redirect
allowlist and an invalid Supabase host, so it cannot touch production
billing or storage. Empty custom domain lists become null, the landing no
longer auto-deploys, and the web health check uses /api/health.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 1480d1b)
* feat: store confirmed waitlist marketing contacts locally

* fix: preserve marketing opt-outs and canonicalize waitlist languages

* Restore user marketing delivery after explicit opt-in

(cherry picked from commit 4c7b76a)
* Implement Google authorization code sign in for ticket 796

* Register Google code routes in agent catalog

* Parse Google token responses with installed SDK

* fix: retry Google code sign-in after redemption

(cherry picked from commit 2301ccc)
* Add Cloudflare DNS and Turnstile Terraform resources

* Verify Cloudflare DNS answers before the registrar switch

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7e946a6)
* Add gated production API release workflow

* Handle queued Render deploys and recheck the live commit before recording a release

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 78b42f2)
* fix: preserve workflow deployed web digests in Terraform

* docs: guard web service applies against Render image tag bug

(cherry picked from commit b8ea2c1)

@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

Preserve existing marketing opt-outs before switching broadcast audiences, and prevent Terraform applies from replacing approved web image digests.

Reviewed changes across the eight commits carrying main infrastructure and auth changes into redesign/main:

  • Infrastructure: Adds Render, AWS, Cloudflare, and Turnstile Terraform resources, production API deployment workflow, and EF migration bundle.
  • Marketing: Moves waitlist contacts into the API database and incorporates them into broadcast, consent, unsubscribe, and account deletion flows.
  • Authentication: Adds Google authorization-code sign-in and localized errors; checked its endpoint contract against the paired consumer PR orbit-ui-mobile#1219.
  • Verification: Adds unit coverage, Terraform validation, and Docker bundle checks.

⚠️ Existing marketing opt-outs are missing from the new broadcast audience

The new MarketingContacts table starts empty while the existing waitlist audience lives in Resend. Switching IMarketingContactsService to database storage does not transfer Resend's unsubscribe or suppression flags: an address that opted out in Resend can reconfirm in the database and receive a broadcast. The PR only makes a staging backfill conditional; production needs a consent-preserving transition before database-backed broadcasts are enabled.

Technical details
# Preserve marketing consent during the audience cutover

## Affected sites
- `src/Orbit.Infrastructure/Migrations/20260927211258_AddMarketingContacts.cs:14` creates an empty contact table.
- `src/Orbit.Api/Extensions/ServiceCollectionExtensions.Infrastructure.cs:72` switches contact persistence to that table.
- `src/Orbit.Infrastructure/Services/DatabaseMarketingContactsService.cs:16` creates a new active contact if none exists locally.
- `src/Orbit.Application/Marketing/Commands/SendMarketingBroadcastCommand.cs:66` sends to locally eligible waitlist contacts without consulting historical Resend opt-outs.

## Required outcome
- Import and verify existing confirmed contacts and their opt-out/suppression state before any database-backed broadcast can reach them, in every affected environment, or retain an equivalent authoritative suppression check through the transition.

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

Comment thread infra/production.tf
@thomasluizon

Copy link
Copy Markdown
Owner Author

Disposition of the review finding on infra/production.tf:52: filed as thomasluizon/orbit-tickets#814, fixed on main first and carried by the next #746 sync. This pull request only carries main unchanged.

@thomasluizon

Copy link
Copy Markdown
Owner Author

@pullfrog review

@pullfrog

pullfrog Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
  • Read authoritative PR diff and metadata end to end
  • Trace changed behavior against base, callers and sibling contracts
  • Submit one evidence-backed review

Pullfrog  | View workflow run | via Pullfrog | Using GPT Sol | 𝕏

@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 since the prior Pullfrog review: no new commits landed; rechecked the existing eight-commit carry and its resolved discussion.

  • Rechecked infrastructure: Confirmed the Terraform digest concern is resolved in discussion and tracked for the next carry as thomasluizon/orbit-tickets#814.
  • Rechecked marketing and authentication: Found no new changes to the database-backed audience or Google authorization-code flow.

Pullfrog  | View workflow run | Using GPT Sol | 𝕏

@thomasluizon
thomasluizon merged commit e1bbb37 into redesign/main Sep 28, 2026
22 checks passed
@thomasluizon
thomasluizon deleted the fix/ticket-746-carry-batch-m branch September 28, 2026 01:23
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