2de5abc6ca
Two chronic problems fall out: 1. **Split-brain config.** Admin edits DB via the panel; app boot ignores DB. Migration completes; live backend hasn't moved. Admin has to remember to copy env vars into `.env` and restart. Two sources of truth for the same setting. Cutover is a manual multi-step flow; users routinely get it wrong. 2. **Migration data-loss window on concurrent writes.** The copy walks `storage.blobs` in hash order. A blob whose hash is lex-lower than the current cursor, written to source AFTER migration passed it, is never copied to target. `passed=true, findings=0` completion does NOT guarantee target has every blob. Silent. 3. **Migration target selection is fragile.** DTO passes the whole S3 config at trigger time; secrets sit plaintext in `admin_settings`. Any future pluggable-storage story compounds this (Azure, GCS, WebDAV-as-source, …). This plan replaces the split-brain model with a single-source-of-truth architecture: - `.env` declares **N named storage entries** (immutable per-deploy). - `admin_settings.storage.active_backend_name` holds ONE row — which named entry the app currently runs on. That's the whole runtime config. - Migration is the atomic transition from one active entry to another. Server is put in read-only mode for the copy window; on completion, the active pointer flips; a restart cuts over.