ux: simplify domain and router setup guidance

This commit is contained in:
2026-08-22 09:10:05 -05:00
parent d31887cae6
commit 14db4282b7
7 changed files with 78 additions and 167 deletions
+7 -7
View File
@@ -246,8 +246,8 @@ CATEGORY_ORDER = [
FEATURE_REGISTRY = [ FEATURE_REGISTRY = [
{ {
"id": "rdp", "id": "rdp",
"name": "Remote Desktop (RDP)", "name": "Remote Desktop",
"description": "Access your desktop remotely via RDP client", "description": "Access your desktop from a device on your home network — no router setup needed",
"category": "infrastructure", "category": "infrastructure",
"needs_domain": False, "needs_domain": False,
"domain_name": None, "domain_name": None,
@@ -362,8 +362,8 @@ FEATURE_REGISTRY = [
}, },
{ {
"id": "btcpay-web", "id": "btcpay-web",
"name": "BTCPay Server Web Access", "name": "Put BTCPay Server Online",
"description": "Expose BTCPay Server to the internet via your domain. When disabled, BTCPay Server still runs locally but is not accessible from the web.", "description": "Let people donate Bitcoin, pay you, or visit your online store. BTCPay Server stays private when this is turned off.",
"category": "bitcoin", "category": "bitcoin",
"needs_domain": True, "needs_domain": True,
"domain_name": "btcpayserver", "domain_name": "btcpayserver",
@@ -545,9 +545,9 @@ SERVICE_DESCRIPTIONS: dict[str, str] = {
"Addresses on your Sovran_SystemsOS node." "Addresses on your Sovran_SystemsOS node."
), ),
"gnome-remote-desktop.service": ( "gnome-remote-desktop.service": (
"Access your server's full desktop environment from anywhere using any RDP client. " "Access your desktop from a device on your home network — no router setup needed. "
"Manage your system visually without being physically present. " "Sovran_SystemsOS creates the address, username, and password for you: "
"Sovran_SystemsOS sets up secure remote access with generated credentials — connect and go." "just open an RDP app and connect."
), ),
"sshd.service": ( "sshd.service": (
"Secure Shell (SSH) remote access. When enabled, authorized users can connect " "Secure Shell (SSH) remote access. When enabled, authorized users can connect "
@@ -1,22 +1,14 @@
/* Sovran_SystemsOS Hub — Shared domain-prerequisite instructions. /* Sovran_SystemsOS Hub — Shared domain and router instructions.
SINGLE SOURCE OF TRUTH for the "you need a Njal.la domain + router port This is the single source of truth for the simple setup guidance shown in:
forwarding" guidance that must read identically everywhere it appears:
• First-boot onboarding wizard (Server + Desktop role) • First-boot onboarding for Server + Desktop
— onboarding.js step 3 • Feature setup in the Hub
Feature-enable domain modal (Node role, and any role) Domain reconfiguration and troubleshooting
— features.js openDomainSetupModal()
(Lightning Wallet Connections / NWC, BTCPay Server, Haven, …)
• Domain reconfigure / troubleshooting modal
— features.js openDomainReconfigureModal()
Keep these three surfaces word-for-word consistent: always edit this Keep the wording consistent by editing it here. */
file, never fork the wording inline. Plain classic script (no modules) —
both templates load it with a plain <script> tag. */
"use strict"; "use strict";
/* Escape helper local to this file so it is self-contained on both pages. */
function dpEsc(str) { function dpEsc(str) {
return String(str) return String(str)
.replace(/&/g, "&amp;") .replace(/&/g, "&amp;")
@@ -26,105 +18,71 @@ function dpEsc(str) {
.replace(/'/g, "&#39;"); .replace(/'/g, "&#39;");
} }
/* ── "What you'll need" intro ────────────────────────────────────── /* Explain only what the user needs for the step in front of them. */
opts.serviceName — e.g. "Lightning Wallet Connections", or null for
the multi-service (onboarding) wording.
opts.hostExample — e.g. "lightning" → lightning.yourdomain.com */
function renderDomainNeedsHtml(opts) { function renderDomainNeedsHtml(opts) {
opts = opts || {};
var serviceName = opts.serviceName || null; var serviceName = opts.serviceName || null;
var hostExample = dpEsc(opts.hostExample || "myservice"); var purpose = opts.purpose || "";
var html = ""; var html = "";
if (serviceName) { if (purpose === "btcpay") {
html += "<p>To enable <strong>" + dpEsc(serviceName) + "</strong>, you'll need two things first:</p>"; html += "<p><strong>Put your BTCPay Server online</strong></p>";
html += '<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">'; html += "<p>Let people donate Bitcoin, pay you, or visit your online store.</p>";
html += "<li><strong>A domain of your own from <a href=\"https://njal.la\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color:var(--accent-color);\">Njal.la</a></strong> " html += "<p>BTCPay Server needs a domain so people can reach it. "
+ "— privacy-friendly, no personal details required, accepts Bitcoin. " + "Sovran_SystemsOS walks you through getting your domain from "
+ dpEsc(serviceName) + " gets its own hostname: a subdomain (e.g. <code>" + hostExample + ".yourdomain.com</code>) " + '<a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>'
+ "or a separate domain — your choice. Subdomains are free, and one domain can have many.</li>"; + " and connecting it. Just follow the steps below.</p>";
html += "<li><strong>Access to your router</strong> — you'll forward ports <strong>80</strong> and <strong>443</strong> (TCP) " } else if (serviceName) {
+ "to this computer once. All domain-based services share these two ports; " html += "<p>To make <strong>" + dpEsc(serviceName) + "</strong> available outside your home, it needs a domain.</p>";
+ "they're required for HTTPS and SSL certificates.</li>"; html += "<p>Sovran_SystemsOS walks you through getting your domain from "
html += "</ol>"; + '<a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>'
+ " and connecting it. Just follow the steps below.</p>";
} else { } else {
html += "<p><strong>Each service below needs two things set up:</strong></p>"; html += "<p>Your domain is the address people use to reach your services on the internet.</p>";
html += '<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">'; html += "<p>Sovran_SystemsOS walks you through getting your domain from "
html += "<li><strong>A domain of your own from <a href=\"https://njal.la\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color:var(--accent-color);\">Njal.la</a></strong> " + '<a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>'
+ "— privacy-friendly, no personal details required, accepts Bitcoin. " + " and connecting your services. Just follow the steps below.</p>";
+ "Each service gets its own hostname: its own subdomain (e.g. <code>" + hostExample + ".yourdomain.com</code>) "
+ "or a separate domain — your choice. Subdomains are free, and one domain can have many, "
+ "so a single domain can serve every service.</li>";
html += "<li><strong>Access to your router</strong> — you'll forward ports <strong>80</strong> and <strong>443</strong> (TCP) "
+ "to this computer once. All domain-based services share these two ports; "
+ "they're required for HTTPS and SSL certificates.</li>";
html += "</ol>";
} }
return html; return html;
} }
/* ── "How to set it up at Njal.la" steps ─────────────────────────── /* The shortest Njal.la steps that still tell the user exactly what to do. */
opts.hostExample — host part used in the examples (default "call").
opts.pasteHint — where the curl command goes:
"below" (single service) or "next to its service below"
(onboarding, many services). */
function renderNjallaStepsHtml(opts) { function renderNjallaStepsHtml(opts) {
var hostExample = dpEsc((opts && opts.hostExample) || "call"); var hostExample = dpEsc((opts && opts.hostExample) || "call");
var pasteHint = (opts && opts.pasteHint) || "below"; var pasteHint = (opts && opts.pasteHint) || "below";
var html = ""; var html = "";
html += "<p style=\"margin-top:12px;\"><strong>How to set it up at Njal.la:</strong></p>"; html += '<p style="margin-top:12px;"><strong>Get and connect your domain:</strong></p>';
html += '<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">'; html += '<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">';
html += "<li>Create an account at <a href=\"https://njal.la\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color:var(--accent-color);\">https://njal.la</a> and buy a domain.</li>"; html += '<li>Open <a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">Njal.la</a>, create an account, and get a domain.</li>';
html += "<li>Add a <strong>Dynamic</strong> record for the hostname:" html += "<li>Open your domain and add a <strong>Dynamic</strong> record for this service. "
+ '<ul style="margin:4px 0 0 16px;padding:0;line-height:1.7;">' + "In the <strong>Name</strong> box, enter only the first part of the address. "
+ "<li>In the Njal.la <strong>Name</strong> field, type ONLY the host part — the word before your domain.<br>" + "For <code>" + hostExample + ".yourdomain.com</code>, enter <code>" + hostExample + "</code>. "
+ "(For &quot;" + hostExample + ".yourdomain.com&quot; you&apos;d type just: <code>" + hostExample + "</code>.)<br>" + "If you are using the whole domain, leave the box blank or enter <code>@</code>.</li>";
+ "&#9888; Do NOT type the full domain here — Njal.la adds it automatically.</li>" html += "<li>Copy the update command Njal.la gives you and paste it " + pasteHint + ".</li>";
+ "<li>Dedicating a whole separate domain to this service? Leave the Name field blank or use <code>@</code>.</li>"
+ "</ul>"
+ "</li>";
html += "<li>A Dynamic record has <strong>NO IP field</strong> — you don&apos;t enter an IP anywhere. "
+ "It auto-fills once Sovran_SystemsOS runs the update command (on save, and again after every reboot).</li>";
html += "<li>Njal.la gives you a curl command, e.g.:<br>"
+ '<code style="font-size:0.8em;">curl &quot;https://njal.la/update/?h=' + hostExample + '.yourdomain.com&amp;k=abc123&amp;auto&quot;</code><br>'
+ "Copy it and paste it " + pasteHint + ".</li>";
html += "</ol>"; html += "</ol>";
return html; return html;
} }
/* ── "One router task" port-forwarding box ───────────────────────── /* One router task shared by all normal domain-based services. */
opts.internalIp — LAN IP string, or empty/null for generic wording.
opts.plural — true for multi-service (onboarding) wording.
opts.includeSsh — also mention port 22 for SSH (onboarding).
opts.extraNote — extra sentence appended at the end (optional). */
function renderRouterPortsHtml(opts) { function renderRouterPortsHtml(opts) {
opts = opts || {}; opts = opts || {};
var ipPart = opts.internalIp var ipPart = opts.internalIp
? " to this computer&rsquo;s internal IP <strong>" + dpEsc(opts.internalIp) + "</strong>" ? " to this computer at <strong>" + dpEsc(opts.internalIp) + "</strong>"
: " to this computer&rsquo;s internal IP"; : " to this computer";
var serviceWord = opts.plural ? "services" : "service";
var html = ""; var html = "";
html += "🔌 <strong>One router task:</strong> in your router&rsquo;s <strong>port forwarding</strong> settings, " html += "🔌 <strong>One router task:</strong> in your router&rsquo;s <strong>Port Forwarding</strong> settings, "
+ "forward port <strong>80 (TCP)</strong> and port <strong>443 (TCP)</strong>" + "forward <strong>80 (TCP)</strong> and <strong>443 (TCP)</strong>"
+ ipPart + ". Use the <strong>same number for the internal and external port</strong>. " + ipPart + ". If your router asks for outside and inside port numbers, use the same number for both. "
+ "This only needs to be done once — all domain services share these ports — " + "You only need to do this once. All your services share these two ports.";
+ "but HTTPS and SSL certificates won&rsquo;t work without them, so your " + serviceWord + " "
+ "can&rsquo;t be reached from outside your home network. "
+ "You&rsquo;ll need normal access to your router&rsquo;s settings with working port forwarding — "
+ "if your ISP blocks it (e.g. CGNAT), domain-based services can&rsquo;t be reached from the internet. ";
if (opts.includeSsh) {
html += "Add port <strong>22 (TCP)</strong> as well if you want remote SSH access. ";
}
if (opts.extraNote) { if (opts.extraNote) {
html += dpEsc(opts.extraNote); html += " " + dpEsc(opts.extraNote);
} }
return html; return html;
} }
/* ── Async helper: fill a router-box container with the internal IP ── /* Show generic wording immediately, then add the computer's address when found. */
Renders generic text immediately, then upgrades to the concrete LAN IP
if /api/network provides one. Best-effort — never blocks the UI. */
function renderRouterPortsBox(elId, opts) { function renderRouterPortsBox(elId, opts) {
var el = document.getElementById(elId); var el = document.getElementById(elId);
if (!el) return; if (!el) return;
@@ -142,5 +100,5 @@ function renderRouterPortsBox(elId, opts) {
target.innerHTML = renderRouterPortsHtml(next); target.innerHTML = renderRouterPortsHtml(next);
} }
}) })
.catch(function() { /* generic wording already shown — fine */ }); .catch(function() { /* Generic wording is already shown. */ });
} }
+13 -13
View File
@@ -60,6 +60,7 @@ function openDomainSetupModal(feat, onSaved) {
if ($domainSetupTitle) $domainSetupTitle.textContent = "🌐 Domain Setup — " + feat.name; if ($domainSetupTitle) $domainSetupTitle.textContent = "🌐 Domain Setup — " + feat.name;
var isWalletConnections = (feat.id === "nwc-wallets" || feat.domain_name === "lightning"); var isWalletConnections = (feat.id === "nwc-wallets" || feat.domain_name === "lightning");
var isBtcpay = feat.id === "btcpay-web";
var npubField = ""; var npubField = "";
if (feat.id === "haven") { if (feat.id === "haven") {
@@ -77,30 +78,30 @@ function openDomainSetupModal(feat, onSaved) {
var nwcWarning = isWalletConnections var nwcWarning = isWalletConnections
? '<div class="domain-nwc-warning">' + ? '<div class="domain-nwc-warning">' +
'<strong>Lightning Wallet Connections requires its own unique hostname.</strong> ' + '<strong>Lightning Wallet Connections needs its own address.</strong> ' +
'Use a new subdomain such as <code>lightning.yourdomain.com</code>, or a separate domain. ' + 'Use a new address such as <code>lightning.yourdomain.com</code>. Do not use an address already connected to another service.' +
'Do not reuse a domain already assigned to Matrix, Nextcloud, WordPress, BTCPay Server, Vaultwarden, Haven, or another Caddy site.' +
'</div>' '</div>'
: ''; : '';
var domainPlaceholder = isWalletConnections ? "lightning.yourdomain.com" : "myservice.example.com"; var domainPlaceholder = isWalletConnections ? "lightning.yourdomain.com" : (isBtcpay ? "btcpay.yourdomain.com" : "myservice.example.com");
var domainLabelExample = isWalletConnections ? "lightning.yourdomain.com" : "call.yourdomain.com"; var domainLabelExample = isWalletConnections ? "lightning.yourdomain.com" : (isBtcpay ? "btcpay.yourdomain.com" : "call.yourdomain.com");
// Shared instructions (single source of truth: static/js/domain-prereqs.js) — // Shared instructions (single source of truth: static/js/domain-prereqs.js) —
// identical wording to the Server + Desktop onboarding wizard and every other // identical wording to the Server + Desktop onboarding wizard and every other
// domain-based feature, regardless of role (Node / Desktop / Server+Desktop). // domain-based feature, regardless of role (Node / Desktop / Server+Desktop).
var hostExample = isWalletConnections ? "lightning" : "call"; var hostExample = isWalletConnections ? "lightning" : (isBtcpay ? "btcpay" : "call");
var purpose = isBtcpay ? "btcpay" : "";
$domainSetupBody.innerHTML = $domainSetupBody.innerHTML =
'<div class="domain-setup-intro">' + '<div class="domain-setup-intro">' +
nwcWarning + nwcWarning +
renderDomainNeedsHtml({ serviceName: feat.name, hostExample: hostExample }) + renderDomainNeedsHtml({ serviceName: feat.name, hostExample: hostExample, purpose: purpose }) +
renderNjallaStepsHtml({ hostExample: hostExample, pasteHint: "below" }) + renderNjallaStepsHtml({ hostExample: hostExample, pasteHint: "below" }) +
'<div class="onboarding-port-warn" id="domain-router-box" style="margin-top:12px;"></div>' + '<div class="onboarding-port-warn" id="domain-router-box" style="margin-top:12px;"></div>' +
'<p style="margin-top:10px;">Below, enter the full domain for this service — a subdomain (e.g. ' + domainLabelExample + ') or a separate domain — and paste its curl command.</p>' + '<p style="margin-top:10px;">Enter the address for this service and paste the update command from Njal.la.</p>' +
'</div>' + '</div>' +
'<div class="domain-field-group"><label class="domain-field-label" for="domain-subdomain-input">Service domain (e.g. ' + domainLabelExample + '):</label><input class="domain-field-input" type="text" id="domain-subdomain-input" placeholder="' + domainPlaceholder + '" /></div>' + '<div class="domain-field-group"><label class="domain-field-label" for="domain-subdomain-input">Service address (e.g. ' + domainLabelExample + '):</label><input class="domain-field-input" type="text" id="domain-subdomain-input" placeholder="' + domainPlaceholder + '" /></div>' +
'<div class="domain-field-group"><label class="domain-field-label" for="domain-ddns-input">Njal.la Dynamic DNS Update Command:</label><input class="domain-field-input" type="text" id="domain-ddns-input" placeholder="curl &quot;https://njal.la/update/?h=' + domainPlaceholder + '&amp;k=abc123&amp;auto&quot;" /><p class="domain-field-hint"> Paste the full curl command from your Njal.la dashboard\'s Dynamic record</p></div>' + '<div class="domain-field-group"><label class="domain-field-label" for="domain-ddns-input">Njal.la Update Command:</label><input class="domain-field-input" type="text" id="domain-ddns-input" placeholder="curl &quot;https://njal.la/update/?h=' + domainPlaceholder + '&amp;k=abc123&amp;auto&quot;" /><p class="domain-field-hint">Paste the update command from Njal.la</p></div>' +
npubField + npubField +
'<div class="domain-field-actions"><button class="btn btn-close-modal" id="domain-setup-cancel-btn">Cancel</button><button class="btn btn-primary" id="domain-setup-save-btn">Save &amp; Enable</button></div>'; '<div class="domain-field-actions"><button class="btn btn-close-modal" id="domain-setup-cancel-btn">Cancel</button><button class="btn btn-primary" id="domain-setup-save-btn">Save &amp; Enable</button></div>';
@@ -169,9 +170,8 @@ function openDomainReconfigureModal(feat, existingDomain, onSaved) {
var nwcWarning = isWalletConnections var nwcWarning = isWalletConnections
? '<div class="domain-nwc-warning">' + ? '<div class="domain-nwc-warning">' +
'<strong>Lightning Wallet Connections requires its own unique hostname.</strong> ' + '<strong>Lightning Wallet Connections needs its own address.</strong> ' +
'Use a new subdomain such as <code>lightning.yourdomain.com</code>, or a separate domain. ' + 'Use a new address such as <code>lightning.yourdomain.com</code>. Do not use an address already connected to another service.' +
'Do not reuse a domain already assigned to Matrix, Nextcloud, WordPress, BTCPay Server, Vaultwarden, Haven, or another Caddy site.' +
'</div>' '</div>'
: ''; : '';
+7 -12
View File
@@ -364,17 +364,12 @@ async function loadStep3() {
+ renderDomainNeedsHtml({ serviceName: null, hostExample: "call" }) + renderDomainNeedsHtml({ serviceName: null, hostExample: "call" })
+ renderNjallaStepsHtml({ hostExample: "call", pasteHint: "next to its service below" }) + renderNjallaStepsHtml({ hostExample: "call", pasteHint: "next to its service below" })
+ '</div>'; + '</div>';
html += '<p class="onboarding-hint">Enter each service\'s full domain — a subdomain (e.g. <code>call.yourdomain.com</code>) or a separate domain (e.g. <code>call.com</code>) — and its Njal.la DDNS curl command.</p>'; html += '<p class="onboarding-hint">Enter the address for each service and paste its update command from Njal.la.</p>';
// Router note (the same wording is shown again, per-service, whenever a // Router note (the same wording is shown again, per-service, whenever a
// domain-based feature is enabled, and lives on each service tile afterwards) // domain-based feature is enabled, and lives on each service tile afterwards)
html += '<div class="onboarding-port-warn" style="margin-bottom:16px;">' html += '<div class="onboarding-port-warn" style="margin-bottom:16px;">'
+ renderRouterPortsHtml({ + renderRouterPortsHtml({ internalIp: internalIp })
internalIp: internalIp,
plural: true,
includeSsh: true,
extraNote: "Element Call needs a few extra ports (some UDP), and youll be shown exactly which when you enable it.",
})
+ '</div>'; + '</div>';
relevantDomains.forEach(function(d) { relevantDomains.forEach(function(d) {
@@ -382,20 +377,20 @@ async function loadStep3() {
html += '<div class="onboarding-domain-group">'; html += '<div class="onboarding-domain-group">';
html += '<label class="onboarding-domain-label">' + escHtml(d.label) + '</label>'; html += '<label class="onboarding-domain-label">' + escHtml(d.label) + '</label>';
html += '<input class="onboarding-domain-input domain-field-input" type="text" id="domain-input-' + escHtml(d.name) + '" data-domain="' + escHtml(d.name) + '" placeholder="e.g. ' + escHtml(d.name) + '.yourdomain.com" value="' + escHtml(currentVal) + '" />'; html += '<input class="onboarding-domain-input domain-field-input" type="text" id="domain-input-' + escHtml(d.name) + '" data-domain="' + escHtml(d.name) + '" placeholder="e.g. ' + escHtml(d.name) + '.yourdomain.com" value="' + escHtml(currentVal) + '" />';
html += '<label class="onboarding-domain-label onboarding-domain-label--sub">Njal.la DDNS Curl Command</label>'; html += '<label class="onboarding-domain-label onboarding-domain-label--sub">Njal.la Update Command</label>';
html += '<input class="onboarding-domain-input domain-field-input" type="text" id="ddns-input-' + escHtml(d.name) + '" data-ddns="' + escHtml(d.name) + '" placeholder="curl &quot;https://njal.la/update/?h=...&amp;k=...&amp;auto&quot;" />'; html += '<input class="onboarding-domain-input domain-field-input" type="text" id="ddns-input-' + escHtml(d.name) + '" data-ddns="' + escHtml(d.name) + '" placeholder="curl &quot;https://njal.la/update/?h=...&amp;k=...&amp;auto&quot;" />';
html += '<p class="onboarding-hint" style="margin-top:4px;"> Paste the curl URL from your Njal.la dashboard\'s Dynamic record</p>'; html += '<p class="onboarding-hint" style="margin-top:4px;">Paste the update command from Njal.la</p>';
html += '<button type="button" class="btn btn-primary onboarding-domain-save-btn" data-save-domain="' + escHtml(d.name) + '" style="align-self:flex-start;margin-top:8px;font-size:0.82rem;padding:6px 16px;">Save</button>'; html += '<button type="button" class="btn btn-primary onboarding-domain-save-btn" data-save-domain="' + escHtml(d.name) + '" style="align-self:flex-start;margin-top:8px;font-size:0.82rem;padding:6px 16px;">Save</button>';
html += '<span class="onboarding-domain-save-status" id="domain-save-status-' + escHtml(d.name) + '" style="font-size:0.82rem;min-height:1.2em;"></span>'; html += '<span class="onboarding-domain-save-status" id="domain-save-status-' + escHtml(d.name) + '" style="font-size:0.82rem;min-height:1.2em;"></span>';
html += '</div>'; html += '</div>';
}); });
} }
// SSL email section // Contact email section
var emailVal = domainValues["sslemail"] || ""; var emailVal = domainValues["sslemail"] || "";
html += '<div class="onboarding-domain-group onboarding-domain-group--email">'; html += '<div class="onboarding-domain-group onboarding-domain-group--email">';
html += '<label class="onboarding-domain-label">📧 SSL Certificate Email</label>'; html += '<label class="onboarding-domain-label">📧 Email Address</label>';
html += '<p class="onboarding-hint onboarding-hint--inline">Let\'s Encrypt uses this for certificate expiry notifications.</p>'; html += '<p class="onboarding-hint onboarding-hint--inline">Used to send important notices about your services.</p>';
html += '<input class="onboarding-domain-input domain-field-input" type="email" id="ssl-email-input" placeholder="you@example.com" value="' + escHtml(emailVal) + '" />'; html += '<input class="onboarding-domain-input domain-field-input" type="email" id="ssl-email-input" placeholder="you@example.com" value="' + escHtml(emailVal) + '" />';
html += '<button type="button" class="btn btn-primary onboarding-domain-save-btn" data-save-email="true" style="align-self:flex-start;margin-top:8px;font-size:0.82rem;padding:6px 16px;">Save</button>'; html += '<button type="button" class="btn btn-primary onboarding-domain-save-btn" data-save-email="true" style="align-self:flex-start;margin-top:8px;font-size:0.82rem;padding:6px 16px;">Save</button>';
html += '<span class="onboarding-domain-save-status" id="domain-save-status-email" style="font-size:0.82rem;min-height:1.2em;"></span>'; html += '<span class="onboarding-domain-save-status" id="domain-save-status-email" style="font-size:0.82rem;min-height:1.2em;"></span>';
+4 -12
View File
@@ -197,22 +197,14 @@
encrypted messaging, password management, cloud storage, website hosting, and more. encrypted messaging, password management, cloud storage, website hosting, and more.
</p> </p>
<div class="upgrade-info-box"> <div class="upgrade-info-box">
<p class="upgrade-info-title">⚠ What you should know:</p> <p class="upgrade-info-title">Simple, guided setup:</p>
<ul class="upgrade-info-list"> <ul class="upgrade-info-list">
<li>You will need to purchase domains for your services — <a href="https://njal.la" target="_blank" rel="noopener noreferrer">Njal.la</a> is the only supported domain provider</li> <li>Sovran_SystemsOS walks you through getting your domain from <a href="https://njal.la" target="_blank" rel="noopener noreferrer">Njal.la</a> and connecting your services</li>
<li>Some services require ports to be opened on your router</li> <li>To make your services available outside your home, complete one router task: forward ports <strong>80 and 443</strong> to this computer</li>
</ul>
</div>
<div class="upgrade-info-box">
<p class="upgrade-info-title">️ Good to know:</p>
<ul class="upgrade-info-list">
<li>Your services will be accessible via your home internet connection. Your approximate geographic area may be visible through DNS records — this is normal for all self-hosted services</li>
<li>Your Bitcoin node remains fully private over Tor</li>
</ul> </ul>
</div> </div>
<p class="support-desc"> <p class="support-desc">
<strong>Don't worry</strong> — the Hub will walk you through every step after the upgrade. The Hub guides you through every step.
Domain setup, port forwarding, and configuration are all guided.
</p> </p>
<p class="support-desc upgrade-rebuild-note"> <p class="support-desc upgrade-rebuild-note">
The system will rebuild after upgrading. This may take several minutes. The system will rebuild after upgrading. This may take several minutes.
@@ -123,10 +123,10 @@
<div class="onboarding-panel" id="step-3" style="display:none"> <div class="onboarding-panel" id="step-3" style="display:none">
<div class="onboarding-step-header"> <div class="onboarding-step-header">
<span class="onboarding-step-icon">🌐</span> <span class="onboarding-step-icon">🌐</span>
<h2 class="onboarding-step-title">Domain Configuration</h2> <h2 class="onboarding-step-title">Connect Your Services</h2>
<p class="onboarding-step-desc"> <p class="onboarding-step-desc">
Sovran_SystemsOS uses <strong><a href="https://njal.la" target="_blank" style="color: var(--accent-color);">Njal.la</a></strong> for domains and Dynamic DNS, and your router needs ports <strong>80</strong> and <strong>443</strong> (TCP) forwarded to this computer. Sovran_SystemsOS walks you through getting your domain from <strong><a href="https://njal.la" target="_blank" style="color: var(--accent-color);">Njal.la</a></strong> and connecting your services.
Everything you need — Njal.la account, Dynamic records, and the one router task — is laid out step by step below. To make your services available outside your home, youll complete one router task: forward ports <strong>80</strong> and <strong>443</strong> to this computer.
</p> </p>
</div> </div>
<div class="onboarding-card" id="step-3-body"> <div class="onboarding-card" id="step-3-body">
+1 -35
View File
@@ -423,40 +423,6 @@ class InstallerWindow(Adw.ApplicationWindow):
sep.set_margin_end(40) sep.set_margin_end(40)
outer.append(sep) outer.append(sep)
notice_frame = Gtk.Frame()
notice_frame.add_css_class("card")
notice_frame.set_margin_start(40)
notice_frame.set_margin_end(40)
notice_frame.set_margin_top(20)
notice_frame.set_margin_bottom(4)
notice_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
notice_box.set_margin_top(12)
notice_box.set_margin_bottom(12)
notice_box.set_margin_start(16)
notice_box.set_margin_end(16)
notice_icon = symbolic_icon("dialog-information-symbolic")
notice_icon.set_valign(Gtk.Align.START)
notice_box.append(notice_icon)
notice_lbl = Gtk.Label()
notice_lbl.set_use_markup(True)
notice_lbl.set_wrap(True)
notice_lbl.set_xalign(0)
notice_lbl.set_halign(Gtk.Align.FILL)
notice_lbl.set_markup(
"<span weight='bold'>Heads up — Server + Desktop prerequisites</span>\n"
"• A domain or subdomain from <span weight='bold'>https://njal.la</span>\n"
"• The ability to open / forward ports on your router\n\n"
"Don't worry — after install, the onboarding wizard walks you through every step.\n"
"<span size='small'>Desktop Only and Node Only do not require a domain or port forwarding.</span>"
)
notice_box.append(notice_lbl)
notice_frame.set_child(notice_box)
outer.append(notice_frame)
if self.virtualization: if self.virtualization:
vm_frame = Gtk.Frame() vm_frame = Gtk.Frame()
vm_frame.add_css_class("card") vm_frame.add_css_class("card")
@@ -505,7 +471,7 @@ class InstallerWindow(Adw.ApplicationWindow):
# Role cards # Role cards
roles = [ roles = [
("Server + Desktop", ("Server + Desktop",
"Full sovereign experience: beautiful desktop, your own cloud, secure messaging, Bitcoin node, and more.", "Full sovereignty: host your own websites, cloud, chat, passwords, and Bitcoin services instead of relying on Big Tech. Sovran_SystemsOS walks you through getting your domain from Njal.la and connecting everything. One router task is required: forward ports 80 and 443 to this computer.",
"Server+Desktop"), "Server+Desktop"),
("Desktop Only", ("Desktop Only",
"A beautiful, easy-to-use desktop without the background server applications.", "A beautiful, easy-to-use desktop without the background server applications.",