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
@@ -108,7 +108,7 @@ pub async fn well_known_nostr(
let name = raw.to_lowercase();
if let Some(user) = state.db.get_user_by_username(&name).await? {
if let Some(pubkey) = user.nostr_pubkey {
relays.insert(pubkey.clone(), relay_hints(&state.config));
relays.insert(pubkey.clone(), relay_hints(&state.cfg()));
names.insert(name, pubkey);
}
}