- 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
6.5 KiB
Wallet Connections
Wallet Connections is a Hub-managed Sovran_SystemsOS feature that lets members create isolated Lightning app connections and reusable Lightning Addresses backed by a local LND node via Alby Hub.
Enablement flow
Use the existing Hub service tile flow:
- Open Wallet Connections in Bitcoin Apps.
- Enable feature.
- Complete existing port/domain/DDNS/rebuild flow (80/TCP and 443/TCP).
- Reopen tile and manage connections.
Service-detail modal UX
Wallet management runs inside the existing Wallet Connections service-detail modal with dedicated states:
- Empty state: no wallets yet, with create action.
- Create form: name, alias, access preset, optional spend limit.
- Created/secret state: one-time pairing secret (URI + QR) shown with prominent warning: Keep the NWC string and QR private. The NWC connection secret cannot be displayed again.
- Wallet list state: per-wallet actions for verify/test, drain, and delete.
Pairing URI and QR data are cleared when:
- "I Saved This Secret" is clicked
- the service modal X is closed
- the overlay closes the modal
- navigation otherwise leaves the secret view
Guardrails:
- Action buttons are disabled while API requests are in flight.
- Destructive actions (drain/delete) require user confirmation.
- API errors are surfaced inline in the modal state.
Architecture
Authenticated Hub management API
-> local Alby Hub (port 8080, loopback only)
-> local LND
Public Lightning Address
-> local Caddy on 80/443
-> loopback nwc-lnurl service (port 8181)
-> local Alby Hub invoice API with isolated appId
-> local LND
Security invariants:
- Alby Hub management port (8080) is never opened to the public firewall.
- Dedicated LNURL service port (8181) is never opened to the public firewall.
- LNURL callback and discovery are exposed only through Caddy on 80/443.
- Management APIs are authenticated and remain under
/api/nwc/. - Pairing secrets are returned only on create responses and are never stored.
- Pairing secret QR data is generated only for create responses and is not rehydrated via wallet list APIs.
- Invoice attribution enforces wallet isolation via numeric appId on every LNURL callback.
- Wallet Connections state (Alby Hub database) is never exposed as a plain JSON file.
Domain and runtime files
- Domain key:
lightning - Runtime domain file:
/var/lib/domains/lightning - Alby Hub state:
/var/lib/albyhub/(restrictive permissions, secret-bearing) - Alby Hub database:
/var/lib/albyhub/nwc.db - Alby Hub unlock password:
/var/lib/albyhub/unlock-password(generated once, mode 0600) - LND macaroon for Alby Hub:
/run/lnd/albyhub.macaroon(restricted permissions)
Alby Hub version pin and patches
Alby Hub is packaged in modules/nwc-wallets.nix with the following patches:
- Private route hints (
0001-lnd-private-route-hints.patch): setsPrivate: truein regular LNDMakeInvoicerequests so that wallets behind private channels can receive payments via route hints. Hold-invoice behavior is unchanged. - Invoice app attribution (
0002-invoice-app-attribution.patch): extendsCreateInvoice,MakeInvoiceRequest,http_service, andwails_handlersto accept and pass an optional numericappIdso that LNURL callbacks can attribute invoices to a specific isolated subwallet. Desktop/Wails calls passniland continue using the primary wallet.
The vendorHash and sha256 fields in the derivation must be updated whenever the Alby Hub version changes.
Services
| Service | User | Description |
|---|---|---|
albyhub.service |
albyhub |
Headless Alby Hub NWC wallet server |
nwc-lnurl.service |
nwc-lnurl |
Dedicated LNURL discovery and callback service |
albyhub-init.service |
root (oneshot) |
Generates unlock-password once on first boot |
API
Management (authenticated):
GET /api/nwc/wallets— list all managed wallets (no secrets)POST /api/nwc/wallets— create a wallet; returnspairing_uriexactly onceDELETE /api/nwc/wallets/{id-or-pubkey}— drain and deletePOST /api/nwc/wallets/{id-or-pubkey}/drain— transfer funds to primary walletPOST /api/nwc/addresses/{alias}/test— verify public LNURL endpoint
Public LNURL (served by dedicated nwc-lnurl service via Caddy):
GET /.well-known/lnurlp/{alias}— LNURL-pay discoveryGET /lnurlp/{alias}/callback?amount=<msat>— invoice creation via Alby Hub
Recovery CLI
nwc-wallet is included with the Hub package and calls the real Alby Hub manager:
nwc-wallet create <name> <alias> --receive-onlynwc-wallet create <name> <alias> --limit-sats <amount>nwc-wallet listnwc-wallet drain <wallet>nwc-wallet delete <wallet>nwc-wallet address show <alias>nwc-wallet health
A CLI create command prints the real NWC pairing secret once. Keep it private.
Backup and restore
/var/lib/albyhub is the authoritative Alby Hub state directory. It contains:
- The SQLite database (
nwc.db) with all wallet app records. - The unlock password (
unlock-password).
This directory must be treated as secret-bearing wallet material. Backups containing it must be encrypted and access-controlled.
To back up while the service is running, use SQLite online backup (VACUUM INTO) or a controlled brief service stop rather than a live cp. A brief stop of albyhub.service before copying nwc.db is the safest approach.
Disabling Wallet Connections stops albyhub.service and nwc-lnurl.service and removes the Caddy exposure, while preserving /var/lib/albyhub. Re-enabling and rebuilding restores all existing connections — no secrets need to be regenerated.
Troubleshooting
Alby Hub service not starting:
- Check
journalctl -u albyhub.servicefor errors. - Verify
/var/lib/albyhub/unlock-passwordexists and is readable byalbyhub. - Verify
/run/lnd/albyhub.macaroonexists (LND must be running and the macaroon generated).
LNURL discovery returning 503:
- Check that
albyhub.serviceis running. - Check that
/var/lib/domains/lightningcontains the correct domain. - Check
journalctl -u nwc-lnurl.service.
Invoice creation failing:
- Verify LND has sufficient inbound liquidity on channels with route hints.
- Check
journalctl -u albyhub.servicefor LND RPC errors.
Wallet creation partial failure (funding not transferred):
- The wallet was created and the NWC connection secret was shown. Save it.
- Do not create another wallet for the same alias.
- Fund the isolated wallet manually via Alby Hub's internal transfer API.