diff --git a/core/archipelago/src/update.rs b/core/archipelago/src/update.rs index 69a3345d..bb7ce127 100644 --- a/core/archipelago/src/update.rs +++ b/core/archipelago/src/update.rs @@ -131,12 +131,19 @@ fn default_mirrors() -> Vec { url: DEFAULT_UPDATE_MANIFEST_URL.to_string(), label: "Archipelago Foundation".to_string(), }, - // Fallback, tried only if the named origin fails: a node whose DNS - // or clock is wrong (both break TLS) must still be able to update - // itself, and the signature check is what makes either source safe. + // NOT a second server — the SAME host as the entry above, reached by + // IP over plain HTTP instead of by name over TLS. It buys nothing if + // the origin is down; what it recovers is a node whose **DNS is + // broken or whose clock is wrong**, either of which fails TLS while + // plain HTTP still works. Safe because the manifest carries an Ed25519 + // signature verified against the pinned release-root anchor, so + // transport integrity is not what protects the update. + // + // Labelled explicitly so the UI cannot imply redundancy it doesn't + // provide. Real redundancy needs a mirror on a different host. UpdateMirror { url: LEGACY_UPDATE_MANIFEST_URL.to_string(), - label: "Direct (fallback)".to_string(), + label: "Same server, no DNS/TLS".to_string(), }, ] } @@ -210,12 +217,16 @@ fn force_ovh_update_primary(list: &mut Vec) { if mirror.url == DEFAULT_UPDATE_MANIFEST_URL { mirror.label = "Archipelago Foundation".to_string(); } else if mirror.url == LEGACY_UPDATE_MANIFEST_URL { - mirror.label = "Direct (fallback)".to_string(); + // Rewritten on every load, so relabelling here reaches nodes that + // already have the old "Direct (fallback)" text saved in their + // update-mirrors.json — the merge below matches on URL, never on + // label, so without this a renamed default would never propagate. + mirror.label = "Same server, no DNS/TLS".to_string(); } } - // Named origin first, its IP fallback second, anything the operator - // added after that. Ordering matters: the list is tried in order, so a - // stale entry sitting first costs a timeout on every check. + // Named origin first, its same-host IP fallback second, anything the + // operator added after that. Ordering matters: the list is tried in order, + // so a stale entry sitting first costs a timeout on every check. list.sort_by_key(|m| match m.url.as_str() { u if u == DEFAULT_UPDATE_MANIFEST_URL => 0, u if u == LEGACY_UPDATE_MANIFEST_URL => 1, diff --git a/neode-ui/src/views/SystemUpdate.vue b/neode-ui/src/views/SystemUpdate.vue index f9f9436c..09c608d8 100644 --- a/neode-ui/src/views/SystemUpdate.vue +++ b/neode-ui/src/views/SystemUpdate.vue @@ -254,7 +254,10 @@ >+ Add mirror

- Servers this node checks for updates. The primary is tried first; if it's slow or unreachable, the next one in the list is tried automatically. Downloads always come from the mirror that served the manifest — switching primary switches where files come from. + Sources this node checks for updates. The primary is tried first; if it's slow or unreachable, the next one is tried automatically. Downloads always come from the source that served the manifest — switching primary switches where files come from. +

+

+ The two built-in entries are the same server, not two servers: the second reaches it by IP without DNS or TLS, which recovers a node whose DNS is broken or whose clock is wrong. It does not help if the server itself is down. Every update is signature-checked whichever source serves it, so an unencrypted fetch can't substitute a tampered build. For real redundancy, add a mirror on a different host.