From 1e89362e7148e7ef39b7f06b979776b719ca57a0 Mon Sep 17 00:00:00 2001 From: archipelago Date: Thu, 23 Jul 2026 17:57:58 -0400 Subject: [PATCH] =?UTF-8?q?fix(nginx):=20IPv6=20listeners=20=E2=80=94=20co?= =?UTF-8?q?mpanion=20mesh=20HTTP=20to=20the=20node's=20ULA=20never=20conne?= =?UTF-8?q?cted?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phones on the FIPS mesh reach the node at http://[], but every shipped nginx config listened on IPv4 only — nothing answered [::]:80/443, so mesh HTTP failed with the tunnel fully healthy (found live on framework-pt during the vc24 5G session, 2026-07-23). Canonical conf gains listen [::] lines and patch_nginx_conf self-heals deployed nodes (covers the sites-enabled -> archipelago-http dev variant via the existing canonicalize pass); validated by the existing nginx -t + rollback flow. Co-Authored-By: Claude Fable 5 --- core/archipelago/src/bootstrap.rs | 22 +++++++++++++++++++++ image-recipe/configs/nginx-archipelago.conf | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/core/archipelago/src/bootstrap.rs b/core/archipelago/src/bootstrap.rs index f071b4b5..18c41109 100644 --- a/core/archipelago/src/bootstrap.rs +++ b/core/archipelago/src/bootstrap.rs @@ -991,6 +991,13 @@ async fn patch_nginx_conf(path: &str) -> Result { // B13: fedimint block present but lacking the asset-rewrite sub_filters. let needs_fedimint_css = content.contains("location /app/fedimint/") && !content.contains("'href=\"/' 'href=\"/app/fedimint/'"); + // Companion mesh access: phones reach this node over FIPS at its fips0 + // ULA (http://[fdxx:…]). Configs shipped before 2026-07-23 listened on + // IPv4 only, so the ULA could never connect — nothing answered [::]:80. + let missing_v6_http = content.contains("listen 80 default_server;") + && !content.contains("listen [::]:80"); + let missing_v6_https = content.contains("listen 443 ssl default_server;") + && !content.contains("listen [::]:443"); if !missing_app_catalog && !missing_bitcoin_status && !missing_lnd_proxy @@ -998,12 +1005,27 @@ async fn patch_nginx_conf(path: &str) -> Result { && !missing_pine_status && !has_lnd_dup_cors && !needs_fedimint_css + && !missing_v6_http + && !missing_v6_https { return Ok(false); } let mut patched = content.clone(); + if missing_v6_http { + patched = patched.replace( + "listen 80 default_server;", + "listen 80 default_server;\n listen [::]:80 default_server;", + ); + } + if missing_v6_https { + patched = patched.replace( + "listen 443 ssl default_server;", + "listen 443 ssl default_server;\n listen [::]:443 ssl default_server;", + ); + } + if has_lnd_dup_cors { // Drop the redundant nginx-side CORS headers so the backend's single // validated Access-Control-Allow-Origin is the only one returned. diff --git a/image-recipe/configs/nginx-archipelago.conf b/image-recipe/configs/nginx-archipelago.conf index ecc7b154..c908eb82 100644 --- a/image-recipe/configs/nginx-archipelago.conf +++ b/image-recipe/configs/nginx-archipelago.conf @@ -9,6 +9,10 @@ resolver_timeout 5s; server { listen 80 default_server; + # IPv6 listener is REQUIRED: companion phones reach this node over the + # FIPS mesh at its fips0 ULA (http://[fdxx:…]) — without [::]:80 that + # address can never connect (found live 2026-07-23, framework-pt). + listen [::]:80 default_server; server_name _; root /opt/archipelago/web-ui; @@ -901,6 +905,7 @@ server { # HTTPS - required for PWA install (Add to Home Screen) from dev servers server { listen 443 ssl default_server; + listen [::]:443 ssl default_server; server_name _; ssl_certificate /etc/archipelago/ssl/archipelago.crt;