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)
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.
- Previous lnd-only refactor removed the '++ optional clightning' line
but left 'extraGroups = [ cfg.nbxplorer.group ]' without trailing ';'
-> syntax error: unexpected '=', expecting ';' at home = ...
- Add ';' to fix build on both f13ff45 and 8b8c811
- lndconnect.nix: rewrite to lnd-only (320 lines removed) - remove
services.clightning.plugins.clnrest.lnconnect and
services.clightning-rest.lndconnect (Sovran only uses services.lnd.lndconnect for Zeus)
Fixes: 'services.clightning.plugins.clnrest.address does not exist' on f13ff45
and 'attribute enable missing' at lndconnect.nix:216
- btcpayserver.nix: remove liquidd (lbtc) dead branch - Sovran uses lbtc=false
* remove inherit (config.services) bitcoind liquidd -> just bitcoind
* lbtc chains/rpc handling is dead code, keep guards but no need for liquidd service
- mempool.nix: remove fulcrum branch - Sovran uses electrs only
- rtl.nix: remove lightning-loop block and clightning service block (lnd only)
- Delete modules/bitcoin/stubs.nix entirely - no dead service references left
* liquidd, fulcrum, lightning-loop, joinmarket now not referenced at all
* clightning already handled via guards, not stubs
- Result: modules/bitcoin/ is now 100% tailored to Sovran (bitcoind+knots, lnd, electrs, rtl, btcpayserver, mempool)
No more stubs whack-a-mole on any nixos-unstable
- Move modules/vendor/nix-bitcoin/* -> modules/bitcoin/* (clean, Sovran-owned)
* modules/bitcoin/default.nix imports the 6 tailored services
* modules/bitcoin/common.nix bundles secrets/onion/lib
* modules/bitcoin/stubs.nix kept minimal (no clightning)
* packages/lndinit/default.nix replaces pkgs/sovran-overlay.nix
(lnd.nix now uses pkgs.callPackage ../../packages/lndinit {})
- Remove pkgs/sovran-overlay.nix, pkgs/nbxplorer.nix, pkgs/README.md
* No global overlay - lndinit is a normal package in packages/
- Remove modules/vendor/ entirely
- Update flake.nix: drop overlay-sovran, import ./modules/bitcoin
instead of ./modules/vendor/nix-bitcoin/modules.nix
- No more random vendor/ or pkgs/ dirs - follows Sovran convention:
modules/ for NixOS modules, packages/ for packages
- f13ff45 HAS services.clightning but only as { plugins = ... }, no enable
-> 'option does not exist, did you mean plugins' (your error)
- Sovran never uses lightningBackend == clightning (uses lnd),
so just don't set services.clightning.enable at all
- Also make lnd port conflict check always true for clightning case
- Fixes both f13ff45 (has clightning/plugins) and 8b8c811 (removed)
- f13ff45 (staging-dev) HAS both services -> unconditional stubs duplicate
- Remove both from stubs.nix, keep only liquidd/fulcrum/etc.
- lndconnect.nix left as is (defines clightning-rest.lndconnect) - safe on f13ff45 where base exists
- On 8b8c811 where clightning is removed, Sovran doesn't use it anyway (lnd only), so guards in btcpayserver/lnd prevent use
nixpkgs 8b8c811 (2026-08-08) removed services.clightning.enable,
causing btcpayserver.nix:124 to throw 'option does not exist' on
nixos-rebuild (your error). Sovran uses lnd only, never clightning,
but evaluation still throws.
- Add modules/vendor/nix-bitcoin/stubs.nix to provide missing options
as false stubs: clightning, clightning-rest, liquidd, fulcrum,
lightning-loop/pool, joinmarket
- Guard btcpayserver/rtl/lnd/mempool clightning/liquidd references
with config.services ? X checks
- Trim enable-tor.nix onionServices for removed services
- Remove inputs.nix-bitcoin (fort-nix/nix-bitcoin/release) from flake.nix
- Vendor only 6 services actually used by Sovran: bitcoind, electrs,
lnd (+lndconnect), rtl, btcpayserver, mempool + supporting infra:
secrets, onion-services/addresses, operator, nodeinfo, security,
versioning
- All packages now from nixpkgs directly (pkgs.*) — no pinned pkgs
- Keep nix-bitcoin.* option namespace for compatibility
- backups.nix removed: Sovran uses rsnapshot to Second_Drive
(configuration.nix: hourly/daily to BTCEcoandBackup) — duplicity
remote backup not needed
- netns-isolation.nix replaced with stub (5 lines): original 365-line
bridge/iptables/ip-netns broke Caddy/AlbyHub/RTL a year ago and
is incompatible with nwc-wallets (requires enable=false). Stub
keeps option valid but warns if enabled.
- Add pkgs/sovran-overlay.nix for gaps only: lndinit + netns-exec stub
- Replace plain-text write of new_root_password in api_security_reset()
with scrypt-hashed storage via _hash_password(), matching how the free
password is already handled.
- Return new_root_password in the API response so the user sees it once
before it is irreversibly hashed on disk.
- Teach _resolve_credential() to detect scrypt hashes and display a
human-readable placeholder instead of raw hex in the Hub credentials UI.
- Harden root-password-setup systemd service: if the secrets file already
contains a hash, skip chpasswd so a manual restart never sets the hash
as the literal login password.
- 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>
- 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>
Naming: user-facing 'Wallet Connections' -> 'Lightning Wallet Connections'
across the Hub, feature registry, tile, and NixOS modules. Internal ids
(nwc-wallets, albyhub.service, /api/nwc/*) are unchanged.
UX: the service-detail modal put status, domain diagnostics, router ports,
the enable/disable toggle, restart, the liquidity guide and the whole wallet
manager in one cramped scrolling column. For this feature the modal is now
980px wide and split into two tabs:
- Wallets: wallet grid, create/share/verify flows, collapsible liquidity guide
- Service & Setup: description, status, domain checklist, ports, enable, restart
A status dot and domain chip sit in the tab bar so state is visible from both
tabs, and the modal opens on Setup when the service is off or the Lightning
Address domain is unconfigured. Wallet cards gain a balance chip, pending
badge, a prominent address row, and separated destructive actions.
Non-NWC services keep the original single-column layout and width.
Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
ReadOnlyPaths for nwc-lnurl.service now lists only the specific file
/var/lib/albyhub/unlock-password (least-privilege) instead of the
whole /var/lib/albyhub directory.
Amount-duplicate error message changed to "A single amount parameter is
required" (clearer for 2+ values than "Exactly one").
Test assertion updated to match new message text.
Blocker 1: run nwc-lnurl.service as albyhub user/group so it can read
/var/lib/albyhub/unlock-password (mode 0600, dir mode 0700).
Remove the now-unused nwc-lnurl user, group, and /var/lib/nwc-lnurl
state directory. ReadOnlyPaths updated to allow the whole albyhub dir.
Blocker 2: require exactly one amount query parameter in the LNURL
callback HTTP handler. Duplicate values now return a 400 protocol
error ("Exactly one amount parameter is required") before the helper is
called. The missing-amount and non-integer paths are unchanged.
Blocker 3: partial-funding failure message now reads "was created
successfully" instead of "already exists" to avoid confusion with a
duplicate-name error, while retaining the warning not to recreate.
Tests added:
- LnurlHandlerAmountTests — HTTP-handler level tests for duplicate (2×,
3×), single-valid, and missing amount parameters.
- test_create_partial_failure_message_says_created_successfully —
asserts exact wording of the partial-funding message.
- test_nwc_lnurl_service_runs_as_albyhub — asserts the Nix service block
sets User/Group to albyhub.
- test_nwc_module_no_separate_nwc_lnurl_user — asserts no standalone
nwc-lnurl user/group is declared.
All 229 Python tests pass (1 skipped). JS syntax clean. No secrets.
- nwc_hub_manager.py: Fix BOLT11 regex to accept only valid prefixes (bc/tb/bcrt/tbs); add path to timeout error message; add early-exit to _get_app_pending_txs pagination
- nwc_lnurl_service.py: Allow NWC_LNURL_PORT env var override for port
- nwc-wallets.nix: Remove || true from gofmt so build fails on syntax errors
- test_wallet_connections.py: Extract _call_body() helper; use c.args[] access
- sovran-hub.nix: Add nwc-lnurl launcher binary to sovran-hub-web derivation (same pattern as nwc-wallet); add options.services.sovranHub.webPackage to expose derivation to other modules
- nwc-wallets.nix: Use config.services.sovranHub.webPackage for LNURL ExecStart (fixes sys.path for sovran_systemsos_web imports); replace no-op placeholder patches with real unified diff format showing intended Go changes (Private: true, AppId *uint)
- Add nwc_hub_manager.py: AlbyHubManager with real Alby Hub API (setup, auth, CRUD, drain, delete, invoice)
- Add nwc_lnurl_service.py: dedicated loopback LNURL service on port 8181
- server.py: remove JSON scaffolding (state.json, fake invoice generator, fake NWC URI, LNURL routes); replace with real manager calls; update service maps to albyhub.service; remove LNURL auth-exempt paths
- nwc_wallet_cli.py: rewrite to use real AlbyHubManager instead of JSON state
- modules/nwc-wallets.nix: replace with albyhub user/service, nwc-lnurl service, LND macaroon, unlock-password generation
- modules/core/caddy.nix: proxy LNURL routes to port 8181 (dedicated service) instead of 8937 (Hub)
- modules/core/sovran-hub.nix: service tile points to albyhub.service
- docs/wallet-connections.md: document real architecture, Alby Hub pin/patches, backup sensitivity
- test_wallet_connections.py: replace scaffolding tests with 54 real manager tests using mocked Alby Hub
- Replace environment.etc raw script with pkgs.writeShellApplication
- Declare runtimeInputs: pkgs.coreutils, pkgs.gawk, pkgs.gnugrep
- Use awk -v for safe marker variable passing (no shell interpolation)
- Point systemd ExecStart and activation script at lib.getExe hostsUpdateScript
- Keep /etc/sovran-hosts-update.sh as a source symlink for operator discoverability
- Remove environment.systemPackages reliance
- Emit warning (not silently swallow) on activation failure
- Add structural regression tests (19 new tests, all passing)
- Use set -eu (not set -euf) in sovran-hosts-update.sh
- Add sync note for domain validation regex between shell and Python
- Rename `seen` to `unique_addresses` in _resolve_all_addresses
- Improve loopback override UI message with external DNS check guidance
- Add docstring note about first-address display in _resolve_all_addresses"
- Add modules/core/local-domain-loopback.nix: systemd service and
activation script that write configured service domains to a
Sovran-managed block in /etc/hosts (127.0.0.1 / ::1) so requests
originating on this computer reach Caddy without NAT loopback.
- Import local-domain-loopback.nix in modules/modules.nix.
- server.py: add _validate_domain_value, _is_loopback_address,
_resolve_all_addresses, _trigger_hosts_update helpers.
- server.py: update _check_domain_reachable to use --resolve so
reachability is checked locally via Caddy, not via NAT loopback.
- server.py: update _evaluate_domain_checklist, api_services inline DNS
check, and api_domains_check to recognise loopback resolution as an
intentional local override rather than a DNS mismatch.
- server.py: call _trigger_hosts_update from api_domains_set after
saving a service domain so the /etc/hosts entry is applied immediately.
- Add app/tests/test_loopback_diagnostics.py with 47 tests covering
domain validation, loopback detection, diagnostic checklist logic,
composite health, and api_domains_check."
GRD 50.x invokes pkexec internally for every grdctl --system call, even
when the caller is root. An isolated systemd script PATH does not include
/run/wrappers/bin automatically, causing exit 70 at boot.
Changes:
- Prepend /run/wrappers/bin to PATH at script start so every subsequent
grdctl --system resolves the NixOS setuid pkexec wrapper.
- Add an explicit preflight check (test -x /run/wrappers/bin/pkexec) with
a clear error message before the first grdctl_system call.
- pkgs.polkit remains absent from the service path.
- Update test_setup_runs_grdctl_directly_as_root to reflect that pkexec
now appears in the preflight check (not as a direct invocation).
- Add test_run_wrappers_bin_prepended_to_path and test_pkexec_preflight_check.
Root cause: gnome-remote-desktop-setup.service ran as root but dropped
privileges via `runuser -u gnome-remote-desktop`. Non-root grdctl --system
attempts authorisation through pkexec. The Nix-store pkexec binary is not
setuid, so every system-mode credential call silently failed while the script
still printed "configured successfully". GNOME Remote Desktop then started
without applied credentials, causing Remmina to loop at the login dialog.
Fix:
- Remove `runuser -u gnome-remote-desktop --` from grdctl_system helper;
the root-run oneshot service can call grdctl --system directly.
- Remove pkgs.polkit and pkgs.util-linux from the setup service path as
neither polkit nor runuser is needed any more.
- Update tests: rename test_setup_runs_grdctl_as_gnome_remote_desktop_user
to test_setup_runs_grdctl_directly_as_root and assert that runuser,
pkexec, and sudo are absent; add
test_privilege_escalation_packages_absent_from_setup_path.
All 21 app/tests pass.