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.
prefetch-npm-deps runs in the deps fetcher's buildPhase and checks for
package-lock.json at CWD (repo root). postPatch runs in both the
fetcher and the main build, so we copy the lock file there. The main
build then cds into the subdir in buildPhase.
npmDepsHash values remain placeholders (lib.fakeHash).
postUnpack did not run in the npmDeps fixed-output derivation, so
prefetch-npm-deps couldn't find package-lock.json. preBuild runs in
both the fetcher and main build, ensuring consistent CWD for the lock
file lookup. Backend patch moved to prePatch/postPatch so it only
applies in the main build.
npmDepsHash values remain placeholders (lib.fakeHash).
The `patches` attribute was being applied inside the npmDeps
fixed-output derivation where patch runs interactively and fails.
sourceRoot also caused the deps fetcher to hash a different tree than
expected. Now:
- postUnpack cds into the subdir for both the fetcher and main build
- postPatch applies the mining-pool patch only in the main build
- npmDepsHash values remain placeholders (lib.fakeHash)
npm ci in buildPhase hangs because the Nix build sandbox has no network
access. buildNpmPackage pre-fetches dependencies as a fixed-output
derivation with network access, then the main build runs offline.
npmDepsHash values are placeholders (lib.fakeHash) — the first builds
will fail with hash mismatches that report the correct hashes.
buildNpmPackage is built into nixpkgs and pre-fetches dependencies as a
fixed-output derivation with network access. npmDepsHash is a
placeholder (lib.fakeHash) — the first build will fail with a hash
mismatch that reports the correct hash.
npm ci in buildPhase hangs because the Nix build sandbox has no network
access. Adopt nix-bitcoin's approach: fetchNodeModules pre-fetches
node_modules as a fixed-output derivation (which has network access),
and the main build copies them offline. Uses the same node_modules hash
as upstream nix-bitcoin for RTL 0.15.8.
ensureClauses generates ALTER ROLE clauses, not GRANT statements, so
'"DATABASE btcpayserver" = "ALL PRIVILEGES"' produced invalid SQL.
ensureDBOwnership = true makes each user own its database, which is
the intended effect.
The services.lnd.macaroons submodule has no 'enable' option — macaroons
are implicitly enabled by being defined. Replace 'enable = true' with
'user = cfg.btcpayserver.user' which is the correct option for
controlling macaroon file ownership.
The option services.postgresql.ensureUsers.*.ensurePermissions was renamed
to ensureClauses in nixpkgs unstable. Update both btcpayserver and nbxplorer
database user configurations.
Replace all pkgs-stable references with pkgs.stable (from overlay-stable in flake.nix):
- Remove pkgs-stable from function arguments
- Use pkgs.stable.nbxplorer (2.6.10 from nixos-26.05)
- Use pkgs.stable.btcpayserver (2.4.2 from nixos-26.05)
Restores the complete original flake.nix that includes:
- nixosModules.Sovran_SystemsOS (required by deployed machines)
- overlay-stable for accessing pkgs.stable packages
- nixosConfigurations.nixos for direct builds
- nixosConfigurations.sovran_systemsos-iso for ISO builds
- nixosTests and checks
1. flake.nix: Add pkgs-stable as specialArg for modules
2. btcpayserver.nix:
- Fix preStart script syntax error (closing brace on new line)
- Use pkgs-stable.btcpayserver (2.4.2) instead of pkgs.btcpayserver
- Use pkgs-stable.nbxplorer (2.6.10) instead of pkgs.nbxplorer
- Remove clightning references (Sovran is LND-only)
- Create packages/rtl/default.nix to build RTL from source
- Update modules/bitcoin/rtl.nix to use vendored package instead of pkgs.rtl
- Remove clightning/lightning-loop references (Sovran is LND-only)
- Simplify to LND-only configuration
The joinmarket-ob-watcher module was not imported from nix-bitcoin,
so referencing it in the onion-services defaults caused an
"attribute 'joinmarket-ob-watcher' missing" evaluation error.
builtins.replaceStrings requires the 'from' and 'to' lists to have the
same length. The 'to' list had a single empty string while 'from' had
three entries (v, newline, CR), which made evaluating image.baseName
fail with: 'from' and 'to' arguments passed to builtins.replaceStrings
have different lengths. Add the two missing empty strings.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
Replace repeated prose across 4 sections with a compact table + flow arrow:
- Top callout: 7-line paragraph → 2-line summary + link
- Development workflow: 2 bullets + 3-step list + warning → 3-row table + flow arrow
- Build from source: re-explained sync relationship → single labels on clone commands
- Contributing footer: repeated branch/host info → one sentence
No information lost; ~270 words removed.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
The README and CONTRIBUTING are mirrored to both Gitea branches, so
GitHub-specific references now name the GitHub repository explicitly
instead of saying 'this repository'.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
The GitHub repo mirrors Gitea's staging-dev branch; changes are tested
here and promoted to the stable branch on the self-hosted Gitea instance.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
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>
Mirrors the service modal pattern where the version badge sits to the
right of the title: .title-group switches from a centered column
(badge under the title, PR #377) to a centered row with the badge
vertically centered directly after "Sovran_SystemsOS Hub".
flex-wrap keeps a graceful fallback: on very narrow screens the badge
wraps below the title, centered — the previous stacked look.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
- Replace tagline with 'Bitcoin sovereignty. Sovereign computing. One system.'
- Open with both pillars as 'inseparable freedoms'
- Extend growth path beyond node/infrastructure to include private cloud and comms
- Remove standalone 'Privacy. Sovereignty. Bitcoin.' from intro (branding holds it at page close)
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
- Fix QR hint for zeus-connect-setup.service to use correct LND REST steps
- Expand 'How to Connect' instructions with title/intro/steps/note
- Add matching styled guide block in credentials modal
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>