The full-system updater runs as a detached systemd service and can finish
successfully even when the browser loses its status connection. In that
case the update log and status file correctly report REBOOT_REQUIRED, but
the Hub modal can remain on "Updating..." with its controls disabled.
There were four independent ways for the frontend to get stuck:
* update status fetches had no deadline, so a request that stayed pending
never rejected and never advanced the existing failure counter;
* setInterval started async polls without waiting for the previous poll,
allowing slow requests to overlap and responses to arrive out of order;
* each log chunk used textContent +=, replacing the complete and growing
Nix build log every two seconds, which could stall browser rendering and
was especially visible over RDP; and
* page reload, tab resume, and RDP reconnect did not reattach the modal to
the update status persisted by the backend.
This produced a dangerous UX mismatch: the machine had a fully staged
NixOS generation and was ready to reboot, while the Hub continued telling
the user that the update was still running.
Bound status requests with AbortController, prevent overlapping polls, and
replace the endless spinner after sustained failures with an explicit
"Update status unavailable" state and Retry Status action. Reconcile state
immediately on focus, visibility, online, page startup, and before starting
a new update. Use no-store requests and render verbose logs incrementally
with a bounded visible tail while retaining the complete report in memory.
Apply the same timeout and single-flight protection to rebuild polling.
Record the exact generation produced by `nixos-rebuild boot`. The Hub now
keeps REBOOT_REQUIRED visible until that generation matches
/run/current-system, then clears the marker after reboot. For an update
started by an older updater that did not write the marker, recover the
staged generation from the final nixos-rebuild log line. The dashboard
sidebar also distinguishes update-in-progress and restart-required states.
Regression coverage verifies generation marker/log recovery, pre- versus
post-reboot detection, request timeout wiring, single-flight polling,
connection-loss UX, RDP/tab resume reconciliation, bounded log rendering,
page-reload recovery, and JavaScript syntax.
Validation:
* python3 -m unittest discover -s tests -p 'test_*.py' -v (170 passed)
* node --check app/sovran_systemsos_web/static/js/*.js
* python3 -m py_compile for changed Python modules
* git diff --check
A Nix evaluation was not available in the development sandbox; the NixOS
module should still be evaluated and built in CI or on a test machine before
release.
Keeps the modal-matching neutral pill styling; the badge now reads
"v1.0.5" as plain uniform text instead of the bare number.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
The header badge now uses the exact visual treatment of
.creds-title-version-badge from modals.css: neutral translucent pill
(rgba(255,255,255,0.06) background, 0.08 white border, 12px radius,
--text-secondary text, 0.72rem/600/2px-10px padding), no hover
animation, and a bare version number like the modals show (e.g.
"2.8.4") instead of the green pill with a "v" prefix.
The badge markup flattens to a single text span; the .version-label
and .version-number rules are removed.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Single source of truth (static/js/domain-prereqs.js) for the domain
prerequisite instructions so all three surfaces read identically:
- Server + Desktop first-boot onboarding wizard (step 3)
- Lightning Wallet Connections (NWC) enable modal (Node-only mode)
- BTCPay Server (web) enable modal (Node-only mode)
Both feature-enable flows share openDomainSetupModal(), which now drops
its role-branched intro and 'Option A/B' blocks in favor of the shared
renderers. All surfaces now consistently cover:
1. A domain from Njal.la (account, subdomain-vs-separate-domain,
Dynamic record with host-part-only Name field, auto-filled IP,
DDNS curl command)
2. Router access — forward ports 80 & 443 (TCP) to this computer's
internal IP, once, for HTTPS/SSL (with CGNAT note)
3. How to get Njal.la working, step by step
The reconfigure/troubleshooting modal gains the same router reminder,
since 'domain not reachable' is often the port forwarding rather than
DNS. domain-prereqs.js is loaded via asset_version cache busting and
ships automatically (installPhase copies the package wholesale).
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
- The Hub header badge could render 'vdev' because the runtime
/etc/nixos/VERSION lookup fell back to the literal string 'dev' when
the file was missing (e.g. dev/test environments, or before the
Nix-generated config carried a version at all).
- modules/core/sovran-hub.nix now reads the repo's VERSION file at
Nix eval time and bakes a real semantic version (sovran_version)
into the generated config.json and a VERSION file shipped with the
package, so the Hub always has a solid value to display.
- server.py's _get_sovran_version() now reads that baked-in
sovran_version first, and explicitly rejects a literal 'dev' value
from any of its file-based fallbacks so the badge never shows
'vdev' again.
- templates/index.html + header.css: wrapped the title and the
version badge in a '.title-group' column so the version badge sits
directly underneath 'Sovran_SystemsOS Hub', left-aligned with it.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
- Add release-stable.sh script with automatic tagging, CHANGELOG updates,
GitHub/Gitea release creation, and VERSION file management
- Update iso/common.nix to include version from VERSION file in ISO filename
- Add VERSION file (current: 1.0.3)
- Polish OS version badge in Sovran Hub header (top-right)
- Update README.md download link to versioned ISO
- Update CHANGELOG.md with existing tags (v1.0.0 – v1.0.3)
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
- Add security-reset-overlay HTML element to index.html that shows immediately
when the user confirms "Erase & Reset", before the synchronous API call runs
- Add .security-reset-overlay CSS to security.css (reuses reboot-card styles,
adds fade-in animation, z-index 1000 to sit above all other content)
- Fix reboot overlay class bug: classList.add("open") → classList.add("visible")
so the overlay actually renders per the .reboot-overlay.visible CSS rule
- Show overlay step text "Erasing data and resetting credentials…" during wipe,
update to "Reset complete. Rebooting now…" when API returns
- Call waitForServerReboot() (globally defined in update.js) after reset so the
page auto-reloads when the system comes back online
- Hide the security-reset-overlay and re-enable the button on error
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/185d0b41-d54d-4ea2-93d6-bfb7c15b8aed
Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>
- Add <aside class="sidebar"> with #sidebar-support and #sidebar-features to index.html
- Restyle .main-content as flex layout (sidebar left, tiles right)
- Body is now display:flex column with overflow:hidden for independent scroll panels
- Sidebar (270px fixed) with overflow-y:auto scrolls independently
- Tiles area (flex:1) scrolls independently
- New sidebar support button (.sidebar-support-btn) replaces support tile in main grid
- Feature Manager now renders into #sidebar-features instead of $tilesArea
- Compact sidebar overrides for .feature-card padding/font-size
- Remove 'support' and 'feature-manager' from CATEGORY_ORDER
- Responsive: sidebar becomes full-width above tiles at <=768px
Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/4304350a-bc4f-4698-82b5-8ee28f0ad960
Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com>