Merge pull request #338 from carlosframework/rails-poc-hostname-postscript
spec: Rails PoC postscript — hostname configuration closed, edge hostnames live
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 |