6 Commits

Author SHA1 Message Date
Sovran_Systems 9b5786fce1 Merge pull request #227 from naturallaw777/copilot/fix-btcpay-default-state
[WIP] Fix BTCPay Server default state in Node mode
2026-04-13 17:56:01 -05:00
Sovran_Systems 9c15b458c4 Merge pull request #228 from naturallaw777/copilot/fix-reboot-overlay-stuck
[WIP] Fix reboot overlay getting stuck after system update
2026-04-13 17:55:44 -05:00
copilot-swe-agent[bot] b86fe94d82 Fix: BTCPay off by default in Node role, Caddy conditional ACME/ports
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/2e2b84a8-c5e9-4eea-8bee-fc587bb3a6fa

Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
2026-04-13 22:55:37 +00:00
copilot-swe-agent[bot] 7b7947db9d fix: robust reboot detection with AbortController timeout and serverWentDown flag
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/8b653481-74f2-450f-a543-c94eb664645a

Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
2026-04-13 22:55:18 +00:00
copilot-swe-agent[bot] 38b45013b3 Initial plan 2026-04-13 22:54:20 +00:00
copilot-swe-agent[bot] 2db344f91f Initial plan 2026-04-13 22:52:35 +00:00
7 changed files with 59 additions and 11 deletions
+1 -1
View File
@@ -291,7 +291,7 @@ _PORTS_ELEMENT_CALLING = _PORTS_WEB + [
SERVICE_PORT_REQUIREMENTS: dict[str, list[dict]] = { SERVICE_PORT_REQUIREMENTS: dict[str, list[dict]] = {
# Infrastructure # Infrastructure
"caddy.service": _PORTS_WEB, "caddy.service": [],
# Communication # Communication
"matrix-synapse.service": _PORTS_MATRIX_FEDERATION, "matrix-synapse.service": _PORTS_MATRIX_FEDERATION,
"livekit.service": _PORTS_ELEMENT_CALLING, "livekit.service": _PORTS_ELEMENT_CALLING,
+30 -5
View File
@@ -163,21 +163,46 @@ function saveErrorReport() {
// ── Reboot ──────────────────────────────────────────────────────── // ── Reboot ────────────────────────────────────────────────────────
var _rebootStartTime = 0;
var _serverWentDown = false;
function doReboot() { function doReboot() {
if ($modal) $modal.classList.remove("open"); if ($modal) $modal.classList.remove("open");
if ($rebuildModal) $rebuildModal.classList.remove("open"); if ($rebuildModal) $rebuildModal.classList.remove("open");
stopUpdatePoll(); stopUpdatePoll();
stopRebuildPoll(); stopRebuildPoll();
if ($rebootOverlay) $rebootOverlay.classList.add("visible"); if ($rebootOverlay) $rebootOverlay.classList.add("visible");
_rebootStartTime = Date.now();
_serverWentDown = false;
fetch("/api/reboot", { method: "POST" }).catch(function() {}); fetch("/api/reboot", { method: "POST" }).catch(function() {});
setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL); // Wait 15 seconds before the first check — give the system time to actually shut down
setTimeout(waitForServerReboot, 15000);
} }
function waitForServerReboot() { function waitForServerReboot() {
fetch("/api/config", { cache: "no-store" }) var controller = new AbortController();
var timeoutId = setTimeout(function() { controller.abort(); }, REBOOT_CHECK_INTERVAL);
fetch("/api/config", { cache: "no-store", signal: controller.signal })
.then(function(res) { .then(function(res) {
if (res.ok) window.location.reload(); clearTimeout(timeoutId);
else setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL); if (res.ok && _serverWentDown) {
// Server is back after having been down — reboot is complete
window.location.reload();
} else if (res.ok && !_serverWentDown && (Date.now() - _rebootStartTime) < 30000) {
// Server still responding but hasn't gone down yet — keep waiting
setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL);
} else if (res.ok) {
// Been over 30 seconds and server is responding — just reload
window.location.reload();
} else {
_serverWentDown = true;
setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL);
}
}) })
.catch(function() { setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL); }); .catch(function() {
clearTimeout(timeoutId);
_serverWentDown = true;
setTimeout(waitForServerReboot, REBOOT_CHECK_INTERVAL);
});
} }
+2 -2
View File
@@ -31,8 +31,8 @@
networking.hostName = "nixos"; networking.hostName = "nixos";
networking.networkmanager.enable = true; networking.networkmanager.enable = true;
networking.firewall.enable = true; networking.firewall.enable = true;
networking.firewall.allowedTCPPorts = [ 80 443 8448 3051 ]; networking.firewall.allowedTCPPorts = [ 8448 3051 ];
networking.firewall.allowedUDPPorts = [ 80 443 8448 3051 5353 ]; networking.firewall.allowedUDPPorts = [ 8448 3051 5353 ];
# ── Avahi (mDNS) ─────────────────────────────────────────── # ── Avahi (mDNS) ───────────────────────────────────────────
services.avahi = { services.avahi = {
+1 -1
View File
@@ -55,7 +55,7 @@ lib.mkIf config.sovran_systemsOS.services.bitcoin {
}; };
services.btcpayserver = { services.btcpayserver = {
enable = true; enable = config.sovran_systemsOS.web.btcpayserver;
}; };
services.btcpayserver.lightningBackend = "lnd"; services.btcpayserver.lightningBackend = "lnd";
+23 -1
View File
@@ -3,6 +3,16 @@
let let
exposeBtcpay = config.sovran_systemsOS.web.btcpayserver; exposeBtcpay = config.sovran_systemsOS.web.btcpayserver;
extraVhosts = config.sovran_systemsOS.caddy.extraVirtualHosts; extraVhosts = config.sovran_systemsOS.caddy.extraVirtualHosts;
# True when any service needs HTTPS/ACME (domain-based vhosts)
needsHttpsPorts =
config.sovran_systemsOS.web.btcpayserver
|| config.sovran_systemsOS.services.synapse
|| config.sovran_systemsOS.services.wordpress
|| config.sovran_systemsOS.services.nextcloud
|| config.sovran_systemsOS.services.vaultwarden
|| config.sovran_systemsOS.features.haven
|| config.sovran_systemsOS.features.element-calling;
in in
{ {
services.caddy = { services.caddy = {
@@ -11,6 +21,10 @@ in
group = "root"; group = "root";
}; };
# Only open ports 80/443 when at least one domain-based service is active
networking.firewall.allowedTCPPorts = lib.mkIf needsHttpsPorts [ 80 443 ];
networking.firewall.allowedUDPPorts = lib.mkIf needsHttpsPorts [ 80 443 ];
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d /var/lib/domains 0755 caddy root -" "d /var/lib/domains 0755 caddy root -"
]; ];
@@ -55,12 +69,20 @@ in
HAVEN=$(read_domain haven) HAVEN=$(read_domain haven)
ACME_EMAIL=$(read_domain sslemail) ACME_EMAIL=$(read_domain sslemail)
# Start with global config # Start with global config use ACME only when domain-based services are active
${if needsHttpsPorts then ''
cat > /run/caddy/Caddyfile <<EOF cat > /run/caddy/Caddyfile <<EOF
{ {
email $ACME_EMAIL email $ACME_EMAIL
} }
EOF EOF
'' else ''
cat > /run/caddy/Caddyfile <<EOF
{
auto_https off
}
EOF
''}
# Matrix # Matrix
if [ -n "$MATRIX" ]; then if [ -n "$MATRIX" ]; then
+1
View File
@@ -5,6 +5,7 @@
# ── Server+Desktop Role (default) ───────────────────────── # ── Server+Desktop Role (default) ─────────────────────────
(lib.mkIf config.sovran_systemsOS.roles.server_plus_desktop { (lib.mkIf config.sovran_systemsOS.roles.server_plus_desktop {
sovran_systemsOS.web.btcpayserver = lib.mkDefault true;
}) })
# ── Desktop Only Role ───────────────────────────────────── # ── Desktop Only Role ─────────────────────────────────────
+1 -1
View File
@@ -55,7 +55,7 @@
web = { web = {
btcpayserver = lib.mkOption { btcpayserver = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = true; default = false;
description = "Expose BTCPay Server via Caddy"; description = "Expose BTCPay Server via Caddy";
}; };
}; };