diff --git a/core/Cargo.lock b/core/Cargo.lock index 9e0c5da4..37fa3cf3 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -80,7 +80,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "archipelago" -version = "1.7.37-alpha" +version = "1.7.39-alpha" dependencies = [ "anyhow", "archipelago-container", diff --git a/core/archipelago/Cargo.toml b/core/archipelago/Cargo.toml index 88dfa420..4e3ead58 100644 --- a/core/archipelago/Cargo.toml +++ b/core/archipelago/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "archipelago" -version = "1.7.37-alpha" +version = "1.7.39-alpha" edition = "2021" description = "Archipelago Bitcoin Node OS - Native backend" authors = ["Archipelago Team"] diff --git a/core/archipelago/src/main.rs b/core/archipelago/src/main.rs index 2c0a5546..48670dd6 100644 --- a/core/archipelago/src/main.rs +++ b/core/archipelago/src/main.rs @@ -86,6 +86,36 @@ async fn main() -> Result<()> { info!("Starting Archipelago Bitcoin Node OS"); + // Self-heal web-ui permissions. The OTA updater in <=v1.7.38 left + // /opt/archipelago/web-ui as drwx------ (700) after the atomic + // swap — nginx (www-data) then returned 500/403 on every request + // until someone shelled in and chmod'd it. Check on every boot + // and repair if needed so a node auto-recovers after the next + // service restart that follows a broken OTA. + tokio::spawn(async move { + use std::os::unix::fs::PermissionsExt; + let web_ui = std::path::Path::new("/opt/archipelago/web-ui"); + if let Ok(meta) = tokio::fs::metadata(web_ui).await { + let mode = meta.permissions().mode() & 0o777; + if mode & 0o005 != 0o005 { + tracing::warn!( + "web-ui perms {:o} not world-readable — self-healing", + mode + ); + let _ = tokio::process::Command::new("sudo") + .args([ + "-n", + "chmod", + "-R", + "u=rwX,go=rX", + "/opt/archipelago/web-ui", + ]) + .status() + .await; + } + } + }); + // Load configuration let config = Config::load().await?; info!("📁 Data directory: {}", config.data_dir.display()); diff --git a/core/archipelago/src/update.rs b/core/archipelago/src/update.rs index 979016c0..f297f451 100644 --- a/core/archipelago/src/update.rs +++ b/core/archipelago/src/update.rs @@ -914,6 +914,21 @@ pub async fn apply_update(data_dir: &Path) -> Result<()> { ]) .await; + // Set world-readable perms so nginx (runs as www-data) + // can stat + serve the files. Without this, the tar + // extraction inherits the staging-dir's 700 mode and + // nginx returns 403/500 for every request after the + // swap — exactly what bit .116 on the v1.7.38 rollout. + let _ = host_sudo(&["chmod", "755", &staging_new]).await; + let _ = host_sudo(&[ + "find", &staging_new, "-type", "d", "-exec", "chmod", "755", "{}", "+", + ]) + .await; + let _ = host_sudo(&[ + "find", &staging_new, "-type", "f", "-exec", "chmod", "644", "{}", "+", + ]) + .await; + // Preserve paths that are installed outside the Vue build // (baked in by the ISO or sibling installers) and so // aren't in the new tarball. Without this copy, every OTA diff --git a/neode-ui/package.json b/neode-ui/package.json index c411e476..0660c435 100644 --- a/neode-ui/package.json +++ b/neode-ui/package.json @@ -1,7 +1,7 @@ { "name": "neode-ui", "private": true, - "version": "1.7.38-alpha", + "version": "1.7.39-alpha", "type": "module", "scripts": { "start": "./start-dev.sh", diff --git a/neode-ui/src/views/settings/AccountInfoSection.vue b/neode-ui/src/views/settings/AccountInfoSection.vue index be56ae57..82921281 100644 --- a/neode-ui/src/views/settings/AccountInfoSection.vue +++ b/neode-ui/src/views/settings/AccountInfoSection.vue @@ -180,6 +180,16 @@ init()
+ +
+
+ v1.7.39-alpha + Apr 22, 2026 +
+
+

Hotfix for v1.7.38 — on some nodes the update landed with the web UI directory set to private file permissions, so nginx returned a 500 / "Internal Server Error" on every page. This release fixes the updater to set world-readable permissions on the new frontend, and the node also now self-heals on boot if it ever finds the UI directory in that state again.

+
+
diff --git a/releases/manifest.json b/releases/manifest.json index 6fdb77fc..6fcb1673 100644 --- a/releases/manifest.json +++ b/releases/manifest.json @@ -1,27 +1,28 @@ { - "version": "1.7.38-alpha", + "version": "1.7.39-alpha", "release_date": "2026-04-22", "changelog": [ - "Signing in is quiet after the first boot. The intro music, welcome voice, and transition sounds only play during initial onboarding — every subsequent login is silent. Typing sounds in the search bar and dashboard are unaffected.", - "Fixed a bug where clearing your browser cache, updating the node, or rebooting could bounce you back through the onboarding wizard even though your node was already set up. The node now self-heals so already-onboarded nodes always go straight to the login screen.", - "Trimmed the App Store — FIPS, Nostr Relay, Nostr VPN, Routstr, and Penpot are no longer listed and their container images have been removed from all registries. Your node's built-in FIPS transport is untouched; this only removes the app-store entries." + "Hotfix: on some nodes the v1.7.38 update landed the web UI directory with private permissions, so nginx returned 500 / Internal Server Error on every page. v1.7.39 fixes the updater to leave the new frontend world-readable, and the node now also self-heals on boot if it ever finds the UI in that state again.", + "Signing in is quiet after the first boot. The intro music, welcome voice, and transition sounds only play during initial onboarding — every login after that is silent. Typing sounds in the search bar and dashboard are unaffected.", + "Nodes that completed setup no longer get bounced back through the onboarding wizard after clearing browser cache, updating, or rebooting. The node self-heals so already-onboarded nodes always go straight to the login screen.", + "Trimmed the App Store — FIPS, Nostr Relay, Nostr VPN, Routstr, and Penpot are no longer listed and their container images have been removed from all registries. Your node's built-in FIPS transport is untouched." ], "components": [ { "name": "archipelago", "current_version": "1.7.37-alpha", - "new_version": "1.7.38-alpha", - "download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.38-alpha/archipelago", - "sha256": "d8c8b8eea14fae78906c79628828b3fba74d1673e983726d6463ec30d434bbca", - "size_bytes": 41085368 + "new_version": "1.7.39-alpha", + "download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.39-alpha/archipelago", + "sha256": "533d5ff9421aba2a1b4acc981746da1692f6a2abb5101da5500f5f920c26fd3a", + "size_bytes": 41107808 }, { - "name": "archipelago-frontend-1.7.38-alpha.tar.gz", + "name": "archipelago-frontend-1.7.39-alpha.tar.gz", "current_version": "1.7.37-alpha", - "new_version": "1.7.38-alpha", - "download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.38-alpha/archipelago-frontend-1.7.38-alpha.tar.gz", - "sha256": "c5861b60a9e888d21a0e77d2f02b5c1f5dea30b02b8fad2c302713e78d595a53", - "size_bytes": 162087753 + "new_version": "1.7.39-alpha", + "download_url": "https://git.tx1138.com/lfg2025/archy/raw/branch/main/releases/v1.7.39-alpha/archipelago-frontend-1.7.39-alpha.tar.gz", + "sha256": "e5dc5b51a1e72836688b7883b8032245aa252a33500d6eba37839569b5c5b17a", + "size_bytes": 162085565 } ] } diff --git a/releases/v1.7.39-alpha/archipelago b/releases/v1.7.39-alpha/archipelago new file mode 100755 index 00000000..fb2807e2 Binary files /dev/null and b/releases/v1.7.39-alpha/archipelago differ diff --git a/releases/v1.7.39-alpha/archipelago-frontend-1.7.39-alpha.tar.gz b/releases/v1.7.39-alpha/archipelago-frontend-1.7.39-alpha.tar.gz new file mode 100644 index 00000000..fd25de3d Binary files /dev/null and b/releases/v1.7.39-alpha/archipelago-frontend-1.7.39-alpha.tar.gz differ