Skip to content

Harden prod Railway runtime secrets#94

Open
willjy1 wants to merge 2 commits into
BAWES-Universe:masterfrom
willjy1:codex/harden-prod-railway-runtime-secrets
Open

Harden prod Railway runtime secrets#94
willjy1 wants to merge 2 commits into
BAWES-Universe:masterfrom
willjy1:codex/harden-prod-railway-runtime-secrets

Conversation

@willjy1
Copy link
Copy Markdown

@willjy1 willjy1 commented May 16, 2026

Related to #55

/claim #55

Scope

This is a narrow production Railway runtime-secret hardening slice. It does not touch S3/AWS keys, SQS/EventManager, MediaConvert, SES/mailers, Xero, Cloudinary, Wallet/Yeaster service tokens, OneSignal, Civil ID upload/remove flows, live production services, database access, candidate data, or real credential values.

Summary

  • Move prod Railway primary DB, wallet DB, Redis, and Sentry DSN values from committed config literals to runtime environment variables.
  • Document the required Railway variables without including secret values.
  • Add a static regression guard for the production connection-secret contract.

Verification

py -3 tests\check-prod-railway-runtime-secrets.py
# Production Railway runtime secret check passed.

py -3 -m py_compile tests\check-prod-railway-runtime-secrets.py

git diff --check HEAD~1 HEAD

php -l could not be run here because PHP is not installed in this environment. The PHP change is limited to replacing literal config values with existing getenv(...) style config expressions.

Safety boundary

No live Railway, AWS/IAM/S3, database, Redis, Sentry, candidate data, private exports, screenshots, or credential values were accessed or included in docs/comments.

Transparency: automation-assisted; I reviewed the diff and verification before submitting.

Summary by CodeRabbit

  • Documentation

    • Added a guide listing required production environment variables for Railway deployments (primary and wallet databases, Redis, Sentry) and instructions for storing secrets in Railway's UI.
  • Configuration

    • Production configuration now reads database, Redis, and Sentry settings from environment variables instead of hardcoded defaults.
  • Tests

    • Added an automated validation script to ensure required runtime variables are both referenced in config and documented.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 41101ddd-8af8-4af7-ab38-3da5f5671222

📥 Commits

Reviewing files that changed from the base of the PR and between c9cbe9d and f5fcf1c.

📒 Files selected for processing (1)
  • tests/check-prod-railway-runtime-secrets.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/check-prod-railway-runtime-secrets.py

📝 Walkthrough

Walkthrough

This PR externalizes hardcoded production secrets to Railway environment variables, documents the required variable names, updates the production config to read them, and adds a validation script ensuring config and docs stay consistent.

Changes

Production Railway Secrets Externalization

Layer / File(s) Summary
Production Secrets Specification
docs/prod-railway-runtime-secrets.md
Documents required Railway environment variables for primary database (DB_DSN, DB_USERNAME, DB_PASSWORD), wallet database (WALLET_DB_DSN, WALLET_DB_USERNAME, WALLET_DB_PASSWORD), Redis (REDIS_HOSTNAME, REDIS_USERNAME, REDIS_PASSWORD, with optional REDIS_PORT and REDIS_DATABASE defaults), and Sentry (SENTRY_DSN). Advises storing real secret values outside version control.
Environment Variable Configuration
environments/prod-railway/common/config/main-local.php
Production db and walletDb connections now read dsn, username, and password from environment variables. redis settings use REDIS_HOSTNAME, REDIS_USERNAME (nullable), REDIS_PASSWORD, and integer-cast REDIS_PORT/REDIS_DATABASE with defaults 6379 and 0. Sentry dsn now reads from SENTRY_DSN (or null).
Runtime Secrets Validation Test
tests/check-prod-railway-runtime-secrets.py
New script asserts the PHP config references expected getenv('VAR') usages and that the docs list the same backticked variable names; it also validates db/walletDb mappings, redis hostname/password usage, and the exact Sentry dsn expression.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through configs, docs, and tests,
Secrets wrapped in names, no hardcoded nests,
Railway keeps the keys, the code reads them kind,
A tidy trail for reviewers to find,
Carrots and checks for peace of mind.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Harden prod Railway runtime secrets' directly and clearly describes the main change: moving production Railway secrets from hardcoded values to runtime environment variables.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot
Copy link
Copy Markdown

