carlos / platform Public

Merge pull request #338 from carlosframework/rails-poc-hostname-postscript

spec: Rails PoC postscript — hostname configuration closed, edge hostnames live
Paul Campbell pushed by paul@keymail.dev 6e189a5c455694ba5b464c8ca0ba1b8b937c1adb parent 9ee32f6parent ba48f92
1 file changed, +35 −0
  • docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md +35 −0
diff --git a/docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md b/docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md
index 04eacc6e6..d5af7a782 100644
--- a/docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md
+++ b/docs/superpowers/specs/2026-08-11-rails-behind-carlos-edge-poc-design.md
@@ -258,6 +258,41 @@ the plumbing, not a finished path to production traffic.
deployment should answer to, which is a migration-design question, not
an infrastructure one.
+**2026-08-28 — hostname configuration closed; the edge hostnames serve
+the real apps.** Both turned out to be env-only, applied in the boxes'
+own SSM params (which the k8s deployment never reads), so the change is
+box-scoped and reverts by reverting two values:
+
+- Classic's 403 was stock Rails `ActionDispatch::HostAuthorization`,
+ allowlist fed entirely from `*_HOST` env vars in
+ `config/application.rb`. `HEALTHCHECK_HOST` is the one var with no
+ second meaning anywhere in the app (no `AppConfig.host` branch, no
+ route constraint, no throttle rule), so the box sets
+ `HEALTHCHECK_HOST=classic.platform.tito.io` and the deploy script's
+ health probe follows it. `ALTERNATIVE_PRIMARY_HOSTS` was rejected: it
+ admits a host and then 301s every path to `PRIMARY_HOST`. Result:
+ `classic.platform.tito.io` → 302 into Tito ID SSO — Classic's real
+ root behavior. Completing a sign-in through the new hostname would
+ additionally need it in the staging ID service's OAuth redirect
+ allowlist — shared state, deliberately untouched.
+- Pro's 404 was the custom-domain catch-all (`Custom::BaseController`
+ raising RecordNotFound for a host that is neither a configured
+ platform host nor a tenant domain). The primary host is anyway_config
+ (`UrlsConfig`), ENV over `urls.yml`, so the box sets
+ `URLS_PRIMARY=pro.platform.tito.io` and the whole primary-domain
+ route tree, CSP, ActionCable origins and URL generation follow.
+ `URLS_SECONDARY` was rejected: the app 302s every secondary-host
+ request to primary. Result: `pro.platform.tito.io` → 200 with the
+ real primary app. Known consequence, accepted as an honest cutover
+ preview: the box's sidekiq shares staging's queue and builds absolute
+ URLs from its own `URLS_PRIMARY`, so staging mails whose jobs run on
+ the box carry working `pro.platform.tito.io` links.
+
+The migration-design lesson: neither app needed code or shared-state
+changes to answer on a new hostname — a real cutover's hostname story
+is an env rollout, plus the ID service's OAuth redirect allowlist for
+Classic sign-in. Ops record: teamtito/carlos-infrastructure PR #10.
+
## Out of scope
- Any platform code change (release kinds, primitives) — the PoC is