initial retooling

This commit is contained in:
2026-03-27 14:23:08 -05:00
commit 5057ed2a05
46 changed files with 4969 additions and 0 deletions

108
modules/core/caddy.nix Normal file
View File

@@ -0,0 +1,108 @@
{ config, pkgs, lib, ... }:
{
services.caddy = {
enable = true;
user = "caddy";
group = "root";
configFile = "/run/caddy/Caddyfile";
};
systemd.services.caddy-generate-config = {
description = "Generate Caddyfile from /var/lib/domains at runtime";
before = [ "caddy.service" ];
requiredBy = [ "caddy.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
RuntimeDirectory = "caddy";
};
path = [ pkgs.coreutils ];
script = ''
MATRIX=$(cat /var/lib/domains/matrix)
WORDPRESS=$(cat /var/lib/domains/wordpress)
NEXTCLOUD=$(cat /var/lib/domains/nextcloud)
BTCPAY=$(cat /var/lib/domains/btcpayserver)
VAULTWARDEN=$(cat /var/lib/domains/vaultwarden)
HAVEN=$(cat /var/lib/domains/haven)
ACME_EMAIL=$(cat /var/lib/domains/sslemail)
# Start with global config
cat > /run/caddy/Caddyfile <<EOF
{
email $ACME_EMAIL
}
EOF
# If element-calling is enabled, it wrote a snippet with
# enhanced Matrix vhosts (.well-known, element-calling routes)
if [ -f /run/caddy/element-calling.snippet ]; then
cat /run/caddy/element-calling.snippet >> /run/caddy/Caddyfile
else
# Fallback: basic Matrix vhosts without element-calling
cat >> /run/caddy/Caddyfile <<EOF
$MATRIX {
reverse_proxy /_matrix/* http://localhost:8008
reverse_proxy /_synapse/client/* http://localhost:8008
}
$MATRIX:8448 {
reverse_proxy http://localhost:8008
}
EOF
fi
# Append remaining vhosts
cat >> /run/caddy/Caddyfile <<EOF
$WORDPRESS {
encode gzip zstd
root * /var/lib/www/wordpress
php_fastcgi unix//run/phpfpm/mypool.sock
file_server browse
}
$NEXTCLOUD {
encode gzip zstd
root * /var/lib/www/nextcloud
php_fastcgi unix//run/phpfpm/mypool.sock {
trusted_proxies private_ranges
}
file_server
redir /.well-known/carddav /remote.php/dav/ 301
redir /.well-known/caldav /remote.php/dav/ 301
header {
Strict-Transport-Security max-age=31536000;
}
}
$BTCPAY {
reverse_proxy http://localhost:23000
encode gzip zstd
}
$VAULTWARDEN {
reverse_proxy http://localhost:8777
encode gzip zstd
}
$HAVEN {
reverse_proxy localhost:3355 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
transport http {
versions 1.1
}
}
request_body {
max_size 100MB
}
}
EOF
'';
};
}

View File

@@ -0,0 +1,68 @@
{ config, pkgs, lib, ... }:
{
# The cron job scans /var/lib/njalla/hooks.d/ for DDNS URLs
systemd.services.njalla-ddns = {
description = "Njalla Dynamic DNS Updater";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
serviceConfig = {
Type = "oneshot";
};
script = ''
set -euo pipefail
IP=$(${pkgs.dig}/bin/dig @resolver4.opendns.com myip.opendns.com +short -4)
if [ -z "$IP" ]; then
echo "Failed to resolve external IP"
exit 1
fi
# Only update if IP changed
LAST_IP_FILE="/var/lib/njalla/.last_ip"
LAST_IP=""
[ -f "$LAST_IP_FILE" ] && LAST_IP=$(cat "$LAST_IP_FILE")
if [ "$IP" = "$LAST_IP" ]; then
echo "IP unchanged ($IP), skipping"
exit 0
fi
echo -n "$IP" > "$LAST_IP_FILE"
echo "IP changed to $IP, updating DNS records..."
# Update external_ip secret
echo -n "$IP" > /var/lib/secrets/external_ip
# Process each DDNS hook
HOOKS_DIR="/var/lib/njalla/hooks.d"
mkdir -p "$HOOKS_DIR"
for hook in "$HOOKS_DIR"/*; do
[ -f "$hook" ] || continue
DDNS_URL=$(cat "$hook")
SERVICE=$(basename "$hook")
echo "Updating $SERVICE..."
${pkgs.curl}/bin/curl -s "''${DDNS_URL}''${IP}" || echo "Failed: $SERVICE"
done
echo "Done."
'';
};
# Run every 15 minutes
systemd.timers.njalla-ddns = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "*:0/15";
Persistent = true;
};
};
# Ensure directory exists
systemd.tmpfiles.rules = [
"d /var/lib/njalla 0700 root root -"
"d /var/lib/njalla/hooks.d 0700 root root -"
];
}

37
modules/core/role-logic.nix Executable file
View File

@@ -0,0 +1,37 @@
{ config, lib, ... }:
{
config = lib.mkMerge [
# Server-Desktop Role most services enabled
(lib.mkIf config.sovran_systemsOS.roles.server-desktop {
sovran_systemsOS.features = {
synapse = true;
bitcoin = true;
coturn = true;
vaultwarden = true;
haven = false;
mempool = false;
bip110 = false;
element-calling = false;
bitcoin-core = false;
rdp = false;
};
})
# Desktop role
(lib.mkIf config.sovran_systemsOS.roles.desktop {
services.xserver.enable = true;
services.desktopManager.gnome.enable = true;
})
# Bitcoin node role
(lib.mkIf config.sovran_systemsOS.roles.node {
sovran_systemsOS.features = {
bitcoin = true;
bip110 = false;
};
})
];
}

33
modules/core/roles.nix Executable file
View File

@@ -0,0 +1,33 @@
{ config, lib, ... }:
{
options.sovran_systemsOS = {
roles = {
server-desktop = lib.mkOption {
type = lib.types.bool;
default = !config.sovran_systemsOS.roles.desktop && !config.sovran_systemsOS.roles.node;
};
desktop = lib.mkEnableOption "Desktop Role";
node = lib.mkEnableOption "Bitcoin Node Only Role";
};
features = {
coturn = lib.mkEnableOption "TURN server";
synapse = lib.mkEnableOption "Matrix Synapse";
bitcoin = lib.mkEnableOption "Bitcoin Ecosystem";
vaultwarden = lib.mkEnableOption "Vaultwarden";
haven = lib.mkEnableOption "Haven NOSTR relay";
bip110 = lib.mkEnableOption "BIP-110 Bitcoin Better Money";
mempool = lib.mkEnableOption "Bitcoin Mempool Explorer";
element-calling = lib.mkEnableOption "Element Video and Audio Calling";
bitcoin-core = lib.mkEnableOption "Bitcoin Core";
rdp = lib.mkEnableOption "Gnome Remote Desktop";
};
nostr_npub = lib.mkOption {
type = lib.types.str;
default = "";
description = "Nostr public key (npub1...) for Haven relay";
};
};
}

View File

@@ -0,0 +1,13 @@
{ config, pkgs, lib, ... }:
let
sovran-manage = pkgs.writeShellScriptBin "sovran-manage" (builtins.readFile ../../scripts/sovran-manage.sh);
in
{
environment.systemPackages = [
sovran-manage
pkgs.pwgen
pkgs.dig
pkgs.curl
];
}