dosubot Bot commented May 16, 2026

Related Knowledge

1 document with suggested updates is ready for review.

BAWES Universe

Production Operations & Deployments Runbook
View Suggested Changes
@@ -39,10 +39,10 @@
 ### Backend (Railway)
 
 - **Build & Deploy**: Uses Railway CLI. Set `RAILWAY_DOCKERFILE_PATH` to `./Dockerfile-nginx-dev-railway` (dev) or `./Dockerfile-nginx-railway` (prod). Use `railway login`, `railway link`, and `railway up` to deploy. [Reference](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/railway/railway.md#L1-L34)
-- **Required Env Vars**: Set via Railway dashboard or `.env` files (not in repo).
+- **Required Env Vars**: Set via Railway dashboard or `.env` files (not in repo). Production Railway requires `DB_DSN`, `DB_USERNAME`, `DB_PASSWORD`, `WALLET_DB_DSN`, `WALLET_DB_USERNAME`, `WALLET_DB_PASSWORD`, `REDIS_HOSTNAME`, `REDIS_PASSWORD`, `REDIS_USERNAME` (optional), `REDIS_PORT` (optional, defaults 6379), `REDIS_DATABASE` (optional, defaults 0), and `SENTRY_DSN`. See [prod-railway-runtime-secrets.md](https://github.com/BAWES-Universe/studenthub/blob/main/docs/prod-railway-runtime-secrets.md) for the full list.
 - **Database Connectivity**: 
   - Dev: MySQL (host=mysql, db=studenthub, user=studenthubuser, pass=12345)
-  - Prod: AWS RDS (host=studenthub-prod.cluster-c8mekjvvbygf.eu-west-2.rds.amazonaws.com, db=studenthub, user=bawes, pass=bawes12student!hub)
+  - Prod Railway: Configured via environment variables (`DB_DSN`, `DB_USERNAME`, `DB_PASSWORD`, `WALLET_DB_DSN`, `WALLET_DB_USERNAME`, `WALLET_DB_PASSWORD`) set in Railway dashboard
 - **Logs**: Use `docker logs <container_id>` or Railway dashboard logs. [Reference](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/README.md#L9-L174)
 
 ### Admin Frontend (CircleCI → S3/CloudFront)
@@ -115,7 +115,7 @@
 
 ## Observability & Incident Response
 
-- **Sentry Logging**: Configured in backend environment files (`environments/*/common/config/main-local.php`) with DSN `https://6cbd2100e1ff41e7875352655ffbf50d:e18336b09d864b29aa12aca3fbc6706c@sentry.io/168200` for error/warning levels, tagged by environment (`dev`, `dev-server`, `production`). [Reference](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/environments/dev/common/config/main-local.php#L1-L124)
+- **Sentry Logging**: Configured in backend environment files (`environments/*/common/config/main-local.php`) for error/warning levels, tagged by environment (`dev`, `dev-server`, `production`). Production Railway Sentry DSN is configured via the `SENTRY_DSN` environment variable set in the Railway dashboard. [Reference](https://github.com/BAWES-Universe/studenthub/blob/ca90a5502040ba8191fe9f80d29d0b6a4d2a6152/environments/dev/common/config/main-local.php#L1-L124)
 - **Slack Logging**: Configured for info/warning levels for admin, candidate, company, staff, remail, common, console categories.
 - **What to Check First**: Sentry project for stack traces (environment tags), Slack channels for notifications, `~/logs` for cron and migration logs.
 - **Common Incidents**: No documentation found in repos for email not sending, PDF export 500, or transfers stuck. Debugging steps must be sourced from Sentry, Slack, Railway, Netlify, or AWS dashboards.
@@ -130,9 +130,9 @@
 
 ## Day-1 Ops Checklist
 
-- Verify Railway deployments for dev/prod: run `railway link`, confirm `RAILWAY_DOCKERFILE_PATH`, inspect AWS RDS/Redis connections in `environments/prod/common/config/main-local.php`.
+- Verify Railway deployments for dev/prod: run `railway link`, confirm `RAILWAY_DOCKERFILE_PATH`, verify production environment variables (`DB_DSN`, `DB_USERNAME`, `DB_PASSWORD`, `WALLET_DB_DSN`, `WALLET_DB_USERNAME`, `WALLET_DB_PASSWORD`, `REDIS_HOSTNAME`, `REDIS_PASSWORD`, `SENTRY_DSN`) are set in Railway dashboard as documented in [prod-railway-runtime-secrets.md](https://github.com/BAWES-Universe/studenthub/blob/main/docs/prod-railway-runtime-secrets.md).
 - Confirm CircleCI contexts: ensure `org-global` context holds AWS credentials for admin/staff S3/CloudFront buckets as listed in `.circleci/config.yml`.
-- Check Sentry project (`dsn ...@sentry.io/168200`) for alerting rules tied to `environment` tags and ensure Slack logger targets the correct workspace.
+- Check Sentry project for alerting rules tied to `environment` tags and ensure Slack logger targets the correct workspace. Production Railway Sentry DSN is configured via environment variable in the Railway dashboard.
 - Review cron job logs under `~/logs` and trigger key jobs manually (e.g., `php ~/www/yii cron/daily`) after verifying cache flush steps from README.
 - Snapshot MySQL (prod RDS) via `railway connect mysql` and push `.sql` dump to the appropriate S3 bucket (`studenthub-uploads`).
 - Confirm candidate & company frontend deployment channels by logging into their hosting dashboards (Netlify/AWS) and documenting build steps.

[Accept] [Edit] [Decline]

How did I do? Any feedback?  Join Discord

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/check-prod-railway-runtime-secrets.py (1)

15-20: ⚡ Quick win

Harden component_block against formatting drift and missing markers.

This parser currently fails with an unhandled exception if a component marker is absent and is tightly coupled to exact indentation, which can cause noisy false failures.

Proposed patch
 def component_block(config, component):
     marker = f"'{component}' => ["
-    start = config.index(marker)
-    next_component = re.search(r"\n        '[^']+' => \[", config[start + len(marker):])
+    start = config.find(marker)
+    require(start != -1, f"Component '{component}' must exist in config.")
+    next_component = re.search(r"\n\s{8}'[^']+'\s*=>\s*\[", config[start + len(marker):])
     end = start + len(marker) + next_component.start() if next_component else len(config)
     return config[start:end]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/check-prod-railway-runtime-secrets.py` around lines 15 - 20,
component_block currently assumes the exact marker exists and exact indentation,
causing crashes; change it to use config.find(marker) and handle a -1 (marker
missing) case gracefully (e.g., return empty string or raise a clear
ValueError), and loosen the next-component regex to accept arbitrary whitespace
(use r"\n\s*'[^']+'\s*=>\s*\[") when searching the remainder; compute end as
before (start + len(marker) + match.start()) if a match exists or use
len(config) when not, and ensure all indexes are only used after confirming
start >= 0.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/check-prod-railway-runtime-secrets.py`:
- Around line 15-20: component_block currently assumes the exact marker exists
and exact indentation, causing crashes; change it to use config.find(marker) and
handle a -1 (marker missing) case gracefully (e.g., return empty string or raise
a clear ValueError), and loosen the next-component regex to accept arbitrary
whitespace (use r"\n\s*'[^']+'\s*=>\s*\[") when searching the remainder; compute
end as before (start + len(marker) + match.start()) if a match exists or use
len(config) when not, and ensure all indexes are only used after confirming
start >= 0.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 565e9af6-3b33-4be9-a7d6-b767f7577eb1

📥 Commits

Reviewing files that changed from the base of the PR and between 7b023ff and c9cbe9d.

📒 Files selected for processing (3)
  • docs/prod-railway-runtime-secrets.md
  • environments/prod-railway/common/config/main-local.php
  • tests/check-prod-railway-runtime-secrets.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant