feat(cors): always trust the service's own RAILWAY_PUBLIC_DOMAIN origin - #85
Draft
PetrefiedThunder wants to merge 1 commit into
Draft
feat(cors): always trust the service's own RAILWAY_PUBLIC_DOMAIN origin#85PetrefiedThunder wants to merge 1 commit into
PetrefiedThunder wants to merge 1 commit into
Conversation
The August 2026 cutover proved that a *configured* CORS allowlist can be worse than none: REGENGINE_CORS_ORIGINS was set — to the previous service's URL, via a Railway reference variable — so the new service rejected every browser request from its own domain for three days, and because auth_middleware gates state-changing requests on the same list, writes failed too (#80, #81). Append the platform-issued origin (https://$RAILWAY_PUBLIC_DOMAIN) to whatever cors_origins_from_env() resolves. A union rather than a fallback, deliberately: the incident had the variable set-but-stale, so a fallback that only applies when the variable is missing would have changed nothing. Trusting the platform domain widens nothing beyond the service's own canonical origin, and anyone who can forge that variable already controls the deployment. A malformed platform value degrades to "no extra origin" instead of raising — this path runs while the ASGI app is constructed, and a platform-injected string must never be able to crash startup. Explicit origins for third-party dashboard hosts still require configuration; the cutover checklist keeps that step and notes the self-trust behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014ykFQkKR1XmCtSkDRCT4sT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the demo service trust its own platform-issued origin (
https://$RAILWAY_PUBLIC_DOMAIN) in addition to whateverREGENGINE_CORS_ORIGINSresolves to. This is the code half of the August cutover lesson — the docs half landed in #84.Why a union and not a fallback: in the incident, the variable was set — to the previous service's URL, via a Railway reference variable. A fallback that only applies when the variable is missing would have changed nothing. With the union, a stale configured value can no longer lock the service out of its own domain, which is exactly what kept both nightly smokes red for three days (#80, #81) — the same list also gates state-changing requests in
auth_middleware, so browser writes failed too.Why it's safe: the appended origin is only the service's own canonical domain, injected by Railway. Anyone who can forge
RAILWAY_PUBLIC_DOMAINalready controls the deployment, so this widens trust by nothing. Wildcards remain rejected; explicit origins for any other dashboard host still require configuration (the cutover checklist keeps that step, with a note about the new behavior).Robustness: a malformed platform value degrades to "no extra origin" rather than raising —
cors_origins_from_env()runs while the ASGI app is constructed, and a platform-injected string must never be able to crash startup.Changes
app/cors.py— append the normalized platform origin post-parse; new_platform_origin()helper with a never-raise contract.tests/test_api.py— three scenarios: platform origin joins defaults when unconfigured; stale-explicit-list can't exclude the own domain (the Remote Smoke is failing against the shared demo #80/Remote Browser Smoke is failing against the shared demo #81 regression, now pinned); no duplicate when already listed. Plus a never-crashes test for malformed/blank platform values.DEPLOYMENT_PROFILES.md— one-line note on the self-trust behavior in the cutover checklist.Test Plan
uv run --frozen pytest)🤖 Generated with Claude Code
https://claude.ai/code/session_014ykFQkKR1XmCtSkDRCT4sT
Generated by Claude Code