feat(config): effective config via ArcSwap + DB overlay at boot (Stage 2)

Wire the settings overlay into the live config path:
- AppState.config: Config -> Arc<ArcSwap<Config>> (hot-swappable effective config) +
  config_base: Arc<Config> (immutable env-only). New AppState::cfg() accessor; every
  config read goes through it (state.cfg().field), so a runtime-safe operator edit is
  seen on the next request. One Config type, no parallel path.
- Boot (main.rs): load_settings_overlay -> Config::apply_overlay(validate) -> build
  every client/worker/router from the EFFECTIVE config. A persisted overlay that no
  longer validates is ignored (boot env-only + loud log), never bricks. Env-only when
  ADMIN_KEY_INTEGRITY_SECRET is unset.
- Migrated ~59 state.config.X reads to state.cfg().X across api/*; bound guards where a
  reference outlives the statement; integrity_secret returns owned String.
- Test harness builds AppState with ArcSwap + config_base (arc-swap dev-dep).

Behavior-preserving: with no operator_settings rows, effective == env config.
cargo check --all-targets clean; lib tests 256/0.
This commit is contained in:
jwinterm
2026-07-18 06:00:16 -04:00
parent 00968a9c05
commit 4c83e53839
24 changed files with 153 additions and 101 deletions
+1 -1
View File
@@ -92,7 +92,7 @@ async fn admin_login_guard_refresh_logout_flow() {
let app = require_app!();
let admin = app.admin_router();
let secret = app.state.config.admin.key_integrity_secret.clone();
let secret = app.state.cfg().admin.key_integrity_secret.clone();
// Seed a PENDING admin key for our signer.
let sk = random_signer();