Compare commits
23
Commits
v1.0.0
...
5041e5202f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5041e5202f | ||
|
|
8baefe1bfd | ||
|
|
d8108dae0f | ||
|
|
3eb347da06 | ||
|
|
8d15b71c06 | ||
|
|
a28ad04b55 | ||
|
|
6720b602ba | ||
|
|
4e2264d5db | ||
|
|
82a6221880 | ||
|
|
6ec1faf3e6 | ||
|
|
d3beee602d | ||
|
|
29960e9937 | ||
|
|
417456485a | ||
|
|
0945092dde | ||
|
|
6f12117521 | ||
|
|
8bf8814fa7 | ||
|
|
d90b5b091b | ||
|
|
4275ac1d2f | ||
|
|
07b36d62d2 | ||
|
|
5fb8279d61 | ||
|
|
6eb63d3f85 | ||
|
|
2702854513 | ||
|
|
106537cc63 |
@@ -10,6 +10,14 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<img src="assets/desktop-screenshot.png" alt="Sovran_SystemsOS desktop showing application dock and PRIVACY. SOVEREIGNTY. BITCOIN. tagline" width="800" />
|
||||
|
||||
*The Sovran_SystemsOS desktop — "Privacy. Sovereignty. Bitcoin."*
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -281,9 +281,6 @@ FEATURE_SERVICE_MAP = {
|
||||
}
|
||||
|
||||
# Port requirements for service tiles (keyed by unit name or icon)
|
||||
_PORTS_MATRIX_FEDERATION = [
|
||||
{"port": "8448", "protocol": "TCP", "description": "Matrix server-to-server federation"},
|
||||
]
|
||||
_PORTS_ELEMENT_CALLING = [
|
||||
{"port": "7881", "protocol": "TCP", "description": "LiveKit WebRTC signalling"},
|
||||
{"port": "7882", "protocol": "UDP", "description": "LiveKit media (UDP mux)"},
|
||||
@@ -296,7 +293,7 @@ SERVICE_PORT_REQUIREMENTS: dict[str, list[dict]] = {
|
||||
# Infrastructure
|
||||
"caddy.service": [],
|
||||
# Communication
|
||||
"matrix-synapse.service": _PORTS_MATRIX_FEDERATION,
|
||||
"matrix-synapse.service": [],
|
||||
"livekit.service": _PORTS_ELEMENT_CALLING,
|
||||
# Domain-based apps (80/443 handled by end-to-end domain reachability checks)
|
||||
"btcpayserver.service": [],
|
||||
@@ -2983,28 +2980,16 @@ async def api_service_detail(unit: str, icon: str | None = None):
|
||||
"status": ps,
|
||||
"description": p.get("description", ""),
|
||||
})
|
||||
extra_ports = port_statuses if unit in ("matrix-synapse.service", "livekit.service") else []
|
||||
extra_ports = port_statuses if unit == "livekit.service" else []
|
||||
|
||||
if needs_domain and unit in ("matrix-synapse.service", "livekit.service"):
|
||||
if needs_domain and unit == "livekit.service":
|
||||
if has_domain_issues:
|
||||
domain_check_steps.append({
|
||||
"step": 4,
|
||||
"label": "Federation Port" if unit == "matrix-synapse.service" else "Additional Ports Required",
|
||||
"label": "Additional Ports Required",
|
||||
"status": "skipped",
|
||||
"detail": "Skipped until Steps 1-3 are complete",
|
||||
})
|
||||
elif unit == "matrix-synapse.service":
|
||||
if extra_ports:
|
||||
matrix_open = extra_ports[0]["status"] != "closed"
|
||||
domain_check_steps.append({
|
||||
"step": 4,
|
||||
"label": "Federation Port",
|
||||
"status": "ok" if matrix_open else "error",
|
||||
"detail": (
|
||||
f"Matrix federation port 8448 (TCP) is {'open' if matrix_open else 'closed'}.\n"
|
||||
f"Matrix federation requires port 8448 (TCP) forwarded to {internal_ip}"
|
||||
),
|
||||
})
|
||||
else:
|
||||
extra_open = all(p["status"] != "closed" for p in extra_ports)
|
||||
domain_check_steps.append({
|
||||
|
||||
@@ -73,22 +73,47 @@ function openDomainSetupModal(feat, onSaved) {
|
||||
npubField = '<div class="domain-field-group"><label class="domain-field-label" for="domain-npub-input">Nostr Public Key (npub1...):</label><input class="domain-field-input" type="text" id="domain-npub-input" placeholder="npub1..." value="' + escHtml(currentNpub) + '" /></div>';
|
||||
}
|
||||
|
||||
var externalIp = _cachedExternalIp || "your external IP";
|
||||
var introHtml;
|
||||
if (_currentRole === "node") {
|
||||
introHtml =
|
||||
'<p>To enable <strong>' + escHtml(feat.name) + '</strong>, it needs its own domain from Njal.la.</p>' +
|
||||
'<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">' +
|
||||
'<li>Create an account at <a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">njal.la</a>.</li>' +
|
||||
'<li>Set up a domain for it — either a free subdomain or a separate domain. Pick one option:</li>' +
|
||||
'</ol>';
|
||||
} else {
|
||||
introHtml =
|
||||
'<p>To enable <strong>' + escHtml(feat.name) + '</strong>, it needs its own domain from Njal.la. ' +
|
||||
'In your Njal.la account, set up a domain for it — either a free subdomain or a separate domain. Pick one option:</p>';
|
||||
}
|
||||
|
||||
$domainSetupBody.innerHTML =
|
||||
'<div class="domain-setup-intro">' +
|
||||
'<p><strong>Before continuing:</strong></p>' +
|
||||
'<ol>' +
|
||||
'<li>Create an account at <a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">https://njal.la</a></li>' +
|
||||
'<li>Purchase a new domain on Njal.la, or create a subdomain from a domain you already own. Tip: Subdomains are free to create — you only need to purchase one domain, and you can add as many subdomains as you need at no extra cost.</li>' +
|
||||
'<li>In the Njal.la web interface, create a <strong>Dynamic</strong> record pointing to this machine\'s external IP address:<br>' +
|
||||
'<span style="display:inline-block;margin-top:4px;padding:4px 10px;background:var(--card-color);border:1px solid var(--border-color);border-radius:6px;font-family:monospace;font-size:1em;font-weight:700;">' + escHtml(externalIp) + '</span></li>' +
|
||||
'<li>Njal.la will give you a curl command like:<br>' +
|
||||
'<code style="font-size:0.8em;">curl "https://njal.la/update/?h=sub.domain.com&k=abc123&auto"</code></li>' +
|
||||
'<li>Enter the subdomain and paste that curl command below</li>' +
|
||||
introHtml +
|
||||
'<details style="margin-top:10px;">' +
|
||||
'<summary style="cursor:pointer;font-weight:600;">Option A — Free subdomain (recommended)</summary>' +
|
||||
'<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">' +
|
||||
'<li>In Njal.la, open a domain you own and click "Add record".</li>' +
|
||||
'<li>Set record type to <strong>Dynamic</strong>.</li>' +
|
||||
'<li>In the <strong>Name</strong> field, type ONLY the host part — the word before your domain.<br>' +
|
||||
'(Example only, your choice — for "call.yourdomain.com" you'd type just: <code>call</code>)<br>' +
|
||||
'⚠ Do NOT type the full domain here — Njal.la adds it automatically.</li>' +
|
||||
'<li>A Dynamic record has NO IP field — the IP auto-fills after the rebuild/reboot.</li>' +
|
||||
'<li>Copy the curl command Njal.la gives you, e.g.:<br>' +
|
||||
'<code style="font-size:0.8em;">curl "https://njal.la/update/?h=call.yourdomain.com&k=abc123&auto"</code></li>' +
|
||||
'</ol>' +
|
||||
'</details>' +
|
||||
'<details style="margin-top:6px;">' +
|
||||
'<summary style="cursor:pointer;font-weight:600;">Option B — Separate / new domain</summary>' +
|
||||
'<ol style="margin:8px 0 0 16px;padding:0;line-height:1.7;">' +
|
||||
'<li>In Njal.la, buy the domain you want.</li>' +
|
||||
'<li>Add a Dynamic record as in Option A. If this domain is dedicated to the service, leave the Name field blank or use <code>@</code>.</li>' +
|
||||
'<li>Copy the curl command Njal.la gives you.</li>' +
|
||||
'</ol>' +
|
||||
'</details>' +
|
||||
'<p style="margin-top:10px;">Below, enter the full domain for this service — a subdomain (e.g. call.yourdomain.com) or a separate domain (e.g. call.com) — and paste its curl command.</p>' +
|
||||
'</div>' +
|
||||
'<div class="domain-field-group"><label class="domain-field-label" for="domain-subdomain-input">Subdomain (e.g. myservice.example.com):</label><input class="domain-field-input" type="text" id="domain-subdomain-input" placeholder="myservice.example.com" /></div>' +
|
||||
'<div class="domain-field-group"><label class="domain-field-label" for="domain-subdomain-input">Service domain (e.g. call.yourdomain.com):</label><input class="domain-field-input" type="text" id="domain-subdomain-input" placeholder="myservice.example.com" /></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 "https://njal.la/update/?h=myservice.example.com&k=abc123&auto"" /><p class="domain-field-hint">ℹ Paste the full curl command from your Njal.la dashboard\'s Dynamic record</p></div>' +
|
||||
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 & Enable</button></div>';
|
||||
@@ -103,7 +128,7 @@ function openDomainSetupModal(feat, onSaved) {
|
||||
ddnsUrl = ddnsUrl.trim();
|
||||
npub = npub.trim();
|
||||
|
||||
if (!subdomain) { alert("Please enter a subdomain."); return; }
|
||||
if (!subdomain) { alert("Please enter a domain."); return; }
|
||||
if (feat.id === "haven" && !npub) { alert("Please enter your Nostr public key."); return; }
|
||||
|
||||
var saveBtn = document.getElementById("domain-setup-save-btn");
|
||||
@@ -159,14 +184,14 @@ function openDomainReconfigureModal(feat, existingDomain, onSaved) {
|
||||
'<p><strong>Troubleshooting steps:</strong></p>' +
|
||||
'<ol>' +
|
||||
'<li>Log into your Njal.la dashboard at <a href="https://njal.la" target="_blank" rel="noopener noreferrer" style="color:var(--accent-color);">https://njal.la</a></li>' +
|
||||
'<li>Find the DNS record for <strong>' + escHtml(currentDomain || "your domain") + '</strong></li>' +
|
||||
'<li>Find the DNS record for <strong>' + escHtml(currentDomain || "your domain") + '</strong>. In Njal.la\'s Name field, note that only the host part is stored (the word before the domain) — not the full domain.</li>' +
|
||||
'<li>Verify it has a <strong>Dynamic</strong> record pointing to your current external IP:<br>' +
|
||||
'<span style="display:inline-block;margin-top:4px;padding:4px 10px;background:var(--card-color);border:1px solid var(--border-color);border-radius:6px;font-family:monospace;font-size:1em;font-weight:700;">' + escHtml(externalIp) + '</span></li>' +
|
||||
'<li>If the IP is wrong or the record is missing, update it</li>' +
|
||||
'<li>If you changed the DDNS curl command, paste the updated one below</li>' +
|
||||
'</ol>' +
|
||||
'</div>' +
|
||||
'<div class="domain-field-group"><label class="domain-field-label" for="domain-subdomain-input">Subdomain (e.g. myservice.example.com):</label><input class="domain-field-input" type="text" id="domain-subdomain-input" placeholder="myservice.example.com" value="' + escHtml(currentDomain) + '" /></div>' +
|
||||
'<div class="domain-field-group"><label class="domain-field-label" for="domain-subdomain-input">Service domain (e.g. call.yourdomain.com):</label><input class="domain-field-input" type="text" id="domain-subdomain-input" placeholder="myservice.example.com" value="' + escHtml(currentDomain) + '" /></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 "https://njal.la/update/?h=myservice.example.com&k=abc123&auto"" /><p class="domain-field-hint">ℹ Paste the full curl command from your Njal.la dashboard\'s Dynamic record</p></div>' +
|
||||
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 & Update</button></div>';
|
||||
|
||||
@@ -333,8 +333,6 @@ async function loadStep3() {
|
||||
return;
|
||||
}
|
||||
|
||||
var externalIp = (networkData && networkData.external_ip) || "Unknown (could not retrieve)";
|
||||
|
||||
// Build set of enabled service units
|
||||
var enabledUnits = new Set();
|
||||
(_servicesData || []).forEach(function(svc) {
|
||||
@@ -352,25 +350,31 @@ async function loadStep3() {
|
||||
html += '<p class="onboarding-body-text">No domain-based services are enabled for your role. You can skip this step.</p>';
|
||||
} else {
|
||||
html += '<div class="onboarding-port-warn" style="margin-bottom:16px;">'
|
||||
+ '<strong>Before you continue:</strong>'
|
||||
+ '<p style="margin:0 0 8px;"><strong>Sovran_SystemsOS uses Njal.la for domains and Dynamic DNS.</strong></p>'
|
||||
+ '<ol style="margin:8px 0 0 16px; padding:0; line-height:1.7;">'
|
||||
+ '<li>Create an account at <a href="https://njal.la" target="_blank" style="color:var(--accent-color);">https://njal.la</a></li>'
|
||||
+ '<li>Purchase a new domain on Njal.la, or create a subdomain from a domain you already own. Tip: Subdomains are free to create — you only need to purchase one domain, and you can add as many subdomains as you need at no extra cost.</li>'
|
||||
+ '<li>In the Njal.la web interface, create a <strong>Dynamic</strong> record pointing to this machine\'s external IP address:<br>'
|
||||
+ '<span style="display:inline-block;margin-top:4px;padding:4px 12px;background:var(--card-color);border:1px solid var(--border-color);border-radius:6px;font-family:monospace;font-size:1.1em;font-weight:700;letter-spacing:0.03em;">' + escHtml(externalIp) + '</span></li>'
|
||||
+ '<li>Njal.la will give you a curl command like:<br>'
|
||||
+ '<code style="font-size:0.8em;">curl "https://njal.la/update/?h=sub.domain.com&k=abc123&auto"</code></li>'
|
||||
+ '<li>Enter the subdomain and paste that curl command below for each service</li>'
|
||||
+ '<li>Create an account at <a href="https://njal.la" target="_blank" style="color:var(--accent-color);">https://njal.la</a>.</li>'
|
||||
+ '<li>Buy at least one domain. Each service below needs its own domain — you can either give each service its own subdomain of a single domain you buy (subdomains are free, and one domain can have many), OR use a separate domain for each. Your choice.</li>'
|
||||
+ '<li>For each service, add a <strong>Dynamic</strong> record in Njal.la:'
|
||||
+ '<ul style="margin:4px 0 0 16px;padding:0;line-height:1.7;">'
|
||||
+ '<li>In the Njal.la <strong>Name</strong> field, type ONLY the host part — the word before your domain.<br>'
|
||||
+ '(Example only, your choice — for "call.yourdomain.com" you'd type just: <code>call</code>.)<br>'
|
||||
+ 'If you bought a whole separate domain just for this service, leave Name blank or use <code>@</code>.<br>'
|
||||
+ '⚠ Do NOT type the full domain in the Name field — Njal.la adds it automatically.</li>'
|
||||
+ '<li>A Dynamic record has NO IP field. You don't enter an IP anywhere — it auto-fills once Sovran_SystemsOS updates it (on save, and again after reboot).</li>'
|
||||
+ '</ul>'
|
||||
+ '</li>'
|
||||
+ '<li>Njal.la gives you a curl command like:<br>'
|
||||
+ '<code style="font-size:0.8em;">curl "https://njal.la/update/?h=call.yourdomain.com&k=abc123&auto"</code></li>'
|
||||
+ '</ol>'
|
||||
+ '</div>';
|
||||
html += '<p class="onboarding-hint">Enter each fully-qualified subdomain (e.g. <code>matrix.yourdomain.com</code>) and its Njal.la DDNS curl command.</p>';
|
||||
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>';
|
||||
relevantDomains.forEach(function(d) {
|
||||
var currentVal = (_domainsData && _domainsData[d.name]) || "";
|
||||
html += '<div class="onboarding-domain-group">';
|
||||
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 += '<label class="onboarding-domain-label onboarding-domain-label--sub">Njal.la DDNS Curl 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 "https://njal.la/update/?h=' + escHtml(d.name) + '.yourdomain.com&k=abc123&auto"" />';
|
||||
html += '<input class="onboarding-domain-input domain-field-input" type="text" id="ddns-input-' + escHtml(d.name) + '" data-ddns="' + escHtml(d.name) + '" placeholder="curl "https://njal.la/update/?h=...&k=...&auto"" />';
|
||||
html += '<p class="onboarding-hint" style="margin-top:4px;">ℹ Paste the curl URL from your Njal.la dashboard\'s Dynamic record</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 += '<span class="onboarding-domain-save-status" id="domain-save-status-' + escHtml(d.name) + '" style="font-size:0.82rem;min-height:1.2em;"></span>';
|
||||
@@ -545,14 +549,13 @@ async function loadStep4() {
|
||||
html += '<tr><td class="port-req-port">80</td><td class="port-req-proto">TCP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">HTTP</td></tr>';
|
||||
html += '<tr><td class="port-req-port">443</td><td class="port-req-proto">TCP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">HTTPS</td></tr>';
|
||||
html += '<tr><td class="port-req-port">22</td><td class="port-req-proto">TCP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">SSH Remote Access</td></tr>';
|
||||
html += '<tr><td class="port-req-port">8448</td><td class="port-req-proto">TCP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">Matrix Federation</td></tr>';
|
||||
html += '</tbody></table>';
|
||||
html += '</div>';
|
||||
|
||||
// Optional ports table
|
||||
html += '<div class="onboarding-port-section" style="margin-bottom:20px;">';
|
||||
html += '<div class="onboarding-port-section-title" style="font-weight:700;margin-bottom:4px;">Optional — Only needed if you enable Element Calling:</div>';
|
||||
html += '<div style="font-size:0.88em;margin-bottom:8px;color:var(--color-text-muted,#888);">These 5 additional port openings are required on top of the 4 required ports above.</div>';
|
||||
html += '<div style="font-size:0.88em;margin-bottom:8px;color:var(--color-text-muted,#888);">These 5 additional port openings are required on top of the 3 required ports above.</div>';
|
||||
html += '<table class="onboarding-port-table">';
|
||||
html += '<thead><tr><th>Port</th><th>Protocol</th><th>Forward to</th><th>Purpose</th></tr></thead>';
|
||||
html += '<tbody>';
|
||||
@@ -560,14 +563,15 @@ async function loadStep4() {
|
||||
html += '<tr><td class="port-req-port">7882</td><td class="port-req-proto">UDP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">LiveKit media (UDP mux)</td></tr>';
|
||||
html += '<tr><td class="port-req-port">5349</td><td class="port-req-proto">TCP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">TURN over TLS</td></tr>';
|
||||
html += '<tr><td class="port-req-port">3478</td><td class="port-req-proto">UDP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">TURN (STUN/relay)</td></tr>';
|
||||
html += '<tr><td class="port-req-port">30000–40000</td><td class="port-req-proto">TCP/UDP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">TURN relay (WebRTC)</td></tr>';
|
||||
html += '<tr><td class="port-req-port">30000–40000</td><td class="port-req-proto">TCP & UDP</td><td class="port-req-internal-ip">' + ip + '</td><td class="port-req-desc">TURN relay (WebRTC)</td></tr>';
|
||||
html += '</tbody></table>';
|
||||
html += '<div style="font-size:0.85em;margin-top:6px;color:var(--color-text-muted,#888);">ℹ The <strong>30000–40000</strong> range is a single forwarding rule — just set its protocol to <strong>both TCP and UDP</strong> (often shown as "Both" or "TCP/UDP" on your router).</div>';
|
||||
html += '</div>';
|
||||
|
||||
// Totals
|
||||
html += '<div class="onboarding-port-totals">';
|
||||
html += '<strong>Total port openings: 4</strong> (without Element Calling)<br>';
|
||||
html += '<strong>Total port openings: 9</strong> (with Element Calling — 4 required + 5 optional)';
|
||||
html += '<strong>Total port openings: 3</strong> (without Element Calling)<br>';
|
||||
html += '<strong>Total port openings: 8</strong> (with Element Calling — 3 required + 5 optional)';
|
||||
html += '</div>';
|
||||
|
||||
html += '<div class="onboarding-port-warn" style="margin-bottom:16px;">'
|
||||
|
||||
@@ -128,9 +128,8 @@
|
||||
<h2 class="onboarding-step-title">Domain Configuration</h2>
|
||||
<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.
|
||||
First, create an account at <strong>Njal.la</strong> and purchase a new domain, or create a subdomain from a domain you already own. Tip: Subdomains are free to create — you only need to purchase one domain, and you can add as many subdomains as you need at no extra cost.
|
||||
Then, in the Njal.la web interface, create a <strong>Dynamic</strong> record pointing to this machine's external IP address (shown below).
|
||||
Finally, paste the DDNS curl command from your Njal.la dashboard for each service below.
|
||||
Create an account at Njal.la, then for each service below, add a <strong>Dynamic</strong> record — no IP needed, it auto-populates once the DDNS curl command runs.
|
||||
Paste the curl command from your Njal.la dashboard for each service.
|
||||
</p>
|
||||
</div>
|
||||
<div class="onboarding-card" id="step-3-body">
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 442 KiB |
+1
-1
@@ -149,7 +149,7 @@
|
||||
gparted pv unzip parted screen zenity
|
||||
libargon2 gnome-terminal libreoffice-fresh
|
||||
dig firefox wp-cli axel
|
||||
lk-jwt-service livekit-libwebrtc livekit-cli livekit
|
||||
lk-jwt-service livekit-libwebrtc livekit
|
||||
matrix-synapse age
|
||||
];
|
||||
|
||||
|
||||
Generated
+15
-15
@@ -5,11 +5,11 @@
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780397635,
|
||||
"narHash": "sha256-6WH7LKD6i91VLWoz4mEpoULtqVinCEZxG7ZjJPMSi3k=",
|
||||
"lastModified": 1781789880,
|
||||
"narHash": "sha256-HU/J4pFFkC2XXsYO8B3QFneTV2NWEXFuNi4QmV/4ZA8=",
|
||||
"owner": "emmanuelrosa",
|
||||
"repo": "btc-clients-nix",
|
||||
"rev": "feacd7684dc6bfcd49c57764944a2049bbd71924",
|
||||
"rev": "10f0300231075e6c7417030fbcbf9f056d0a7c21",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -139,11 +139,11 @@
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1780453794,
|
||||
"narHash": "sha256-bXMRa9VTsHSPXL4Cw8R6JJLQeY3Y/IP4+YJCYVmQ7FY=",
|
||||
"lastModified": 1781216227,
|
||||
"narHash": "sha256-9mUW6gNwoN2SWc/l0fW4svPNOulXLl8ijqKyeSOGgJE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6b316287bae2ee04c9b93c8c858d930fd07d7338",
|
||||
"rev": "a0374025a863d007d98e3297f6aa46cc3141c2f0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -187,11 +187,11 @@
|
||||
},
|
||||
"nixpkgs_3": {
|
||||
"locked": {
|
||||
"lastModified": 1780243769,
|
||||
"narHash": "sha256-x5UQuRsH3MqI0U9afaXSNqzTPSeZlRLvFAav2Ux1pNw=",
|
||||
"lastModified": 1781577229,
|
||||
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "331800de5053fcebacf6813adb5db9c9dca22a0c",
|
||||
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -203,11 +203,11 @@
|
||||
},
|
||||
"nixpkgs_4": {
|
||||
"locked": {
|
||||
"lastModified": 1780336545,
|
||||
"narHash": "sha256-vhVhuXzFrIOfcssC/9hDHx7MHzDKjF3keHuREOQqQiQ=",
|
||||
"lastModified": 1781607440,
|
||||
"narHash": "sha256-rxO+uc/KFbSJp+pgyXRuAX6QlG9hJdnt0BXpEQRXY+U=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4df1b885d76a54e1aa1a318f8d16fd6005b6401f",
|
||||
"rev": "3e41b24abd260e8f71dbe2f5737d24122f972158",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -224,11 +224,11 @@
|
||||
"systems": "systems_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1780646548,
|
||||
"narHash": "sha256-Ckyl/l1XBmEwnaHcHD8PvBZk1uph0NqwbJ//CAvB7iE=",
|
||||
"lastModified": 1781713417,
|
||||
"narHash": "sha256-Kaj44jTNmnaFhKrcADx8nXmUYPa7l2HYfb7m6lEPy7Q=",
|
||||
"owner": "nix-community",
|
||||
"repo": "nixvim",
|
||||
"rev": "816a15282e58678dde831477964987d0262d4293",
|
||||
"rev": "caee4e5d4161778815f522d9ea1c9e3dc42462b7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -16,7 +16,6 @@ in
|
||||
{
|
||||
imports = [
|
||||
"${modulesPath}/installer/cd-dvd/installation-cd-graphical-gnome.nix"
|
||||
./branding.nix
|
||||
];
|
||||
|
||||
image.baseName = lib.mkForce "Sovran_SystemsOS";
|
||||
|
||||
@@ -94,10 +94,10 @@ EOF
|
||||
$MATRIX {
|
||||
reverse_proxy /_matrix/* http://localhost:8008
|
||||
reverse_proxy /_synapse/client/* http://localhost:8008
|
||||
handle /.well-known/matrix/server {
|
||||
header Content-Type application/json
|
||||
respond \`{"m.server":"$MATRIX:443"}\` 200
|
||||
}
|
||||
|
||||
$MATRIX:8448 {
|
||||
reverse_proxy http://localhost:8008
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
|
||||
+58
-21
@@ -34,8 +34,8 @@ lib.mkIf config.sovran_systemsOS.features.element-calling {
|
||||
};
|
||||
|
||||
####### ENSURE SERVICES START AFTER KEY EXISTS #######
|
||||
systemd.services.livekit.after = [ "livekit-key-setup.service" ];
|
||||
systemd.services.livekit.wants = [ "livekit-key-setup.service" ];
|
||||
systemd.services.livekit.after = [ "livekit-key-setup.service" "livekit-turn-setup.service" ];
|
||||
systemd.services.livekit.wants = [ "livekit-key-setup.service" "livekit-turn-setup.service" ];
|
||||
systemd.services.lk-jwt-service.after = [ "livekit-key-setup.service" ];
|
||||
systemd.services.lk-jwt-service.wants = [ "livekit-key-setup.service" ];
|
||||
|
||||
@@ -68,10 +68,7 @@ $MATRIX {
|
||||
header /.well-known/matrix/* Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
|
||||
header /.well-known/matrix/* Access-Control-Allow-Headers "X-Requested-With, Content-Type, Authorization"
|
||||
respond /.well-known/matrix/client \`{ "m.homeserver": {"base_url": "https://$MATRIX" }, "org.matrix.msc4143.rtc_foci": [{ "type":"livekit", "livekit_service_url":"https://$ELEMENT_CALLING/livekit/jwt" }] }\`
|
||||
}
|
||||
|
||||
$MATRIX:8448 {
|
||||
reverse_proxy http://localhost:8008
|
||||
respond /.well-known/matrix/server \`{"m.server":"$MATRIX:443"}\`
|
||||
}
|
||||
|
||||
$ELEMENT_CALLING {
|
||||
@@ -92,11 +89,17 @@ EOF
|
||||
'';
|
||||
};
|
||||
|
||||
####### LIVEKIT RUNTIME CONFIG #######
|
||||
systemd.services.livekit-runtime-config = {
|
||||
description = "Generate LiveKit runtime config from domain files";
|
||||
####### LIVEKIT TURN SETUP (runtime cert + config) #######
|
||||
# Replaces the old dead livekit-runtime-config.service. At runtime this:
|
||||
# * reads the matrix domain from /var/lib/domains/matrix (never hardcoded)
|
||||
# * copies Caddy's already-issued matrix cert/key into /var/lib/livekit
|
||||
# so LoadCredential can stage them for the (DynamicUser) livekit unit
|
||||
# * writes a complete LiveKit config (with turn.domain substituted) that the
|
||||
# overridden ExecStart loads.
|
||||
systemd.services.livekit-turn-setup = {
|
||||
description = "Stage TURN cert and generate LiveKit runtime config from domain files";
|
||||
after = [ "caddy.service" "livekit-key-setup.service" ];
|
||||
before = [ "livekit.service" ];
|
||||
after = [ "livekit-key-setup.service" ];
|
||||
requiredBy = [ "livekit.service" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
@@ -106,20 +109,42 @@ EOF
|
||||
unitConfig = {
|
||||
ConditionPathExists = "/var/lib/domains/element-calling";
|
||||
};
|
||||
path = [ pkgs.coreutils ];
|
||||
path = [ pkgs.coreutils pkgs.findutils ];
|
||||
script = ''
|
||||
MATRIX=$(cat /var/lib/domains/matrix)
|
||||
|
||||
mkdir -p /run/livekit
|
||||
|
||||
cat > /run/livekit/runtime-config.yaml <<EOF
|
||||
# Copy Caddy's already-issued matrix cert/key into LiveKit's state dir.
|
||||
# The ACME CA hostname directory can vary, so glob for the domain dir.
|
||||
CRT=$(find /var/lib/caddy -path "*/$MATRIX/$MATRIX.crt" | head -n1)
|
||||
KEY=$(find /var/lib/caddy -path "*/$MATRIX/$MATRIX.key" | head -n1)
|
||||
cp "$CRT" /var/lib/livekit/turn.crt
|
||||
cp "$KEY" /var/lib/livekit/turn.key
|
||||
chmod 640 /var/lib/livekit/turn.crt /var/lib/livekit/turn.key
|
||||
|
||||
# Generate the full LiveKit config the daemon will load. turn.domain is
|
||||
# only known at runtime, so it is substituted here. The cert/key paths
|
||||
# point at the LoadCredential-staged copies under /run/credentials.
|
||||
cat > /run/livekit/livekit.yaml <<EOF
|
||||
port: 7880
|
||||
rtc:
|
||||
use_external_ip: true
|
||||
udp_port: 7882
|
||||
port_range_start: 30000
|
||||
port_range_end: 40000
|
||||
room:
|
||||
auto_create: false
|
||||
turn:
|
||||
enabled: true
|
||||
domain: $MATRIX
|
||||
cert_file: /var/lib/livekit/$MATRIX.crt
|
||||
key_file: /var/lib/livekit/$MATRIX.key
|
||||
tls_port: 5349
|
||||
udp_port: 3478
|
||||
cert_file: /run/credentials/livekit.service/turn-cert
|
||||
key_file: /run/credentials/livekit.service/turn-key
|
||||
EOF
|
||||
|
||||
chmod 640 /run/livekit/runtime-config.yaml
|
||||
chmod 640 /run/livekit/livekit.yaml
|
||||
'';
|
||||
};
|
||||
|
||||
@@ -131,6 +156,8 @@ EOF
|
||||
settings = {
|
||||
rtc.use_external_ip = true;
|
||||
rtc.udp_port = 7882;
|
||||
rtc.port_range_start = 30000;
|
||||
rtc.port_range_end = 40000;
|
||||
room.auto_create = false;
|
||||
turn = {
|
||||
enabled = true;
|
||||
@@ -140,14 +167,24 @@ EOF
|
||||
};
|
||||
};
|
||||
|
||||
# Override ExecStart to load the runtime-generated config (which carries the
|
||||
# runtime-only turn.domain), mirroring the Caddy ExecStart override pattern in
|
||||
# modules/core/caddy.nix. Deliver the TURN cert/key via LoadCredential so they
|
||||
# are readable under the upstream unit's DynamicUser=true sandbox without
|
||||
# weakening it. Everything else about the standard unit is left intact.
|
||||
systemd.services.livekit.serviceConfig.ExecStart = lib.mkForce [
|
||||
""
|
||||
"${pkgs.livekit}/bin/livekit-server --config /run/livekit/livekit.yaml --key-file /run/credentials/livekit.service/livekit-secrets"
|
||||
];
|
||||
|
||||
systemd.services.livekit.serviceConfig.LoadCredential = [
|
||||
"livekit-secrets:${livekitKeyFile}"
|
||||
"turn-cert:/var/lib/livekit/turn.crt"
|
||||
"turn-key:/var/lib/livekit/turn.key"
|
||||
];
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 5349 7881 ];
|
||||
networking.firewall.allowedUDPPorts = [ 3478 7882 ];
|
||||
networking.firewall.allowedUDPPortRanges = [
|
||||
{ from = 30000; to = 40000; }
|
||||
];
|
||||
networking.firewall.allowedTCPPortRanges = [
|
||||
{ from = 30000; to = 40000; }
|
||||
];
|
||||
|
||||
####### JWT SERVICE RUNTIME CONFIG #######
|
||||
systemd.services.lk-jwt-service-runtime-config = {
|
||||
|
||||
@@ -250,9 +250,6 @@ CREDS
|
||||
'';
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 8448 ];
|
||||
networking.firewall.allowedUDPPorts = [ 8448 ];
|
||||
|
||||
sovran_systemsOS.domainRequirements = [
|
||||
{ name = "matrix"; label = "Matrix Synapse"; example = "matrix.yourdomain.com"; }
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user