diff --git a/app/icons/bip110.svg b/app/icons/bip110.svg
new file mode 100644
index 0000000..4534ad7
--- /dev/null
+++ b/app/icons/bip110.svg
@@ -0,0 +1 @@
+
diff --git a/app/icons/bitcoin-core.svg b/app/icons/bitcoin-core.svg
new file mode 100644
index 0000000..4296a25
--- /dev/null
+++ b/app/icons/bitcoin-core.svg
@@ -0,0 +1 @@
+
diff --git a/app/sovran_systemsos_hub/application.py b/app/sovran_systemsos_hub/application.py
index 4f52e87..e72a840 100644
--- a/app/sovran_systemsos_hub/application.py
+++ b/app/sovran_systemsos_hub/application.py
@@ -22,7 +22,8 @@ Adw.init()
# Category display order and labels
CATEGORY_ORDER = [
("infrastructure", "Infrastructure"),
- ("bitcoin", "Bitcoin"),
+ ("bitcoin-base", "Bitcoin Base"),
+ ("bitcoin-apps", "Bitcoin Apps"),
("communication", "Communication"),
("apps", "Self-Hosted Apps"),
("nostr", "Nostr"),
@@ -57,17 +58,7 @@ class SovranHubWindow(Adw.ApplicationWindow):
)
header = Adw.HeaderBar()
-
- # Show active role in header
- role = config.get("role", "server_plus_desktop")
- role_label = ROLE_LABELS.get(role, role)
- role_tag = Gtk.Label(
- label=role_label,
- css_classes=["caption", "role-badge"],
- )
- header.set_title_widget(
- self._build_title_box(role_label)
- )
+ header.set_title_widget(self._build_title_box())
refresh_btn = Gtk.Button(
icon_name="view-refresh-symbolic",
@@ -76,7 +67,6 @@ class SovranHubWindow(Adw.ApplicationWindow):
refresh_btn.connect("clicked", lambda _b: self._refresh_all())
header.pack_end(refresh_btn)
- # Main vertical layout
self._main_box = Gtk.Box(
orientation=Gtk.Orientation.VERTICAL,
spacing=0,
@@ -100,7 +90,9 @@ class SovranHubWindow(Adw.ApplicationWindow):
if interval and interval > 0:
GLib.timeout_add_seconds(interval, self._auto_refresh)
- def _build_title_box(self, role_label):
+ def _build_title_box(self):
+ role = self._config.get("role", "server_plus_desktop")
+ role_label = ROLE_LABELS.get(role, role)
box = Gtk.Box(
orientation=Gtk.Orientation.VERTICAL,
halign=Gtk.Align.CENTER,
@@ -141,7 +133,6 @@ class SovranHubWindow(Adw.ApplicationWindow):
)
self._main_box.append(section_label)
- # Separator
sep = Gtk.Separator(
orientation=Gtk.Orientation.HORIZONTAL,
margin_start=24,
@@ -150,7 +141,6 @@ class SovranHubWindow(Adw.ApplicationWindow):
)
self._main_box.append(sep)
- # FlowBox for this category
flowbox = Gtk.FlowBox(
max_children_per_line=4,
min_children_per_line=2,
@@ -180,7 +170,6 @@ class SovranHubWindow(Adw.ApplicationWindow):
self._main_box.append(flowbox)
- # Defer first status poll so the window renders immediately
GLib.idle_add(self._refresh_all)
def _refresh_all(self):
diff --git a/modules/core/sovran-hub.nix b/modules/core/sovran-hub.nix
index 2980a2e..8902e30 100644
--- a/modules/core/sovran-hub.nix
+++ b/modules/core/sovran-hub.nix
@@ -3,31 +3,30 @@
let
cfg = config.sovran_systemsOS;
- # ── Determine Bitcoin implementation label ───────────────────
- bitcoinImplName =
- if cfg.features.bitcoin-core then "Bitcoin Core"
- else if cfg.features.bip110 then "Bitcoin Knots + BIP110"
- else "Bitcoin Knots";
-
monitoredServices =
# ── Infrastructure (always present) ────────────────────────
[
- { name = "Caddy"; unit = "caddy.service"; type = "system"; icon = "caddy"; enabled = true; category = "infrastructure"; }
- { name = "Tor"; unit = "tor.service"; type = "system"; icon = "tor"; enabled = true; category = "infrastructure"; }
+ { name = "Caddy"; unit = "caddy.service"; type = "system"; icon = "caddy"; enabled = true; category = "infrastructure"; }
+ { name = "Tor"; unit = "tor.service"; type = "system"; icon = "tor"; enabled = true; category = "infrastructure"; }
]
- # ── Bitcoin Ecosystem ──────────────────────────────────────
+ # ── Bitcoin Base (node implementations) ────────────────────
++ [
- { name = bitcoinImplName; unit = "bitcoind.service"; type = "system"; icon = "bitcoind"; enabled = cfg.services.bitcoin; category = "bitcoin"; }
- { name = "Electrs"; unit = "electrs.service"; type = "system"; icon = "electrs"; enabled = cfg.services.bitcoin; category = "bitcoin"; }
- { name = "LND"; unit = "lnd.service"; type = "system"; icon = "lnd"; enabled = cfg.services.bitcoin; category = "bitcoin"; }
- { name = "Ride The Lightning"; unit = "rtl.service"; type = "system"; icon = "rtl"; enabled = cfg.services.bitcoin; category = "bitcoin"; }
- { name = "BTCPayserver"; unit = "btcpayserver.service"; type = "system"; icon = "btcpayserver"; enabled = cfg.services.bitcoin; category = "bitcoin"; }
- { name = "Mempool"; unit = "mempool.service"; type = "system"; icon = "mempool"; enabled = cfg.features.mempool; category = "bitcoin"; }
+ { name = "Bitcoin Knots"; unit = "bitcoind.service"; type = "system"; icon = "bitcoind"; enabled = cfg.services.bitcoin && !cfg.features.bitcoin-core && !cfg.features.bip110; category = "bitcoin-base"; }
+ { name = "Bitcoin Core"; unit = "bitcoind.service"; type = "system"; icon = "bitcoin-core"; enabled = cfg.features.bitcoin-core; category = "bitcoin-base"; }
+ { name = "Bitcoin Knots + BIP110"; unit = "bitcoind.service"; type = "system"; icon = "bip110"; enabled = cfg.features.bip110; category = "bitcoin-base"; }
+ ]
+ # ── Bitcoin Apps (services on top of the node) ─────────────
+ ++ [
+ { name = "Electrs"; unit = "electrs.service"; type = "system"; icon = "electrs"; enabled = cfg.services.bitcoin; category = "bitcoin-apps"; }
+ { name = "LND"; unit = "lnd.service"; type = "system"; icon = "lnd"; enabled = cfg.services.bitcoin; category = "bitcoin-apps"; }
+ { name = "Ride The Lightning"; unit = "rtl.service"; type = "system"; icon = "rtl"; enabled = cfg.services.bitcoin; category = "bitcoin-apps"; }
+ { name = "BTCPayserver"; unit = "btcpayserver.service"; type = "system"; icon = "btcpayserver"; enabled = cfg.services.bitcoin; category = "bitcoin-apps"; }
+ { name = "Mempool"; unit = "mempool.service"; type = "system"; icon = "mempool"; enabled = cfg.features.mempool; category = "bitcoin-apps"; }
]
# ── Communication ──────────────────────────────────────────
++ [
- { name = "Matrix-Synapse"; unit = "matrix-synapse.service"; type = "system"; icon = "synapse"; enabled = cfg.services.synapse; category = "communication"; }
- { name = "Element-Call"; unit = "livekit.service"; type = "system"; icon = "livekit"; enabled = cfg.features.element-calling; category = "communication"; }
+ { name = "Matrix-Synapse"; unit = "matrix-synapse.service"; type = "system"; icon = "synapse"; enabled = cfg.services.synapse; category = "communication"; }
+ { name = "Element-Call"; unit = "livekit.service"; type = "system"; icon = "livekit"; enabled = cfg.features.element-calling; category = "communication"; }
]
# ── Self-Hosted Apps ───────────────────────────────────────
++ [
@@ -40,7 +39,6 @@ let
{ name = "Haven Relay"; unit = "haven-relay.service"; type = "system"; icon = "haven"; enabled = cfg.features.haven; category = "nostr"; }
];
- # ── Determine active role name ───────────────────────────────
activeRole =
if cfg.roles.desktop then "desktop"
else if cfg.roles.node then "node"