From ede46facf1ad0e3645e9382f69b8c44a1a519d74 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 3 Apr 2026 16:51:31 +0000
Subject: [PATCH 1/2] Initial plan
From b2fb7035e06f4458940dde91c805cd1db9edb8e1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 3 Apr 2026 17:03:42 +0000
Subject: [PATCH 2/2] Add network port requirements UI, install notification,
and tile port info
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/54981eb1-b1c5-4e1a-b587-730f41c59e01
Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
---
app/sovran_systemsos_web/server.py | 50 +++++++++
app/sovran_systemsos_web/static/app.js | 90 ++++++++++++++-
app/sovran_systemsos_web/static/style.css | 79 +++++++++++++
app/sovran_systemsos_web/templates/index.html | 11 ++
iso/installer.py | 105 +++++++++++++++++-
5 files changed, 332 insertions(+), 3 deletions(-)
diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py
index 6844370..da23ac3 100644
--- a/app/sovran_systemsos_web/server.py
+++ b/app/sovran_systemsos_web/server.py
@@ -83,6 +83,7 @@ FEATURE_REGISTRY = [
"needs_ddns": False,
"extra_fields": [],
"conflicts_with": [],
+ "port_requirements": [],
},
{
"id": "haven",
@@ -102,6 +103,8 @@ FEATURE_REGISTRY = [
},
],
"conflicts_with": [],
+ # Haven uses only 80/443, already covered by the main install alert
+ "port_requirements": [],
},
{
"id": "element-calling",
@@ -114,6 +117,15 @@ FEATURE_REGISTRY = [
"extra_fields": [],
"conflicts_with": [],
"requires": ["matrix_domain"],
+ "port_requirements": [
+ {"port": "80", "protocol": "TCP", "description": "HTTP (redirect to HTTPS)"},
+ {"port": "443", "protocol": "TCP", "description": "HTTPS (domain)"},
+ {"port": "7881", "protocol": "TCP", "description": "LiveKit WebRTC signalling"},
+ {"port": "7882-7894", "protocol": "UDP", "description": "LiveKit media streams"},
+ {"port": "5349", "protocol": "TCP", "description": "TURN over TLS"},
+ {"port": "3478", "protocol": "UDP", "description": "TURN (STUN/relay)"},
+ {"port": "30000-40000", "protocol": "TCP/UDP", "description": "TURN relay (WebRTC)"},
+ ],
},
{
"id": "mempool",
@@ -125,6 +137,7 @@ FEATURE_REGISTRY = [
"needs_ddns": False,
"extra_fields": [],
"conflicts_with": [],
+ "port_requirements": [],
},
{
"id": "bip110",
@@ -136,6 +149,7 @@ FEATURE_REGISTRY = [
"needs_ddns": False,
"extra_fields": [],
"conflicts_with": ["bitcoin-core"],
+ "port_requirements": [],
},
{
"id": "bitcoin-core",
@@ -147,6 +161,7 @@ FEATURE_REGISTRY = [
"needs_ddns": False,
"extra_fields": [],
"conflicts_with": ["bip110"],
+ "port_requirements": [],
},
]
@@ -160,6 +175,37 @@ FEATURE_SERVICE_MAP = {
"bitcoin-core": None,
}
+# Port requirements for service tiles (keyed by unit name or icon)
+# Services using only 80/443 for domain access share the same base list.
+_PORTS_WEB = [
+ {"port": "80", "protocol": "TCP", "description": "HTTP (redirect to HTTPS)"},
+ {"port": "443", "protocol": "TCP", "description": "HTTPS"},
+]
+_PORTS_MATRIX_FEDERATION = _PORTS_WEB + [
+ {"port": "8448", "protocol": "TCP", "description": "Matrix server-to-server federation"},
+]
+_PORTS_ELEMENT_CALLING = _PORTS_WEB + [
+ {"port": "7881", "protocol": "TCP", "description": "LiveKit WebRTC signalling"},
+ {"port": "7882-7894", "protocol": "UDP", "description": "LiveKit media streams"},
+ {"port": "5349", "protocol": "TCP", "description": "TURN over TLS"},
+ {"port": "3478", "protocol": "UDP", "description": "TURN (STUN/relay)"},
+ {"port": "30000-40000", "protocol": "TCP/UDP", "description": "TURN relay (WebRTC)"},
+]
+
+SERVICE_PORT_REQUIREMENTS: dict[str, list[dict]] = {
+ # Infrastructure
+ "caddy.service": _PORTS_WEB,
+ # Communication
+ "matrix-synapse.service": _PORTS_MATRIX_FEDERATION,
+ "livekit.service": _PORTS_ELEMENT_CALLING,
+ # Domain-based apps (80/443)
+ "btcpayserver.service": _PORTS_WEB,
+ "vaultwarden.service": _PORTS_WEB,
+ "phpfpm-nextcloud.service": _PORTS_WEB,
+ "phpfpm-wordpress.service": _PORTS_WEB,
+ "haven-relay.service": _PORTS_WEB,
+}
+
# For features that share a unit, disambiguate by icon field
FEATURE_ICON_MAP = {
"bip110": "bip110",
@@ -689,6 +735,8 @@ async def api_services():
creds = entry.get("credentials", [])
has_credentials = len(creds) > 0
+ port_requirements = SERVICE_PORT_REQUIREMENTS.get(unit, [])
+
return {
"name": entry.get("name", ""),
"unit": unit,
@@ -698,6 +746,7 @@ async def api_services():
"category": entry.get("category", "other"),
"status": status,
"has_credentials": has_credentials,
+ "port_requirements": port_requirements,
}
results = await asyncio.gather(*[get_status(s) for s in services])
@@ -910,6 +959,7 @@ async def api_features():
"needs_ddns": feat.get("needs_ddns", False),
"extra_fields": extra_fields,
"conflicts_with": feat.get("conflicts_with", []),
+ "port_requirements": feat.get("port_requirements", []),
}
if "requires" in feat:
entry["requires"] = feat["requires"]
diff --git a/app/sovran_systemsos_web/static/app.js b/app/sovran_systemsos_web/static/app.js
index 09e6f89..7308f57 100644
--- a/app/sovran_systemsos_web/static/app.js
+++ b/app/sovran_systemsos_web/static/app.js
@@ -113,6 +113,11 @@ const $featureConfirmOk = document.getElementById("feature-confirm-ok-btn")
const $featureConfirmCancel = document.getElementById("feature-confirm-cancel-btn");
const $featureConfirmClose = document.getElementById("feature-confirm-close-btn");
+// Port Requirements modal
+const $portReqModal = document.getElementById("port-requirements-modal");
+const $portReqBody = document.getElementById("port-req-body");
+const $portReqClose = document.getElementById("port-req-close-btn");
+
// ── Helpers ───────────────────────────────────────────────────────
function tileId(svc) { return svc.unit + "::" + svc.name; }
@@ -218,7 +223,16 @@ function buildTile(svc) {
}
var infoBtn = hasCreds ? '' : "";
- tile.innerHTML = infoBtn + '
You have enabled ' + escHtml(featureName) + '. ' + + 'For it to work with clients outside your local network you must open the following ports ' + + 'on your home router / WAN firewall:
' + + '| Port(s) | Protocol | Purpose |
|---|
ℹ Consult your router manual or search "how to open ports on [router model]" ' + + 'for instructions. Features like Element Video Calling will not work for remote users until these ports are open.
' + + '