15 Commits
Author SHA1 Message Date
naturallaw777 043981a34b chore(release): prepare v1.1.3 2026-08-27 09:24:31 -05:00
naturallaw777 5529fccab2 Sparrow update 2026-08-27 09:12:03 -05:00
naturallaw777 5f84154dca nixpkgs update with bisq, bisq2, and sparrow update 2026-08-27 09:10:45 -05:00
Arena Agent 60b55f715e fix(hub): evaluate local vendored package versions directly
Fix the version metadata generation in sovran-hub.nix for Alby Hub, RTL, and Mempool. Previously, the build would incorrectly fall back to older upstream nixpkgs versions because the package names exist upstream, despite the OS deploying custom vendored forks locally. This replaces the fragile checks with direct evaluations of the local packages.

Also updates the development fallback versions.json to reflect the current vendored Alby Hub version (1.24.0).
2026-08-27 09:05:20 -05:00
naturallaw777 01037cd55c albyhub: re-harvest hashes for fixed sovran tag 2026-08-27 02:09:35 -05:00
naturallaw777 7980833fc2 albyhub: re-harvest hashes for fixed sovran tag 2026-08-27 02:08:13 -05:00
naturallaw777 837fffc8f9 albyhub: re-harvest hashes for fixed sovran tag 2026-08-27 02:05:35 -05:00
naturallaw777 cb4585ecf8 albyhub default.nix hash update 2026-08-27 01:13:48 -05:00
naturallaw777 1de1bf95ad albyhub default.nix hash update 2026-08-27 01:01:56 -05:00
naturallaw777 e9d6edca56 albyhub default.nix hash update 2026-08-27 01:00:17 -05:00
naturallaw777 a7bfc9aa0d albyhub default.nix fix 2026-08-27 00:58:17 -05:00
naturallaw777 ed2e4059c1 albyhub: build from sovran fork tag (LND-only, no frontend) 2026-08-27 00:50:35 -05:00
naturallaw777 f057e43ca0 fix(albyhub): use proxyVendor to preserve secp256k1-zkp cgo headers 2026-08-25 19:28:18 -05:00
naturallaw777 d600198049 feat(albyhub): vendor v1.24.0 LND-only, no-frontend build
Replace the nixpkgs albyhub overrideAttrs patch-chain with a fully
vendored package at packages/albyhub for v1.24.0.

- modules/core/sovran-hub.nix: bump fallback version 1.8.0 -> 1.24.0
- modules/nwc-wallets.nix: build via pkgs.callPackage ../packages/albyhub
- packages/albyhub:
  - drop 0002-isolated-invoice-app-id.patch (fixed upstream)
  - add 0004-lnd-only.patch: strip LDK/Bark/Cashu/CLN/Phoenix backends
    from service/start.go, leaving only the LND case
  - add 0005-no-frontend.patch: remove //go:embed dist and the
    frontend handler registration
  - add default.nix: buildGoModule for v1.24.0 with no nodejs/yarn/
    bark-ffi-go/ldk-node deps (only stdenv.cc.cc), subPackages cmd/http

