# Section 7 — Secrets and Application Configuration > Companion to [`musehub-production-readiness-checklist.md`](../musehub-production-readiness-checklist.md). ## Full secrets inventory (corrected, see `docs/infrastructure.md`) | Secret | Purpose | Where it lives today | |---|---|---| | `DB_PASSWORD` | Postgres auth | Hand-provisioned `.env`, per instance | | `WEBHOOK_SECRET_KEY` | Fernet key for webhook signing | Hand-provisioned `.env`; also in SSM (`/musehub/staging/`) | | `RUNNER_TOKEN` | `musehub-runner` CI auth | Hand-provisioned `.env` | | `BLOB_STORAGE_ACCESS_KEY_ID` / `_SECRET_ACCESS_KEY` | Cloudflare R2 API credentials | Hand-provisioned `.env` | | `WORKER_INTERNAL_KEY` | Cloudflare Worker → MuseHub callback shared secret | Hand-provisioned `.env`; also in SSM | | `PACK_WORKER_URL` | Public URL of the mpack-receiver Worker | In SSM only (non-secret config, stored as SecureString anyway) | No OAuth client secrets, no payment provider keys, no email provider keys — none of those integrations exist (Section 0). ## Checklist assessment - [x] Inventory all secrets. — done, above. - [x] Remove secrets from source control and repository history. — confirmed via grep, no secrets committed; `.env` is never tracked. - [ ] Store secrets in AWS Secrets Manager or SSM Parameter Store as appropriate. — **partially drafted, not live.** `deploy/secrets.sh` implements this correctly but only 3 of 7 required parameters actually exist in SSM, and the script would fail its own preflight check against the current live state. This is the actual work item for this section. - [ ] Encrypt secrets with KMS. — the 3 SSM params that do exist are SecureString (KMS-encrypted by default); the rest aren't in SSM at all yet. - [x] Use separate staging and production secrets. — confirmed: separate `.env` per instance, separate R2 buckets (`musehub-staging`/`musehub-prod`), separate SSM path prefixes (`/musehub//`). - [ ] Give workloads access only to the secrets they require. — the app reads its own `.env` wholesale; no per-secret access scoping exists (nor would it, for a single-process app on one instance) — likely N/A given the architecture, but flagging rather than assuming. - [x] Do not expose production secrets to pull-request workflows. — N/A, no GitHub Actions/PR workflows exist; MuseHub-native CI (Section 9) doesn't exist yet either, so there's nothing to expose secrets *to* yet. - [ ] Do not print secrets in logs. — not verified in this pass; would need a log-content review once Section 12's centralized logging exists (currently no logs are collected at all, so nothing to audit yet). - [ ] Configure secret rotation where supported. — not done. - [ ] Rotate credentials during the production launch. — not done; deferred to Section 18 (Cutover Plan). - [ ] Document emergency rotation procedures. — not done. - [x] Separate secrets from nonsecret configuration. — `musehub/config.py`'s `Settings` class clearly separates secret fields (`db_password`, `blob_storage_secret_access_key`, etc.) from operational config (rate limits, quotas, timeouts) — this is already well-structured in code, even though the deployment-time delivery mechanism (hand-provisioned `.env`) isn't hardened yet. - [x] Validate required configuration at application startup. — `pydantic_settings.BaseSettings` does this by construction; missing required fields fail at import/startup, not at first use. - [x] Fail safely when critical configuration is missing. — same mechanism; also `deploy/secrets.sh`'s own preflight check (`die` on missing `DB_PASSWORD`) is a fail-safe *for the SSM path specifically*, once that path is actually used. - [x] Remove default credentials and sample keys. — no default/sample secrets found in `config.py` (secret fields default to `None`, not placeholder values); the backup script's weak-password check (`musehub`, `changeme123`, etc.) in `secrets.sh` is a good sign of intent here even though that script isn't live yet. - [ ] Audit OAuth callback URLs and API allowlists for production. — N/A, no OAuth exists (Section 0). - [ ] Audit every external webhook secret. — `WORKER_INTERNAL_KEY` is the one webhook-adjacent secret (Cloudflare Worker callback); not independently audited in this pass — needs a closer look at how `musehub/worker.py` validates it. - [ ] Document ownership and rotation frequency for every production secret. — not done; blocked on Section 1's ownership-recording work generally. ## The actual work item here Wire `deploy/secrets.sh` in for real: populate the missing SSM parameters (`DB_PASSWORD`, `BLOB_STORAGE_ACCESS_KEY_ID`, `BLOB_STORAGE_SECRET_ACCESS_KEY`, `RUNNER_TOKEN`) for staging, run `secrets.sh` against staging to prove the pipeline actually works end-to-end, then decide (per Section 0's open item) whether production adopts this from its very first deploy rather than repeating staging's hand-provisioning. This is real infrastructure work, correctly deferred to the build phase per the doc-first workflow.