Summary
src/ contains 251 unwrap()/expect() call sites. Several sit on hot request paths where a panic aborts the task servicing a live request; the worst pattern is configuration read per call instead of at startup:
// src/services/auth_service.rs:13
fn jwt_secret() -> String {
std::env::var("JWT_SECRET").expect("JWT_SECRET must be set")
}
A missing/corrupted env var here panics inside token issuance/verification at runtime, rather than failing deployment at boot. The same late-read pattern appears across config consumers (e.g. cors_layer_from_env parses env on construction with silent fallbacks).
Task
- Introduce a typed, validated
AppConfig loaded and validated once at startup (fail-fast with a precise, aggregated error report listing all missing/invalid variables, not just the first). Include: database, Redis, JWT (secret length/entropy minimum enforced), SMTP, CORS, rate limits, webhook secrets.
- Thread
AppConfig through AppState; delete every runtime std::env::var read outside the config loader (grep-enforceable).
- Audit all 251
unwrap()/expect() sites and classify:
- request-path sites → convert to typed
AppError propagation with correct status codes
- startup-path sites → move behind config validation or keep with a documented invariant comment
- test-only sites → out of scope
Include the classification table in the PR description.
- Enforce regression prevention: enable
clippy::unwrap_used and clippy::expect_used as deny for src/ (allow in #[cfg(test)]), wired into the lint configuration.
- Add a startup-failure integration test: boot with an intentionally missing
JWT_SECRET and assert a clean, aggregated config error (no panic backtrace, no partial boot).
Acceptance criteria
PR requirements (mandatory)
- ✅ Your PR must pass all checks — PRs with failing or skipped checks will not be merged.
- 📸 You must attach a screenshot in the PR description demonstrating the result (for this issue: the aggregated startup validation error output plus the clean clippy run).
- 🔗 You must link this issue number in your PR description (e.g.
Closes #<issue-number>). PRs without a linked issue will not be reviewed.
Summary
src/contains 251unwrap()/expect()call sites. Several sit on hot request paths where a panic aborts the task servicing a live request; the worst pattern is configuration read per call instead of at startup:A missing/corrupted env var here panics inside token issuance/verification at runtime, rather than failing deployment at boot. The same late-read pattern appears across config consumers (e.g.
cors_layer_from_envparses env on construction with silent fallbacks).Task
AppConfigloaded and validated once at startup (fail-fast with a precise, aggregated error report listing all missing/invalid variables, not just the first). Include: database, Redis, JWT (secret length/entropy minimum enforced), SMTP, CORS, rate limits, webhook secrets.AppConfigthroughAppState; delete every runtimestd::env::varread outside the config loader (grep-enforceable).unwrap()/expect()sites and classify:AppErrorpropagation with correct status codesInclude the classification table in the PR description.
clippy::unwrap_usedandclippy::expect_usedas deny forsrc/(allow in#[cfg(test)]), wired into the lint configuration.JWT_SECRETand assert a clean, aggregated config error (no panic backtrace, no partial boot).Acceptance criteria
std::env::varoutside the config module; zerounwrap()/expect()in non-test request-path codeclippy::unwrap_used/expect_useddeny-level active and passingPR requirements (mandatory)
Closes #<issue-number>). PRs without a linked issue will not be reviewed.