Keeps 0001-private-route-hints and 0003-loopback-bind-host. Does not
touch flake.nix, VERSION, or CHANGELOG.
2026-08-25 19:11:25 -05:00
naturallaw777 14db4282b7 ux: simplify domain and router setup guidance 2026-08-22 09:10:05 -05:00
18 changed files with 210 additions and 333 deletions
+18
View File
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
--- ---
## [1.1.3] - 2026-08-27
### Added
- Sparrow update
- Nixpkgs update with bisq and bisq2
- Albyhub: re-harvest hashes for fixed sovran tag
- Albyhub default.nix hash update
- Albyhub default.nix fix
- Albyhub: build from sovran fork tag (LND-only, no frontend)
- Vendor v1.24.0 LND-only, no-frontend build
- Ux: simplify domain and router setup guidance
### Fixed
- Evaluate local vendored package versions directly
- Use proxyVendor to preserve secp256k1-zkp cgo headers
[1.1.3]: https://git.sovransystems.com/Sovran_Systems/Sovran_SystemsOS/releases/tag/v1.1.3
## [1.1.2] - 2026-08-21 ## [1.1.2] - 2026-08-21
### Added ### Added
+12 -12
View File
@@ -21,9 +21,9 @@ Lightning infrastructure, private cloud, and communications platform when you
are ready. are ready.
[Visit the Website](https://sovransystems.com) · [Visit the Website](https://sovransystems.com) ·
[Download the ISO](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.2.iso) · [Download the ISO](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.3.iso) ·
[Try it safely in a VM](#try-it-first-in-a-virtual-machine) · [Try it safely in a VM](#try-it-first-in-a-virtual-machine) ·
[Verify the Download](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.2.iso.sha256) · [Verify the Download](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.3.iso.sha256) ·
[Build from Source](#build-from-source) [Build from Source](#build-from-source)
<img src="assets/desktop-screenshot.webp" alt="Sovran_SystemsOS private Bitcoin desktop" width="800" /> <img src="assets/desktop-screenshot.webp" alt="Sovran_SystemsOS private Bitcoin desktop" width="800" />
@@ -345,8 +345,8 @@ with an imaging application such as [Balena Etcher](https://etcher.balena.io).
### 1. Download the ISO and checksum ### 1. Download the ISO and checksum
- [Download Sovran_SystemsOS-1.1.2.iso](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.2.iso) - [Download Sovran_SystemsOS-1.1.3.iso](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.3.iso)
- [Download Sovran_SystemsOS-1.1.2.iso.sha256](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.2.iso.sha256) - [Download Sovran_SystemsOS-1.1.3.iso.sha256](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.3.iso.sha256)
The download may take some time. Do not rename or modify the ISO before The download may take some time. Do not rename or modify the ISO before
verifying it, and keep both files in the same folder. verifying it, and keep both files in the same folder.
@@ -364,16 +364,16 @@ checksum exactly.
Open a terminal in the download folder and run: Open a terminal in the download folder and run:
```bash ```bash
sha256sum --check Sovran_SystemsOS-1.1.2.iso.sha256 sha256sum --check Sovran_SystemsOS-1.1.3.iso.sha256
``` ```
A successful comparison reports: A successful comparison reports:
```text ```text
Sovran_SystemsOS-1.1.2.iso: OK Sovran_SystemsOS-1.1.3.iso: OK
``` ```
You can also run `sha256sum Sovran_SystemsOS-1.1.2.iso` and compare the output You can also run `sha256sum Sovran_SystemsOS-1.1.3.iso` and compare the output
against the checksum file manually. against the checksum file manually.
</details> </details>
@@ -384,11 +384,11 @@ against the checksum file manually.
Open Terminal in the download folder and run: Open Terminal in the download folder and run:
```bash ```bash
shasum -a 256 Sovran_SystemsOS-1.1.2.iso shasum -a 256 Sovran_SystemsOS-1.1.3.iso
``` ```
Compare the value shown in Terminal with the value inside Compare the value shown in Terminal with the value inside
`Sovran_SystemsOS-1.1.2.iso.sha256`. `Sovran_SystemsOS-1.1.3.iso.sha256`.
</details> </details>
@@ -398,7 +398,7 @@ Compare the value shown in Terminal with the value inside
Open PowerShell in the download folder and run: Open PowerShell in the download folder and run:
```powershell ```powershell
Get-FileHash .\Sovran_SystemsOS-1.1.2.iso -Algorithm SHA256 Get-FileHash .\Sovran_SystemsOS-1.1.3.iso -Algorithm SHA256
``` ```
Compare the value under `Hash` with the published checksum. Compare the value under `Hash` with the published checksum.
@@ -413,7 +413,7 @@ match exactly.
1. Download and install [Balena Etcher](https://etcher.balena.io), then 1. Download and install [Balena Etcher](https://etcher.balena.io), then
connect the USB drive. connect the USB drive.
2. Choose **Flash from file** and select `Sovran_SystemsOS-1.1.2.iso`. 2. Choose **Flash from file** and select `Sovran_SystemsOS-1.1.3.iso`.
3. Choose **Select target**, select the USB drive, and review your selection 3. Choose **Select target**, select the USB drive, and review your selection
carefully. carefully.
4. Choose **Flash** and wait for the writing and verification process to 4. Choose **Flash** and wait for the writing and verification process to
@@ -856,7 +856,7 @@ primary location for collaboration. Please read our
## Privacy. Sovereignty. Bitcoin. ## Privacy. Sovereignty. Bitcoin.
[Visit Sovran Systems](https://sovransystems.com) · [Visit Sovran Systems](https://sovransystems.com) ·
[Download Sovran_SystemsOS](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.2.iso) · [Download Sovran_SystemsOS](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.3.iso) ·
[View the License](LICENSE) [View the License](LICENSE)
</div> </div>
+1 -1
View File
@@ -1 +1 @@
1.1.2 1.1.3
+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 -1
View File
@@ -7,7 +7,7 @@
"lnd.service": "0.18.0", "lnd.service": "0.18.0",
"rtl.service": "0.15.10", "rtl.service": "0.15.10",
"btcpayserver.service": "2.4.2", "btcpayserver.service": "2.4.2",
"albyhub.service": "1.8.0", "albyhub.service": "1.24.0",
"mempool.service": "3.2.1", "mempool.service": "3.2.1",
"matrix-synapse.service": "1.115.0", "matrix-synapse.service": "1.115.0",
"livekit.service": "1.5.2", "livekit.service": "1.5.2",
Generated
+15 -15
View File
@@ -5,11 +5,11 @@
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
}, },
"locked": { "locked": {
"lastModified": 1787246616, "lastModified": 1787839684,
"narHash": "sha256-TTbXIBwoaPbzk2o+rGEayqOvrXqKo9JdYJAMDwqjCQ4=", "narHash": "sha256-xTafqsVs2/zyGsaCel41QT5Lpbs/rG56Kp+YsmLXL8I=",
"owner": "emmanuelrosa", "owner": "emmanuelrosa",
"repo": "btc-clients-nix", "repo": "btc-clients-nix",
"rev": "8b10c40cb13bac5d100ae1d1fb42eccc0d9c3223", "rev": "ef81159876930802ef9861aa5c039cec6886a30f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -56,11 +56,11 @@
}, },
"nixpkgs-stable": { "nixpkgs-stable": {
"locked": { "locked": {
"lastModified": 1787101114, "lastModified": 1787753485,
"narHash": "sha256-gwrPcFf/rDjHPaVflbDZ040ZDmBTRj/7+s8ZmE2SaIM=", "narHash": "sha256-BZWCi9ZRJiARTuKTbbtvFTj7t1TK4G3UEckT3HyNfRg=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b18a4b905f8d028dc4476412e6d6891728695379", "rev": "062346a6d85bc4b49dfaa61c986e9c5be21217d1",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -72,11 +72,11 @@
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1787135253, "lastModified": 1787498568,
"narHash": "sha256-RD2kNWCG+Bjo6h+JVjWVNntZs2GtRoeY2xHjts/FNkA=", "narHash": "sha256-9i/VTdusq/+NM/tz+J1Re+ojkMB8MBf0QshnYfzHz30=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "ffb3c9b700e759be2ef13237c9d8f953b32a1e46", "rev": "56c02bc00adcf003215cc4bd996d6efaf4cff188",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -88,11 +88,11 @@
}, },
"nixpkgs_3": { "nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1787111413, "lastModified": 1787364730,
"narHash": "sha256-sFosWtq21eHGJRnTc/hvf4M1obRgLEUMNm/IzllkHMA=", "narHash": "sha256-NcYt9QJfpJiF1lAyN8BDPB4EeScbPU+EwQqPiBElrpU=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "afe3d8ac4395617bdcdac9f188ac8717a062e014", "rev": "a831408e6378bc02ebf8cc09b52c96ca86f6bab4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -109,11 +109,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1787151631, "lastModified": 1787691219,
"narHash": "sha256-EblMdrDFBFNNlUPm5zUQdh0j6gDx+OFQPv7LFE4B5AA=", "narHash": "sha256-CjeJTOjrluiDuL0W/YBFkSBOQjBYmdKwj7hWAJ4PIh0=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nixvim", "repo": "nixvim",
"rev": "d0d62a2b5027da689b4e8d5ee43f1cf83f2e975d", "rev": "ebec1ae277a50b16e7d6682edd009ef585d68261",
"type": "github" "type": "github"
}, },
"original": { "original": {
+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.",
+5 -9
View File
@@ -135,16 +135,12 @@ let
"bitcoind.service" = if pkgs ? bitcoind then pkgs.bitcoind.version else "27.1.0"; "bitcoind.service" = if pkgs ? bitcoind then pkgs.bitcoind.version else "27.1.0";
"electrs.service" = if pkgs ? electrs then pkgs.electrs.version else "0.10.6"; "electrs.service" = if pkgs ? electrs then pkgs.electrs.version else "0.10.6";
"lnd.service" = if pkgs ? lnd then pkgs.lnd.version else "0.18.0"; "lnd.service" = if pkgs ? lnd then pkgs.lnd.version else "0.18.0";
# Keep the fallbacks aligned with the vendored packages used by the # Evaluate locally vendored packages directly so we don't accidentally
# service modules (RTL 0.15.10 and Mempool 3.2.1). The nixpkgs attrs are # pick up older/different versions from upstream nixpkgs.
# optional because these packages are built locally in this repository. "rtl.service" = (pkgs.callPackage ../../packages/rtl { fetchNodeModules = pkgs.callPackage ../../packages/build-support/fetch-node-modules.nix {}; }).version;
"rtl.service" = if pkgs ? clightning-rtl then pkgs.clightning-rtl.version else (if pkgs ? rtl then pkgs.rtl.version else "0.15.10");
# BTCPay Server is intentionally sourced from pkgs.stable by the service
# module. Read the configured package here rather than pkgs.btcpayserver
# (unstable), otherwise the Hub can advertise a version that is not running.
"btcpayserver.service" = lib.getVersion config.services.btcpayserver.package; "btcpayserver.service" = lib.getVersion config.services.btcpayserver.package;
"albyhub.service" = if pkgs ? albyhub then pkgs.albyhub.version else "1.8.0"; "albyhub.service" = (pkgs.callPackage ../../packages/albyhub {}).version;
"mempool.service" = if pkgs ? mempool then pkgs.mempool.version else "3.2.1"; "mempool.service" = (pkgs.callPackage ../../packages/mempool { fetchNodeModules = pkgs.callPackage ../../packages/build-support/fetch-node-modules.nix {}; }).version;
"matrix-synapse.service" = if pkgs ? matrix-synapse then pkgs.matrix-synapse.version else "1.115.0"; "matrix-synapse.service" = if pkgs ? matrix-synapse then pkgs.matrix-synapse.version else "1.115.0";
"livekit.service" = if pkgs ? livekit then pkgs.livekit.version else "1.5.2"; "livekit.service" = if pkgs ? livekit then pkgs.livekit.version else "1.5.2";
"vaultwarden.service" = if pkgs ? vaultwarden then pkgs.vaultwarden.version else "1.32.0"; "vaultwarden.service" = if pkgs ? vaultwarden then pkgs.vaultwarden.version else "1.32.0";
+2 -7
View File
@@ -3,13 +3,8 @@
let let
albyHubPort = 18080; albyHubPort = 18080;
albyHubApiBase = "http://127.0.0.1:${toString albyHubPort}"; albyHubApiBase = "http://127.0.0.1:${toString albyHubPort}";
patchedAlbyHub = pkgs.albyhub.overrideAttrs (old: { vendoredAlbyHub = pkgs.callPackage ../packages/albyhub {};
patches = (old.patches or []) ++ [ patchedAlbyHub = vendoredAlbyHub;
../packages/albyhub/0001-private-route-hints.patch
../packages/albyhub/0002-isolated-invoice-app-id.patch
../packages/albyhub/0003-loopback-bind-host.patch
];
});
lndRpcAddress = lib.attrByPath [ "services" "lnd" "rpcAddress" ] "127.0.0.1" config; lndRpcAddress = lib.attrByPath [ "services" "lnd" "rpcAddress" ] "127.0.0.1" config;
lndRpcPort = toString (lib.attrByPath [ "services" "lnd" "rpcPort" ] 10009 config); lndRpcPort = toString (lib.attrByPath [ "services" "lnd" "rpcPort" ] 10009 config);
@@ -1,13 +0,0 @@
diff --git a/lnclient/lnd/lnd.go b/lnclient/lnd/lnd.go
index bad532f..ba35b7b 100644
--- a/lnclient/lnd/lnd.go
+++ b/lnclient/lnd/lnd.go
@@ -708,7 +708,7 @@ func (svc *LNDService) MakeInvoice(ctx context.Context, amountMsat int64, descri
DescriptionHash: descriptionHashBytes,
Expiry: expiry,
RouteHints: hints,
- Private: !hasPublicChannels, // use private channel hints in the invoice
+ Private: true, // always include private channel hints in the invoice
}
resp, err := svc.client.AddInvoice(ctx, addInvoiceRequest)
@@ -1,71 +0,0 @@
diff --git a/api/models.go b/api/models.go
index 28bd117..dceb79a 100644
--- a/api/models.go
+++ b/api/models.go
@@ -46,7 +46,7 @@ type API interface {
ListTransactions(ctx context.Context, appId *uint, limit uint64, offset uint64) (*ListTransactionsResponse, error)
ListOnchainTransactions(ctx context.Context) ([]OnchainTransaction, error)
SendPayment(ctx context.Context, invoice string, amountMsat *uint64, metadata map[string]interface{}, fromAppId *uint) (*SendPaymentResponse, error)
- CreateInvoice(ctx context.Context, amountMsat uint64, description string) (*MakeInvoiceResponse, error)
+ CreateInvoice(ctx context.Context, amountMsat uint64, description string, appId *uint) (*MakeInvoiceResponse, error)
LookupInvoice(ctx context.Context, paymentHash string) (*LookupInvoiceResponse, error)
SetTransactionUserLabels(ctx context.Context, id uint, labels map[string]string) error
RequestMempoolApi(ctx context.Context, endpoint string) (interface{}, error)
@@ -586,6 +586,7 @@ type MakeInvoiceRequest struct {
AmountSat *uint64 `json:"amountSat"`
AmountMsat *uint64 `json:"amountMsat"`
Description string `json:"description"`
+ AppId *uint `json:"appId"`
}
type ResetRouterRequest struct {
diff --git a/api/transactions.go b/api/transactions.go
index aaf9380..3f9850a 100644
--- a/api/transactions.go
+++ b/api/transactions.go
@@ -12,12 +12,12 @@ import (
"github.com/sirupsen/logrus"
)
-func (api *api) CreateInvoice(ctx context.Context, amountMsat uint64, description string) (*MakeInvoiceResponse, error) {
+func (api *api) CreateInvoice(ctx context.Context, amountMsat uint64, description string, appId *uint) (*MakeInvoiceResponse, error) {
lnClient := api.svc.GetLNClient()
if lnClient == nil {
return nil, ErrLNClientNotStarted
}
- transaction, err := api.svc.GetTransactionsService().MakeInvoice(ctx, amountMsat, description, "", 0, nil, lnClient, nil, nil, nil)
+ transaction, err := api.svc.GetTransactionsService().MakeInvoice(ctx, amountMsat, description, "", 0, nil, lnClient, appId, nil, nil)
if err != nil {
return nil, err
}
diff --git a/http/http_service.go b/http/http_service.go
index bf650b9..cf61250 100644
--- a/http/http_service.go
+++ b/http/http_service.go
@@ -687,7 +687,12 @@ func (httpSvc *HttpService) makeInvoiceHandler(c echo.Context) error {
amountMsat = *resolvedAmountMsat
}
- invoice, err := httpSvc.api.CreateInvoice(c.Request().Context(), amountMsat, makeInvoiceRequest.Description)
+ invoice, err := httpSvc.api.CreateInvoice(
+ c.Request().Context(),
+ amountMsat,
+ makeInvoiceRequest.Description,
+ makeInvoiceRequest.AppId,
+ )
if err != nil {
return c.JSON(http.StatusInternalServerError, ErrorResponse{
diff --git a/wails/wails_handlers.go b/wails/wails_handlers.go
index d25259b..c9fe4e8 100644
--- a/wails/wails_handlers.go
+++ b/wails/wails_handlers.go
@@ -600,7 +600,7 @@ func (app *WailsApp) WailsRequestRouter(route string, method string, body string
if resolvedAmountMsat != nil {
amountMsat = *resolvedAmountMsat
}
- invoice, err := app.api.CreateInvoice(ctx, amountMsat, makeInvoiceRequest.Description)
+ invoice, err := app.api.CreateInvoice(ctx, amountMsat, makeInvoiceRequest.Description, nil)
if err != nil {
return WailsRequestRouterResponse{Body: nil, Error: err.Error()}
}
@@ -1,37 +0,0 @@
diff --git a/cmd/http/main.go b/cmd/http/main.go
index ed31f96..4d31212 100644
--- a/cmd/http/main.go
+++ b/cmd/http/main.go
@@ -2,8 +2,8 @@ package main
import (
"context"
- "fmt"
nethttp "net/http"
+ "net"
"os"
"os/signal"
"syscall"
@@ -51,8 +51,9 @@ func main() {
httpSvc := http.NewHttpService(svc, svc.GetEventPublisher())
httpSvc.RegisterSharedRoutes(e)
//start Echo server
+ bindAddress := net.JoinHostPort(svc.GetConfig().GetEnv().Host, svc.GetConfig().GetEnv().Port)
go func() {
- if err := e.Start(fmt.Sprintf(":%v", svc.GetConfig().GetEnv().Port)); err != nil && err != nethttp.ErrServerClosed {
+ if err := e.Start(bindAddress); err != nil && err != nethttp.ErrServerClosed {
logger.Logger.WithError(err).Error("echo server failed to start")
cancel()
}
diff --git a/config/models.go b/config/models.go
index 0fb8870..dc11afd 100644
--- a/config/models.go
+++ b/config/models.go
@@ -24,6 +24,7 @@ type AppConfig struct {
LNDAddress string `envconfig:"LND_ADDRESS"`
LNDCertFile string `envconfig:"LND_CERT_FILE"`
LNDMacaroonFile string `envconfig:"LND_MACAROON_FILE"`
+ Host string `envconfig:"HOST" default:"127.0.0.1"`
Workdir string `envconfig:"WORK_DIR"`
Port string `envconfig:"PORT" default:"8080"`
DatabaseUri string `envconfig:"DATABASE_URI" default:"nwc.db"`
+78
View File
@@ -0,0 +1,78 @@
{ lib
, buildGoModule
, fetchFromGitHub
, stdenv
}:
buildGoModule rec {
pname = "albyhub";
version = "1.24.0";
# sovran fork = upstream v1.24.0 + LND-only, no frontend, HOST bind,
# always-private route hints (the customization lives in the fork's commits)
src = fetchFromGitHub {
owner = "naturallaw777";
repo = "hub";
tag = "sovran-1.24.0";
# round 1: copy the "got: sha256-..." from the build error
hash = "sha256-9l7xK7iA1p9qQRClgNV+eYg/vYJxbwBiRObeLRpyfDo=";
};
# `go mod vendor` strips the secp256k1-zkp cgo headers (include/), so use
# the full module cache. Round 2: copy the "got: sha256-..." from the error
proxyVendor = true;
vendorHash = "sha256-U0NLVV0gH2IERqaGsJwE2f+iZQ35D+czH8mUKXSM4Wc=";
subPackages = [ "cmd/http" ];
# LND client needs cgo (secp256k1-zkp)
buildInputs = [ (lib.getLib stdenv.cc.cc) ];
# pin module downloads to a hermetic proxy and the local toolchain
# (otherwise GOPROXY/GOTOOLCHAIN can leak into the builder env and the
# cache comes back incomplete — the original failure)
overrideModAttrs = (finalAttrs: previousAttrs: {
modBuildPhase = ''
runHook preBuild
export GIT_SSL_CAINFO=$NIX_SSL_CERT_FILE
export GOPROXY=https://proxy.golang.org,direct
export GOSUMDB=sum.golang.org
export GOTOOLCHAIN=local
mkdir -p "$GOPATH/pkg/mod/cache/download"
go mod download all
export GOPROXY="file://$GOPATH/pkg/mod/cache/download"
go list ./cmd/http
mkdir -p vendor
runHook postBuild
'';
});
ldflags = [
"-X github.com/getAlby/hub/version.Tag=${version}"
"-s"
"-w"
];
postInstall = ''
mv $out/bin/http $out/bin/albyhub
'';
preFixup = ''
# Go's external linker bakes the sandbox build dir into the RPATH;
# rewrite it to the real store paths before the fixup audit
patchelf --set-rpath ${
lib.makeLibraryPath [
(lib.getLib stdenv.cc.cc)
(lib.getLib stdenv.cc.libc)
]
} $out/bin/albyhub
'';
meta.mainProgram = "albyhub";
}