Compare commits
8
Commits
220d6dff2c
..
stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d46d036c0 | ||
|
|
9ae4e34fe0 | ||
|
|
d1e226a687 | ||
|
|
49e41eeea9 | ||
|
|
88be5b99dd | ||
|
|
93492e88fb | ||
|
|
7da37cdb34 | ||
|
|
f34d1533c3 |
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [1.1.4] - 2026-09-02
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Element-calling: fix Nix string interpolation of LAN_CIDR echo
|
||||||
|
- Docs/hub: update Element Calling port guidance to the new port set
|
||||||
|
- Element-calling: fix Wi-Fi calls and tighten media/TURN ports
|
||||||
|
- Flake: pin LiveKit to 1.13.6 for rtc.advertise_internal_ip support
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated nixpkgs
|
||||||
|
- Updated sovran-bitcon
|
||||||
|
- Dedupe NWC tooling — use Sovran_Bitcoin's sovran-nwc
|
||||||
|
- Update sovran-bitcoin flake input
|
||||||
|
- Extract bitcoin stack into Sovran_Bitcoin flake input
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Set lnurl domainFile for Hub-managed Lightning Address domain
|
||||||
|
- Correct disablewallet casing
|
||||||
|
- Fixed typo
|
||||||
|
[1.1.4]: https://git.sovransystems.com/Sovran_Systems/Sovran_SystemsOS/releases/tag/v1.1.4
|
||||||
|
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
@@ -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.3.iso) ·
|
[Download the ISO](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.4.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.3.iso.sha256) ·
|
[Verify the Download](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.4.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.3.iso](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.3.iso)
|
- [Download Sovran_SystemsOS-1.1.4.iso](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.4.iso)
|
||||||
- [Download Sovran_SystemsOS-1.1.3.iso.sha256](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.3.iso.sha256)
|
- [Download Sovran_SystemsOS-1.1.4.iso.sha256](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.4.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.3.iso.sha256
|
sha256sum --check Sovran_SystemsOS-1.1.4.iso.sha256
|
||||||
```
|
```
|
||||||
|
|
||||||
A successful comparison reports:
|
A successful comparison reports:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Sovran_SystemsOS-1.1.3.iso: OK
|
Sovran_SystemsOS-1.1.4.iso: OK
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also run `sha256sum Sovran_SystemsOS-1.1.3.iso` and compare the output
|
You can also run `sha256sum Sovran_SystemsOS-1.1.4.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.3.iso
|
shasum -a 256 Sovran_SystemsOS-1.1.4.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.3.iso.sha256`.
|
`Sovran_SystemsOS-1.1.4.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.3.iso -Algorithm SHA256
|
Get-FileHash .\Sovran_SystemsOS-1.1.4.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.3.iso`.
|
2. Choose **Flash from file** and select `Sovran_SystemsOS-1.1.4.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.3.iso) ·
|
[Download Sovran_SystemsOS](https://downloads.sovransystems.com/Sovran_SystemsOS-1.1.4.iso) ·
|
||||||
[View the License](LICENSE)
|
[View the License](LICENSE)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -908,11 +908,68 @@ def _get_remote_rev(branch=None):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_version(text):
|
||||||
|
"""Return a (major, minor, patch) tuple from a VERSION string, or None."""
|
||||||
|
try:
|
||||||
|
match = re.search(r"(\d+)\.(\d+)\.(\d+)", str(text))
|
||||||
|
if match:
|
||||||
|
return tuple(int(g) for g in match.groups())
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_remote_version(branch=None):
|
||||||
|
"""Read VERSION on the tracked branch, e.g. the stable release version."""
|
||||||
|
try:
|
||||||
|
ref = branch or "stable"
|
||||||
|
url = (
|
||||||
|
"https://git.sovransystems.com/api/v1/repos/"
|
||||||
|
"Sovran_Systems/Sovran_SystemsOS/raw/VERSION?ref="
|
||||||
|
+ urllib.parse.quote(ref)
|
||||||
|
)
|
||||||
|
req = urllib.request.Request(url, method="GET")
|
||||||
|
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||||
|
return _parse_version(resp.read().decode())
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def check_for_updates() -> bool | None:
|
def check_for_updates() -> bool | None:
|
||||||
|
"""Whether an update is available.
|
||||||
|
|
||||||
|
Primary signal: the flake lock's pinned Sovran_Systems rev differs from
|
||||||
|
the remote branch head. BUT a failed update rewrites ``flake.lock`` (the
|
||||||
|
``nix flake update`` step) without staging a generation (the
|
||||||
|
``nixos-rebuild boot`` step failed), so after a failure the lock and the
|
||||||
|
remote agree while the *running* system is still on the old version. In
|
||||||
|
that case the rev comparison alone reports a false "up to date" and hides
|
||||||
|
the failed update from the dashboard.
|
||||||
|
|
||||||
|
Backstop: compare the running Hub version against the branch VERSION.
|
||||||
|
A newer released version while running an older one means the update did
|
||||||
|
not apply (build failed, reboot skipped, generation rolled back) and must
|
||||||
|
be offered again.
|
||||||
|
"""
|
||||||
locked_rev, branch = _get_locked_info()
|
locked_rev, branch = _get_locked_info()
|
||||||
remote_rev = _get_remote_rev(branch)
|
remote_rev = _get_remote_rev(branch)
|
||||||
if locked_rev and remote_rev:
|
if locked_rev and remote_rev:
|
||||||
return locked_rev != remote_rev
|
rev_differs = locked_rev != remote_rev
|
||||||
|
if rev_differs:
|
||||||
|
return True
|
||||||
|
# Revs match — make sure the pinned (failed) rev isn't masking an
|
||||||
|
# older *running* system.
|
||||||
|
running_ver = _parse_version(_get_sovran_version())
|
||||||
|
remote_ver = _get_remote_version(branch)
|
||||||
|
if running_ver and remote_ver and remote_ver > running_ver:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
# Couldn't compare revs — fall back to the version backstop.
|
||||||
|
running_ver = _parse_version(_get_sovran_version())
|
||||||
|
remote_ver = _get_remote_version(branch)
|
||||||
|
if running_ver and remote_ver:
|
||||||
|
return remote_ver > running_ver
|
||||||
return None # inconclusive — couldn't read lock or reach remote
|
return None # inconclusive — couldn't read lock or reach remote
|
||||||
|
|
||||||
|
|
||||||
@@ -3885,6 +3942,11 @@ async def api_updates_check():
|
|||||||
# Avoid a slow remote update check when there is already an operation
|
# Avoid a slow remote update check when there is already an operation
|
||||||
# the dashboard needs to surface.
|
# the dashboard needs to surface.
|
||||||
return {"available": True, "status": status.lower()}
|
return {"available": True, "status": status.lower()}
|
||||||
|
if status == "FAILED":
|
||||||
|
# The last update did not complete (build failed). Keep offering the
|
||||||
|
# update so the user can re-run it rather than silently landing on a
|
||||||
|
# false "up to date".
|
||||||
|
return {"available": True, "status": "failed"}
|
||||||
|
|
||||||
available = await loop.run_in_executor(None, check_for_updates)
|
available = await loop.run_in_executor(None, check_for_updates)
|
||||||
# None means inconclusive (check failed) — report as available so the UI doesn't block
|
# None means inconclusive (check failed) — report as available so the UI doesn't block
|
||||||
@@ -3962,8 +4024,15 @@ async def api_updates_run():
|
|||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Re-read status: a prior failed update leaves flake.lock advanced even
|
||||||
|
# though no generation was staged, so the rev-based check below can say
|
||||||
|
# "no updates" even though the system is still old. A failed update must
|
||||||
|
# always be re-runnable to recover.
|
||||||
|
persisted_status = await loop.run_in_executor(None, _read_update_status)
|
||||||
|
last_failed = persisted_status == "FAILED"
|
||||||
|
|
||||||
available = await loop.run_in_executor(None, check_for_updates)
|
available = await loop.run_in_executor(None, check_for_updates)
|
||||||
if available is False: # only block when positively confirmed no updates
|
if available is False and not last_failed: # only block when positively confirmed no updates
|
||||||
# Clear stale status/log so they don't contaminate future modal opens.
|
# Clear stale status/log so they don't contaminate future modal opens.
|
||||||
_write_update_status("IDLE")
|
_write_update_status("IDLE")
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ if ($btnCloseModal) $btnCloseModal.addEventListener("click", closeUpdateModal);
|
|||||||
if ($btnReboot) $btnReboot.addEventListener("click", doReboot);
|
if ($btnReboot) $btnReboot.addEventListener("click", doReboot);
|
||||||
if ($btnSave) $btnSave.addEventListener("click", saveErrorReport);
|
if ($btnSave) $btnSave.addEventListener("click", saveErrorReport);
|
||||||
if ($btnRetryUpdate) $btnRetryUpdate.addEventListener("click", retryUpdateStatus);
|
if ($btnRetryUpdate) $btnRetryUpdate.addEventListener("click", retryUpdateStatus);
|
||||||
|
if ($btnRetryRun) $btnRetryRun.addEventListener("click", retryUpdateRun);
|
||||||
|
|
||||||
// Browser timers and requests may be suspended while an RDP session/tab is in
|
// Browser timers and requests may be suspended while an RDP session/tab is in
|
||||||
// the background. Reconcile immediately when the user returns instead of
|
// the background. Reconcile immediately when the user returns instead of
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ function renderPortForwardGuideHtml(ports, opts) {
|
|||||||
var noteClass = opts.noteClass || "port-req-hint";
|
var noteClass = opts.noteClass || "port-req-hint";
|
||||||
var ipHtml = opts.internalIp
|
var ipHtml = opts.internalIp
|
||||||
? '<code class="port-req-internal-ip">' + escHtml(opts.internalIp) + '</code>'
|
? '<code class="port-req-internal-ip">' + escHtml(opts.internalIp) + '</code>'
|
||||||
: 'this computer’s <strong>internal IP</strong> (shown as “Internal IP” at the top of the Hub dashboard)';
|
: 'this computer’s <strong>internal IP</strong>';
|
||||||
|
|
||||||
var rows = (ports || []).map(function(p) {
|
var rows = (ports || []).map(function(p) {
|
||||||
return '<tr>' +
|
return '<tr>' +
|
||||||
@@ -78,26 +78,17 @@ function renderPortForwardGuideHtml(ports, opts) {
|
|||||||
}).join("");
|
}).join("");
|
||||||
|
|
||||||
var forWhat = opts.serviceName
|
var forWhat = opts.serviceName
|
||||||
? 'For <strong>' + escHtml(opts.serviceName) + '</strong> to be reachable from outside your home network, open'
|
? 'To make <strong>' + escHtml(opts.serviceName) + '</strong> reachable from outside your home, forward these ports to ' + ipHtml + ':'
|
||||||
: 'Open';
|
: 'Forward these ports to ' + ipHtml + ':';
|
||||||
|
|
||||||
return '<p class="' + introClass + '">' +
|
return '<p class="' + introClass + '">' + forWhat + '</p>' +
|
||||||
forWhat + ' the ports below in your router’s <strong>port forwarding</strong> settings ' +
|
'<p class="port-req-steps" style="margin-top:6px;margin-bottom:10px;font-size:0.92em;color:#555;">' +
|
||||||
'and point them at ' + ipHtml + '.' +
|
'Set the internal and external port to the <strong>same number</strong>. Match <strong>TCP</strong> or <strong>UDP</strong> exactly. For ranges like <strong>40000-40099</strong>, use your router’s range fields (start 40000, end 40099).' +
|
||||||
'</p>' +
|
'</p>' +
|
||||||
'<ul class="port-req-steps">' +
|
|
||||||
'<li>Set the <strong>internal (private) port</strong> and the <strong>external (public) port</strong> to the <strong>same number</strong>.</li>' +
|
|
||||||
'<li>Match the <strong>protocol</strong> exactly — a rule set to TCP will not pass UDP traffic. Where the table says <strong>TCP + UDP</strong>, create both rules (or pick “Both”/“TCP/UDP” if your router offers it).</li>' +
|
|
||||||
'<li>For a range such as <strong>40000-40099</strong>, use your router’s port-range fields — start 40000, end 40099 — rather than one rule per port.</li>' +
|
|
||||||
'</ul>' +
|
|
||||||
'<table class="' + tableClass + '">' +
|
'<table class="' + tableClass + '">' +
|
||||||
'<thead><tr><th>Port(s)</th><th>Protocol</th><th>Used for</th></tr></thead>' +
|
'<thead><tr><th>Port(s)</th><th>Protocol</th><th>Used for</th></tr></thead>' +
|
||||||
'<tbody>' + rows + '</tbody>' +
|
'<tbody>' + rows + '</tbody>' +
|
||||||
'</table>' +
|
'</table>';
|
||||||
'<p class="' + noteClass + '">' +
|
|
||||||
'📱 <strong>How to confirm it worked:</strong> forwarding happens on your router, so it can only be verified from outside your network. ' +
|
|
||||||
'Turn Wi-Fi off on your phone and open the service over mobile data — if it loads, your ports are open.' +
|
|
||||||
'</p>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDuration(seconds) {
|
function formatDuration(seconds) {
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ const $modalLog = document.getElementById("modal-log");
|
|||||||
const $btnReboot = document.getElementById("btn-reboot");
|
const $btnReboot = document.getElementById("btn-reboot");
|
||||||
const $btnSave = document.getElementById("btn-save-report");
|
const $btnSave = document.getElementById("btn-save-report");
|
||||||
const $btnRetryUpdate = document.getElementById("btn-retry-update-status");
|
const $btnRetryUpdate = document.getElementById("btn-retry-update-status");
|
||||||
|
const $btnRetryRun = document.getElementById("btn-retry-update");
|
||||||
const $btnCloseModal = document.getElementById("btn-close-modal");
|
const $btnCloseModal = document.getElementById("btn-close-modal");
|
||||||
|
|
||||||
const $rebootOverlay = document.getElementById("reboot-overlay");
|
const $rebootOverlay = document.getElementById("reboot-overlay");
|
||||||
|
|||||||
@@ -275,7 +275,13 @@ async function checkUpdates() {
|
|||||||
var sidebarUpdateBtn = document.getElementById("sidebar-btn-update");
|
var sidebarUpdateBtn = document.getElementById("sidebar-btn-update");
|
||||||
var sidebarUpdateHint = document.getElementById("sidebar-update-hint");
|
var sidebarUpdateHint = document.getElementById("sidebar-update-hint");
|
||||||
if (sidebarUpdateBtn) {
|
if (sidebarUpdateBtn) {
|
||||||
if (updateStatus === "reboot_required") {
|
if (updateStatus === "failed") {
|
||||||
|
// Last update errored and did not apply — surface it as a persistent
|
||||||
|
// red banner that re-opens the failed run with a "Retry Update" action.
|
||||||
|
sidebarUpdateBtn.style.borderColor = "#e01b24";
|
||||||
|
sidebarUpdateBtn.style.backgroundColor = "rgba(224, 27, 36, 0.10)";
|
||||||
|
if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Update failed — click to retry";
|
||||||
|
} else if (updateStatus === "reboot_required") {
|
||||||
sidebarUpdateBtn.style.borderColor = "#e5a50a";
|
sidebarUpdateBtn.style.borderColor = "#e5a50a";
|
||||||
sidebarUpdateBtn.style.backgroundColor = "rgba(229, 165, 10, 0.10)";
|
sidebarUpdateBtn.style.backgroundColor = "rgba(229, 165, 10, 0.10)";
|
||||||
if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Restart required";
|
if (sidebarUpdateHint) sidebarUpdateHint.textContent = "Restart required";
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ async function openUpdateModal() {
|
|||||||
{ cache: "no-store" },
|
{ cache: "no-store" },
|
||||||
STATUS_POLL_FETCH_TIMEOUT
|
STATUS_POLL_FETCH_TIMEOUT
|
||||||
);
|
);
|
||||||
if (current.running || current.result === "reboot_required") {
|
if (current.running || current.result === "reboot_required" || current.result === "failed") {
|
||||||
|
// An in-progress update, a staged update awaiting reboot, or a prior
|
||||||
|
// failed update — reattach to the persisted systemd/log state instead of
|
||||||
|
// starting over or wrongly reporting "up to date".
|
||||||
showExistingUpdate(current);
|
showExistingUpdate(current);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -41,6 +44,7 @@ async function openUpdateModal() {
|
|||||||
if ($btnReboot) $btnReboot.style.display = "none";
|
if ($btnReboot) $btnReboot.style.display = "none";
|
||||||
if ($btnSave) $btnSave.style.display = "none";
|
if ($btnSave) $btnSave.style.display = "none";
|
||||||
if ($btnRetryUpdate) $btnRetryUpdate.style.display = "none";
|
if ($btnRetryUpdate) $btnRetryUpdate.style.display = "none";
|
||||||
|
if ($btnRetryRun) $btnRetryRun.style.display = "none";
|
||||||
if ($btnCloseModal) $btnCloseModal.disabled = false;
|
if ($btnCloseModal) $btnCloseModal.disabled = false;
|
||||||
$modal.classList.add("open");
|
$modal.classList.add("open");
|
||||||
return;
|
return;
|
||||||
@@ -69,6 +73,7 @@ function prepareUpdateModal() {
|
|||||||
if ($btnReboot) $btnReboot.style.display = "none";
|
if ($btnReboot) $btnReboot.style.display = "none";
|
||||||
if ($btnSave) $btnSave.style.display = "none";
|
if ($btnSave) $btnSave.style.display = "none";
|
||||||
if ($btnRetryUpdate) $btnRetryUpdate.style.display = "none";
|
if ($btnRetryUpdate) $btnRetryUpdate.style.display = "none";
|
||||||
|
if ($btnRetryRun) $btnRetryRun.style.display = "none";
|
||||||
if ($btnCloseModal) $btnCloseModal.disabled = true;
|
if ($btnCloseModal) $btnCloseModal.disabled = true;
|
||||||
$modal.classList.add("open");
|
$modal.classList.add("open");
|
||||||
}
|
}
|
||||||
@@ -154,6 +159,7 @@ function startUpdate() {
|
|||||||
if ($btnReboot) $btnReboot.style.display = "none";
|
if ($btnReboot) $btnReboot.style.display = "none";
|
||||||
if ($btnSave) $btnSave.style.display = "none";
|
if ($btnSave) $btnSave.style.display = "none";
|
||||||
if ($btnRetryUpdate) $btnRetryUpdate.style.display = "none";
|
if ($btnRetryUpdate) $btnRetryUpdate.style.display = "none";
|
||||||
|
if ($btnRetryRun) $btnRetryRun.style.display = "none";
|
||||||
if ($btnCloseModal) $btnCloseModal.disabled = false;
|
if ($btnCloseModal) $btnCloseModal.disabled = false;
|
||||||
_updateFinished = true;
|
_updateFinished = true;
|
||||||
return;
|
return;
|
||||||
@@ -283,6 +289,16 @@ function retryUpdateStatus() {
|
|||||||
startUpdatePoll();
|
startUpdatePoll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-run a failed (or never-applied) update from scratch. The backend always
|
||||||
|
// allows this after a FAILED attempt even though flake.lock may already be
|
||||||
|
// advanced (the previous build never staged a bootable generation).
|
||||||
|
function retryUpdateRun() {
|
||||||
|
if ($btnRetryRun) $btnRetryRun.style.display = "none";
|
||||||
|
if ($btnSave) $btnSave.style.display = "none";
|
||||||
|
if ($btnReboot) $btnReboot.style.display = "none";
|
||||||
|
_doOpenUpdateModal();
|
||||||
|
}
|
||||||
|
|
||||||
function resumeUpdateStatusAfterInterruption() {
|
function resumeUpdateStatusAfterInterruption() {
|
||||||
if (!$modal || !$modal.classList.contains("open")) return;
|
if (!$modal || !$modal.classList.contains("open")) return;
|
||||||
if (_updateStatusUnavailable) {
|
if (_updateStatusUnavailable) {
|
||||||
@@ -304,9 +320,10 @@ function onUpdateDone(result) {
|
|||||||
if ($modalStatus) $modalStatus.textContent = "✓ Update complete — restart required";
|
if ($modalStatus) $modalStatus.textContent = "✓ Update complete — restart required";
|
||||||
if ($btnReboot) $btnReboot.style.display = "inline-flex";
|
if ($btnReboot) $btnReboot.style.display = "inline-flex";
|
||||||
} else {
|
} else {
|
||||||
if ($modalStatus) $modalStatus.textContent = "✗ Update failed";
|
if ($modalStatus) $modalStatus.textContent = "✗ Update failed — your system was not changed. Run the update again or save the error report for support.";
|
||||||
|
if ($btnRetryRun) $btnRetryRun.style.display = "inline-flex";
|
||||||
if ($btnSave) $btnSave.style.display = "inline-flex";
|
if ($btnSave) $btnSave.style.display = "inline-flex";
|
||||||
if ($btnReboot) $btnReboot.style.display = "inline-flex";
|
if ($btnReboot) $btnReboot.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@
|
|||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class="btn btn-save" id="btn-save-report" style="display:none">Save Error Report</button>
|
<button class="btn btn-save" id="btn-save-report" style="display:none">Save Error Report</button>
|
||||||
<button class="btn btn-save" id="btn-retry-update-status" style="display:none">Retry Status</button>
|
<button class="btn btn-save" id="btn-retry-update-status" style="display:none">Retry Status</button>
|
||||||
|
<button class="btn btn-reboot" id="btn-retry-update" style="display:none">Retry Update</button>
|
||||||
<button class="btn btn-reboot" id="btn-reboot" style="display:none">Restart Entire System</button>
|
<button class="btn btn-reboot" id="btn-reboot" style="display:none">Restart Entire System</button>
|
||||||
<button class="btn btn-close-modal" id="btn-close-modal" disabled>Close</button>
|
<button class="btn btn-close-modal" id="btn-close-modal" disabled>Close</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -147,7 +147,7 @@
|
|||||||
hunspell hunspellDicts.en_US
|
hunspell hunspellDicts.en_US
|
||||||
synadm brave-origin dua
|
synadm brave-origin dua
|
||||||
gparted pv unzip parted screen zenity
|
gparted pv unzip parted screen zenity
|
||||||
libargon2 gnome-terminal libreoffice-fresh
|
libargon2 gnome-terminal libreoffice-stable
|
||||||
dig firefox wp-cli axel
|
dig firefox wp-cli axel
|
||||||
lk-jwt-service livekit-libwebrtc livekit
|
lk-jwt-service livekit-libwebrtc livekit
|
||||||
matrix-synapse age onlyoffice-desktopeditors
|
matrix-synapse age onlyoffice-desktopeditors
|
||||||
|
|||||||
Generated
+30
-30
@@ -5,11 +5,11 @@
|
|||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787839684,
|
"lastModified": 1788527183,
|
||||||
"narHash": "sha256-xTafqsVs2/zyGsaCel41QT5Lpbs/rG56Kp+YsmLXL8I=",
|
"narHash": "sha256-Jvuh4VWR8Ep+UlX1siyiMWJAn9fPNZKN5IxzVOnGAec=",
|
||||||
"owner": "emmanuelrosa",
|
"owner": "emmanuelrosa",
|
||||||
"repo": "btc-clients-nix",
|
"repo": "btc-clients-nix",
|
||||||
"rev": "ef81159876930802ef9861aa5c039cec6886a30f",
|
"rev": "761b147fd7038b210251b16cb2b4e3fe5e9b603b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -26,11 +26,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785627969,
|
"lastModified": 1787559586,
|
||||||
"narHash": "sha256-4dtXQk/NMePegK/nWp5NSeuZKLATItOq61lpEvmXqGw=",
|
"narHash": "sha256-onL0VLf9vPllmT0H/OlURIU5r5t5WIEl7t4tVNKT0Nw=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "427bf4bd9435fdf21321c8cc628c24efc14c0f7a",
|
"rev": "9d0d87172c374f89da73c1cfe6d81ae62feac1f1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -41,11 +41,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1785590095,
|
"lastModified": 1788179970,
|
||||||
"narHash": "sha256-CNO2szJbdLjVN/Hi1BML9MSALz1GM2fIdwnzs404QO8=",
|
"narHash": "sha256-r5LmxzIhsu5+oDybatN/HJ8roYOKjb2Apa5xI6v46VU=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e568f3b19d54b08f48bfae9b12b3e124d1a28002",
|
"rev": "1db62ab7d2ccf1916bbf7deb61fc9d16f1c4ab49",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -56,11 +56,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-stable": {
|
"nixpkgs-stable": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787753485,
|
"lastModified": 1788690626,
|
||||||
"narHash": "sha256-BZWCi9ZRJiARTuKTbbtvFTj7t1TK4G3UEckT3HyNfRg=",
|
"narHash": "sha256-+v4I4LawmRD/mVxO7QIAerRrCkElp3YImzWkkUnvOTg=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "062346a6d85bc4b49dfaa61c986e9c5be21217d1",
|
"rev": "c25784012c9982bca5b3e0de87e90bbdac8927d3",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -72,11 +72,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-stable_2": {
|
"nixpkgs-stable_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787753485,
|
"lastModified": 1788297115,
|
||||||
"narHash": "sha256-BZWCi9ZRJiARTuKTbbtvFTj7t1TK4G3UEckT3HyNfRg=",
|
"narHash": "sha256-Z+vUNbfd2FIKkWOTkcT7RYlh3oFCnig/d2eXD1SWf2E=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "062346a6d85bc4b49dfaa61c986e9c5be21217d1",
|
"rev": "a3116115851d68b8952a2a4221cc25a84e56b532",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -88,11 +88,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787498568,
|
"lastModified": 1788614874,
|
||||||
"narHash": "sha256-9i/VTdusq/+NM/tz+J1Re+ojkMB8MBf0QshnYfzHz30=",
|
"narHash": "sha256-7QYjT2vHLuX9Z1pdxHXDKCbh1CR3D/2rywB9Tx0MPRg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "56c02bc00adcf003215cc4bd996d6efaf4cff188",
|
"rev": "c043004d1c6985732bcc1cbc5a9c9aecbbb4e0f0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -104,11 +104,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787364730,
|
"lastModified": 1787631388,
|
||||||
"narHash": "sha256-NcYt9QJfpJiF1lAyN8BDPB4EeScbPU+EwQqPiBElrpU=",
|
"narHash": "sha256-vMiXptXarfSdJb1Gkc+FYVOAibuBRj7qxGa8z68q1Uw=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "a831408e6378bc02ebf8cc09b52c96ca86f6bab4",
|
"rev": "ac6b2166e7a9375683b8e98f860f273222337b16",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -120,11 +120,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787736819,
|
"lastModified": 1788179007,
|
||||||
"narHash": "sha256-cV5xEJJK3BvhU8rEd4mC9UsmDi5qscv/kzGPhBRC5WA=",
|
"narHash": "sha256-hn1oU2rue2SYK8dAr8+WNZWtbsz1S2W5mnHlSEuh3bo=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9fbb54b33e91ee4ca368e35a78e0613c720600b3",
|
"rev": "34ab99075ac4f7e40cf037eef32cb1c360bb85e9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -141,11 +141,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1787691219,
|
"lastModified": 1788190018,
|
||||||
"narHash": "sha256-CjeJTOjrluiDuL0W/YBFkSBOQjBYmdKwj7hWAJ4PIh0=",
|
"narHash": "sha256-59BAfH0txPAZrPBF4QJqwvUWppD+ICrcjA1LZAmPnrQ=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixvim",
|
"repo": "nixvim",
|
||||||
"rev": "ebec1ae277a50b16e7d6682edd009ef585d68261",
|
"rev": "41844750e55f17b1385d5b09ca7ade5f11f49506",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -169,11 +169,11 @@
|
|||||||
"nixpkgs-stable": "nixpkgs-stable_2"
|
"nixpkgs-stable": "nixpkgs-stable_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1788226637,
|
"lastModified": 1788796120,
|
||||||
"narHash": "sha256-MMtcA7phjkde0uw9QzTWZCuA938fhoVIuojgRu4R2eo=",
|
"narHash": "sha256-SLxKMbMPf0MN4tVLAURr6p5mNsieXH8dzmMZisqljmM=",
|
||||||
"owner": "naturallaw777",
|
"owner": "naturallaw777",
|
||||||
"repo": "Sovran_Bitcoin",
|
"repo": "Sovran_Bitcoin",
|
||||||
"rev": "7dd51e069690129ee6fbde2136f82b62c09c3d39",
|
"rev": "17cc04f5229563cb06f7e1843ccb8994af1df7f9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -55,7 +55,6 @@
|
|||||||
{ nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.overlays = [ overlay-stable ]; }
|
{ nixpkgs.hostPlatform = "x86_64-linux"; nixpkgs.overlays = [ overlay-stable ]; }
|
||||||
./iso/common.nix
|
./iso/common.nix
|
||||||
sovran-bitcoin.nixosModules.default
|
sovran-bitcoin.nixosModules.default
|
||||||
./modules/sovran-bitcoin-integration.nix
|
|
||||||
nixvim.nixosModules.nixvim
|
nixvim.nixosModules.nixvim
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|||||||
+73
-12
@@ -150,6 +150,61 @@ let
|
|||||||
"haven-relay.service" = if pkgs ? haven-relay then pkgs.haven-relay.version else (if pkgs ? haven then pkgs.haven.version else "0.1.0");
|
"haven-relay.service" = if pkgs ? haven-relay then pkgs.haven-relay.version else (if pkgs ? haven then pkgs.haven.version else "0.1.0");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
# Shared shell prelude used by both the update and rebuild wrapper scripts.
|
||||||
|
# A flake/package fetch that is interrupted (network blip, reboot
|
||||||
|
# mid-download, disk filled, hiccup on the remote) can leave a truncated
|
||||||
|
# tarball or partial git clone in Nix's download caches. Nix then reuses the
|
||||||
|
# corrupt archive on every retry and dies with "cannot read file from
|
||||||
|
# tarball: Truncated tar archive detected" — a failure that is NOT fixed by
|
||||||
|
# simply re-running, but IS fixed by clearing the fetch caches. run_step runs
|
||||||
|
# a command and, on the first failure that matches a download/cache
|
||||||
|
# signature, clears the caches and retries once. Real config errors never
|
||||||
|
# match, so they still fail loudly. Each sourcing script must define $LOG.
|
||||||
|
nix-self-heal-prelude = ''
|
||||||
|
transient_failure() {
|
||||||
|
grep -Eqi 'truncated tar|unexpected end of (file|archive)|unexpected eof|corrupt(ed)? (archive|nar|download|file)|could not (fetch|download)|download.*(failed|interrupted)|timed out|timeout|connection (reset|refused|timed out)|network is unreachable|temporary failure in name resolution|checksum mismatch|hash mismatch|nar hash|unable to download|store path.*is not valid|cannot read file from tarball|into the git cache' "$LOG"
|
||||||
|
}
|
||||||
|
|
||||||
|
clear_fetch_caches() {
|
||||||
|
echo "[SELF-HEAL] Clearing stale Nix download caches and verifying the Nix store…"
|
||||||
|
# Re-fetchable caches only; /nix/store generations and the running system
|
||||||
|
# are never touched here.
|
||||||
|
rm -rf /root/.cache/nix/tarballs /root/.cache/nix/vcs-cache /root/.cache/nix/git* /root/.cache/nix/flakes 2>/dev/null || true
|
||||||
|
# Fast closure-level repair only. A full --check-contents scan hashes
|
||||||
|
# every store path and can take tens of minutes on a big node; the cache
|
||||||
|
# clear above is the actual fix for truncated/corrupt downloads.
|
||||||
|
nix-store --verify --repair >/dev/null 2>&1 || true
|
||||||
|
echo "[SELF-HEAL] Caches cleared; retrying…"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# run_step LABEL CMD [ARGS...] — run a build step; on a transient
|
||||||
|
# fetch/cache failure, heal once and retry. Returns the command exit code
|
||||||
|
# but leaves error messaging to the caller.
|
||||||
|
run_step() {
|
||||||
|
label="$1"; shift
|
||||||
|
rc=1
|
||||||
|
for try in 1 2; do
|
||||||
|
if [ "$try" -eq 2 ]; then
|
||||||
|
echo "── $label — retry after cache repair ──"
|
||||||
|
fi
|
||||||
|
"$@"
|
||||||
|
rc=$?
|
||||||
|
if [ "$rc" -eq 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ "$try" -eq 1 ] && transient_failure; then
|
||||||
|
echo ""
|
||||||
|
echo "[SELF-HEAL] $label failed on a download/cache error (see above)."
|
||||||
|
clear_fetch_caches
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
return "$rc"
|
||||||
|
done
|
||||||
|
return "$rc"
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
# ── Update wrapper script ──────────────────────────────────────
|
# ── Update wrapper script ──────────────────────────────────────
|
||||||
update-script = pkgs.writeShellScript "sovran-hub-update.sh" ''
|
update-script = pkgs.writeShellScript "sovran-hub-update.sh" ''
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
@@ -171,12 +226,14 @@ let
|
|||||||
|
|
||||||
RC=0
|
RC=0
|
||||||
|
|
||||||
|
${nix-self-heal-prelude}
|
||||||
|
|
||||||
echo "── Step 1/3: nix flake update ────────────────────"
|
echo "── Step 1/3: nix flake update ────────────────────"
|
||||||
if ! nix flake update --flake /etc/nixos --print-build-logs \
|
if ! run_step "nix flake update" nix flake update --flake /etc/nixos --print-build-logs \
|
||||||
--option connect-timeout 10 \
|
--option connect-timeout 10 \
|
||||||
--option stalled-download-timeout 90 \
|
--option stalled-download-timeout 90 \
|
||||||
--option download-attempts 7 \
|
--option download-attempts 7 \
|
||||||
--option fallback true 2>&1; then
|
--option fallback true; then
|
||||||
echo "[ERROR] nix flake update failed"
|
echo "[ERROR] nix flake update failed"
|
||||||
RC=1
|
RC=1
|
||||||
fi
|
fi
|
||||||
@@ -186,22 +243,22 @@ let
|
|||||||
echo "── Step 2/3: nixos-rebuild boot (stage next reboot) ──"
|
echo "── Step 2/3: nixos-rebuild boot (stage next reboot) ──"
|
||||||
# Stream output straight into $LOG (see rebuild-script) so the Hub UI
|
# Stream output straight into $LOG (see rebuild-script) so the Hub UI
|
||||||
# shows live progress instead of an empty log during long builds.
|
# shows live progress instead of an empty log during long builds.
|
||||||
nixos-rebuild boot --flake /etc/nixos --print-build-logs \
|
if run_step "nixos-rebuild boot" nixos-rebuild boot --flake /etc/nixos --print-build-logs \
|
||||||
--option connect-timeout 10 \
|
--option connect-timeout 10 \
|
||||||
--option stalled-download-timeout 90 \
|
--option stalled-download-timeout 90 \
|
||||||
--option download-attempts 7 \
|
--option download-attempts 7 \
|
||||||
--option fallback true
|
--option fallback true; then
|
||||||
BOOT_RC=$?
|
if ! readlink -f /nix/var/nix/profiles/system > "$GENERATION"; then
|
||||||
if [ "$BOOT_RC" -ne 0 ]; then
|
|
||||||
echo "[ERROR] nixos-rebuild boot failed"
|
|
||||||
RC=1
|
|
||||||
elif ! readlink -f /nix/var/nix/profiles/system > "$GENERATION"; then
|
|
||||||
# The marker is informational only. The Hub derives pending-reboot
|
# The marker is informational only. The Hub derives pending-reboot
|
||||||
# state from the NixOS system profile itself, so failing to record
|
# state from the NixOS system profile itself, so failing to record
|
||||||
# the marker must not fail an otherwise successful update.
|
# the marker must not fail an otherwise successful update.
|
||||||
echo "[WARNING] update succeeded but its staged generation could not be recorded"
|
echo "[WARNING] update succeeded but its staged generation could not be recorded"
|
||||||
rm -f "$GENERATION"
|
rm -f "$GENERATION"
|
||||||
fi
|
fi
|
||||||
|
else
|
||||||
|
echo "[ERROR] nixos-rebuild boot failed"
|
||||||
|
RC=1
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -245,12 +302,15 @@ let
|
|||||||
echo " Sovran_SystemsOS Rebuild — $(date)"
|
echo " Sovran_SystemsOS Rebuild — $(date)"
|
||||||
echo "══════════════════════════════════════════════════"
|
echo "══════════════════════════════════════════════════"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
|
${nix-self-heal-prelude}
|
||||||
|
|
||||||
echo "── Rebuilding system configuration ──────────────"
|
echo "── Rebuilding system configuration ──────────────"
|
||||||
# Stream output straight into $LOG (tee'd by the exec redirect above) so
|
# Stream output straight into $LOG (tee'd by the exec redirect above) so
|
||||||
# the Hub UI shows live progress. Capturing the output in a variable
|
# the Hub UI shows live progress. Capturing the output in a variable
|
||||||
# kept the log empty for the entire build+activation, which made long
|
# kept the log empty for the entire build+activation, which made long
|
||||||
# rebuilds can otherwise look like a hang.
|
# rebuilds can otherwise look like a hang.
|
||||||
nixos-rebuild switch --flake /etc/nixos --print-build-logs \
|
run_step "nixos-rebuild switch" nixos-rebuild switch --flake /etc/nixos --print-build-logs \
|
||||||
--option connect-timeout 10 \
|
--option connect-timeout 10 \
|
||||||
--option stalled-download-timeout 90 \
|
--option stalled-download-timeout 90 \
|
||||||
--option download-attempts 7 \
|
--option download-attempts 7 \
|
||||||
@@ -266,11 +326,11 @@ let
|
|||||||
echo ""
|
echo ""
|
||||||
echo " ✓ Build succeeded — a reboot is required to apply this rebuild"
|
echo " ✓ Build succeeded — a reboot is required to apply this rebuild"
|
||||||
echo " (Critical system components changed; running nixos-rebuild boot instead)"
|
echo " (Critical system components changed; running nixos-rebuild boot instead)"
|
||||||
if nixos-rebuild boot --flake /etc/nixos --print-build-logs \
|
if run_step "nixos-rebuild boot" nixos-rebuild boot --flake /etc/nixos --print-build-logs \
|
||||||
--option connect-timeout 10 \
|
--option connect-timeout 10 \
|
||||||
--option stalled-download-timeout 90 \
|
--option stalled-download-timeout 90 \
|
||||||
--option download-attempts 7 \
|
--option download-attempts 7 \
|
||||||
--option fallback true 2>&1; then
|
--option fallback true; then
|
||||||
echo "REBOOT_REQUIRED" > "$STATUS"
|
echo "REBOOT_REQUIRED" > "$STATUS"
|
||||||
else
|
else
|
||||||
echo "[ERROR] nixos-rebuild boot also failed"
|
echo "[ERROR] nixos-rebuild boot also failed"
|
||||||
@@ -278,6 +338,7 @@ let
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
|
echo "[ERROR] nixos-rebuild switch failed"
|
||||||
echo ""
|
echo ""
|
||||||
echo "══════════════════════════════════════════════════"
|
echo "══════════════════════════════════════════════════"
|
||||||
echo " ✗ Rebuild failed — see errors above"
|
echo " ✗ Rebuild failed — see errors above"
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ EOF
|
|||||||
# interface's own address, so it always matches the subnet the LAN
|
# interface's own address, so it always matches the subnet the LAN
|
||||||
# clients (phones on Wi-Fi) actually live on.
|
# clients (phones on Wi-Fi) actually live on.
|
||||||
LAN_CIDR=$(ip -4 -o addr show dev "$IFACE" | awk '{print $4}' | grep -vE '^(127\.|169\.254\.)' | head -n1 | python3 -c 'import sys, ipaddress; s = sys.stdin.read().strip(); print(str(ipaddress.ip_network(s, strict=False)) if s else "")' 2>/dev/null)
|
LAN_CIDR=$(ip -4 -o addr show dev "$IFACE" | awk '{print $4}' | grep -vE '^(127\.|169\.254\.)' | head -n1 | python3 -c 'import sys, ipaddress; s = sys.stdin.read().strip(); print(str(ipaddress.ip_network(s, strict=False)) if s else "")' 2>/dev/null)
|
||||||
echo "Derived LAN CIDR for TURN relay: ${LAN_CIDR:-<none>}"
|
if [ -n "$LAN_CIDR" ]; then echo "Derived LAN CIDR for TURN relay: $LAN_CIDR"; else echo "Derived LAN CIDR for TURN relay: <none>"; fi
|
||||||
|
|
||||||
# Generate the full LiveKit config the daemon will load. turn.domain and
|
# Generate the full LiveKit config the daemon will load. turn.domain and
|
||||||
# rtc.interfaces.includes are only known at runtime, so they are
|
# rtc.interfaces.includes are only known at runtime, so they are
|
||||||
|
|||||||
Reference in New Issue
Block a user