From 28e38a36a9d82df77066b0adc5bd5afe8c14d64a Mon Sep 17 00:00:00 2001 From: archipelago Date: Thu, 23 Apr 2026 08:51:26 -0400 Subject: [PATCH] fix(config): auto-purge decommissioned .23 VPS from saved registry/mirror configs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit load_registries + load_mirrors normally only ADD missing defaults to the persisted JSON — explicit removals stick. After retiring the .23 Hetzner VPS we need the opposite: existing nodes have .23 baked into their saved configs and would spend seconds per install/update timing out against a dead host until the operator manually removes it via the Settings UI. Add a targeted one-time migration in both loaders: if any saved entry has 23.182.128.160 in its URL, drop it on load and rewrite the file. This is an exception to the usual "explicit removals stick" rule — the user never chose to add this mirror, it was a default. Narrow-scope migration (one hardcoded IP match, no schema version) because the cost/benefit of a general migration system isn't worth it for a single decommissioned host. Future retirements can follow the same pattern. --- core/archipelago/src/container/registry.rs | 16 +++++++++++++--- core/archipelago/src/update.rs | 15 ++++++++++++--- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/core/archipelago/src/container/registry.rs b/core/archipelago/src/container/registry.rs index 73bde3bc..b7299f31 100644 --- a/core/archipelago/src/container/registry.rs +++ b/core/archipelago/src/container/registry.rs @@ -107,6 +107,17 @@ pub async fn load_registries(data_dir: &Path) -> Result { let mut config: RegistryConfig = serde_json::from_str(&content).unwrap_or_else(|_| RegistryConfig::default()); + // One-time migration: the Hetzner VPS at 23.182.128.160 was + // decommissioned 2026-04-23. Existing nodes have it baked into + // their saved registry list (was the original Server 1). Strip it + // on load so every container pull doesn't pay a connection-refused + // timeout against a dead host. Exception to the usual "explicit + // removals stick" rule: the user never chose to add this — it + // was a default. + let before = config.registries.len(); + config.registries.retain(|r| !r.url.contains("23.182.128.160")); + let mut changed = config.registries.len() != before; + // Migrate: any default registry URL that isn't already in the // saved list gets appended at the end (so existing priority order // is preserved for anything the operator already configured). @@ -119,16 +130,15 @@ pub async fn load_registries(data_dir: &Path) -> Result { .map(|r| r.priority) .max() .unwrap_or(0); - let mut added = false; for (i, def) in defaults.registries.iter().enumerate() { if !known.contains(&def.url) { let mut cloned = def.clone(); cloned.priority = max_priority.saturating_add(10 + i as u32); config.registries.push(cloned); - added = true; + changed = true; } } - if added { + if changed { // Persist so the next load doesn't have to re-merge. let _ = save_registries(data_dir, &config).await; } diff --git a/core/archipelago/src/update.rs b/core/archipelago/src/update.rs index eab30e8a..8654491b 100644 --- a/core/archipelago/src/update.rs +++ b/core/archipelago/src/update.rs @@ -143,18 +143,27 @@ pub async fn load_mirrors(data_dir: &Path) -> Result> { return Ok(default_mirrors()); } + // One-time migration: the Hetzner VPS at 23.182.128.160 was + // decommissioned 2026-04-23. Existing nodes have it baked into their + // saved mirror list (was the original Server 1). Strip it on load so + // we don't spend seconds per install timing out against a dead host. + // Exception to the usual "explicit removals stick" rule: the user + // never chose to add this — it was a default. + let before = list.len(); + list.retain(|m| !m.url.contains("23.182.128.160")); + let mut changed = list.len() != before; + // Merge in any default URLs the saved config is missing. let known: std::collections::HashSet = list.iter().map(|m| m.url.clone()).collect(); let defaults = default_mirrors(); - let mut added = false; for def in &defaults { if !known.contains(&def.url) { list.push(def.clone()); - added = true; + changed = true; } } - if added { + if changed { let _ = save_mirrors(data_dir, &list).await; } Ok(list)