diff --git a/configuration.nix b/configuration.nix
index 2b01d9a..e34e001 100644
--- a/configuration.nix
+++ b/configuration.nix
@@ -49,6 +49,7 @@
services.desktopManager.gnome.enable = true;
services.printing.enable = true;
systemd.enableEmergencyMode = false;
+ environment.gnome.excludePackages = [ pkgs.gnome-tour gnome-initial-setup ];
# ── Audio ──────────────────────────────────────────────────
services.pulseaudio.enable = false;
diff --git a/iso/branding.nix b/iso/branding.nix
index 1028bdf..e08e0ec 100644
--- a/iso/branding.nix
+++ b/iso/branding.nix
@@ -8,4 +8,5 @@ in
boot.plymouth.theme = "sovran";
boot.plymouth.themePackages = [ theme ];
boot.kernelParams = [ "quiet" "splash" ];
-}
\ No newline at end of file
+ boot.initrd.systemd.enable = true;
+}
diff --git a/iso/common.nix b/iso/common.nix
index 9fc551b..5f0f90d 100644
--- a/iso/common.nix
+++ b/iso/common.nix
@@ -11,6 +11,8 @@ in
];
image.fileName = "Sovran_SystemsOS.iso";
+
+ isoImage.splashImage = ./assets/splash-logo.png;
users.users.free = {
isNormalUser = true;
@@ -29,6 +31,7 @@ in
installer
zenity
util-linux
+ disko
parted
dosfstools
e2fsprogs
@@ -37,6 +40,8 @@ in
git
curl
];
+
+ environment.etc."sovran/logo.png".source = ./assets/splash-logo.png;
environment.etc."sovran/flake".source = sovranSource;
diff --git a/iso/disko.nix b/iso/disko.nix
new file mode 100644
index 0000000..ad7e30f
--- /dev/null
+++ b/iso/disko.nix
@@ -0,0 +1,62 @@
+{ device ? "/dev/sda", dataDevice ? "", ... }:
+
+{
+ disko.devices = {
+ disk = {
+ main = {
+ type = "disk";
+ device = builtins.toString device;
+ content = {
+ type = "gpt";
+ partitions = {
+ ESP = {
+ priority = 1;
+ name = "ESP";
+ start = "1M";
+ end = "512M";
+ type = "EF00";
+ content = {
+ type = "filesystem";
+ format = "vfat";
+ mountpoint = "/boot/efi";
+ mountOptions = [ "umask=0077" "defaults" ];
+ };
+ };
+ root = {
+ name = "root";
+ start = "512M";
+ end = "100%";
+ content = {
+ type = "filesystem";
+ format = "ext4";
+ mountpoint = "/";
+ extraArgs = [ "-L" "sovran_systemsos" ];
+ };
+ };
+ };
+ };
+ };
+ } // (if dataDevice != "" then {
+ data = {
+ type = "disk";
+ device = builtins.toString dataDevice;
+ content = {
+ type = "gpt";
+ partitions = {
+ primary = {
+ name = "primary";
+ start = "1M";
+ end = "100%";
+ content = {
+ type = "filesystem";
+ format = "ext4";
+ mountpoint = "/run/media/Second_Drive";
+ extraArgs = [ "-L" "BTCEcoandBackup" ];
+ };
+ };
+ };
+ };
+ };
+ } else {});
+ };
+}
\ No newline at end of file
diff --git a/iso/installer.sh b/iso/installer.sh
index 9653a59..0f64e09 100644
--- a/iso/installer.sh
+++ b/iso/installer.sh
@@ -6,17 +6,21 @@ exec > >(tee -a "$LOG") 2>&1
export PATH=/run/current-system/sw/bin:$PATH
-BYTES_4TB=$((4 * 1024 * 1024 * 1024 * 1024))
+# Changed to 2TB cutoff
+BYTES_2TB=$((2 * 1024 * 1024 * 1024 * 1024))
+LOGO="/etc/sovran/logo.png"
human_size() {
numfmt --to=iec --suffix=B "$1"
}
-zenity --info --width=500 --text="Sovran SystemsOS Installer\n\nWARNING:\nThis installer will ERASE ALL DATA on selected disks.\n\nPress OK to continue."
+zenity --info --window-icon="$LOGO" --text="Sovran SystemsOS Installer\n\nWARNING:\nThis installer will ERASE ALL DATA on selected disks.\n\nPress OK to continue."
+
+# Filter out USB drives and loop/cdrom devices so it doesn't try to install to the installation media
+mapfile -t DISKS < <(lsblk -b -dno NAME,SIZE,TYPE,RO,TRAN -e 7,11 | awk '$3=="disk" && $4=="0" && $5!="usb" {print $1":"$2}')
-mapfile -t DISKS < <(lsblk -b -dno NAME,SIZE,TYPE | awk '$3=="disk"{print $1":"$2}')
if [ "${#DISKS[@]}" -eq 0 ]; then
- zenity --error --text="No disks found."
+ zenity --error --window-icon="$LOGO" --text="No valid internal drives found. (USB drives are ignored)"
exit 1
fi
@@ -34,8 +38,9 @@ if [ "${#DISKS_SORTED[@]}" -ge 2 ]; then
DATA_SIZE="${DISKS_SORTED[-1]##*:}"
fi
-if [ -n "$DATA_DISK" ] && [ "$DATA_SIZE" -lt "$BYTES_4TB" ]; then
- zenity --warning --width=500 --text="Second disk detected (${DATA_DISK}), but it is smaller than 4TB.\n\nIt will NOT be used."
+# Updated to check against 2TB
+if [ -n "$DATA_DISK" ] && [ "$DATA_SIZE" -lt "$BYTES_2TB" ]; then
+ zenity --warning --window-icon="$LOGO" --text="Second disk detected (${DATA_DISK}), but it is smaller than 2TB.\n\nIt will NOT be used."
DATA_DISK=""
DATA_SIZE=""
fi
@@ -47,7 +52,8 @@ else
SUMMARY="${SUMMARY}\nData disk: none"
fi
-ROLE=$(zenity --list --radiolist --width=500 --height=250 \
+ROLE=$(zenity --list --radiolist \
+ --window-icon="$LOGO" \
--title="Choose Install Role" \
--column="" --column="Role" \
TRUE "Server-Desktop (default)" \
@@ -58,52 +64,21 @@ if [ -z "$ROLE" ]; then
ROLE="Server-Desktop (default)"
fi
-CONFIRM=$(zenity --entry --width=500 --text="WARNING: This will ERASE ALL DATA on:\n\n${SUMMARY}\n\nType ERASE to continue.")
+CONFIRM=$(zenity --entry --window-icon="$LOGO" --text="WARNING: This will ERASE ALL DATA on:\n\n${SUMMARY}\n\nType ERASE to continue.")
if [ "$CONFIRM" != "ERASE" ]; then
- zenity --error --text="Install cancelled."
+ zenity --error --window-icon="$LOGO" --text="Install cancelled."
exit 1
fi
BOOT_PATH="/dev/${BOOT_DISK}"
-PART_SUFFIX=""
-if [[ "$BOOT_DISK" =~ ^(nvme|mmcblk) ]]; then
- PART_SUFFIX="p"
-fi
-
-parted --script "$BOOT_PATH" mklabel gpt
-parted --script "$BOOT_PATH" mkpart ESP fat32 1MiB 512MiB
-parted --script "$BOOT_PATH" set 1 esp on
-parted --script "$BOOT_PATH" mkpart primary ext4 512MiB 100%
-
-partprobe "$BOOT_PATH"
-udevadm settle
-
-mkfs.fat -F 32 -n boot "${BOOT_PATH}${PART_SUFFIX}1"
-mkfs.ext4 -F -L sovran_systemsos "${BOOT_PATH}${PART_SUFFIX}2"
-
+DATA_PATH=""
if [ -n "$DATA_DISK" ]; then
DATA_PATH="/dev/${DATA_DISK}"
- part_suffix_data=""
- if [[ "$DATA_DISK" =~ ^(nvme|mmcblk) ]]; then
- part_suffix_data="p"
- fi
-
- parted --script "$DATA_PATH" mklabel gpt
- parted --script "$DATA_PATH" mkpart primary ext4 1MiB 100%
- partprobe "$DATA_PATH"
- udevadm settle
- mkfs.ext4 -F -L BTCEcoandBackup "${DATA_PATH}${part_suffix_data}1"
fi
-mkdir -p /mnt
-mount /dev/disk/by-label/sovran_systemsos /mnt
-mkdir -p /mnt/boot/efi
-mount /dev/disk/by-label/boot /mnt/boot/efi
-
-if [ -n "$DATA_DISK" ]; then
- mkdir -p /mnt/run/media/Second_Drive
- mount /dev/disk/by-label/BTCEcoandBackup /mnt/run/media/Second_Drive
-fi
+# Run Disko to partition and format drives
+echo "Running Disko to partition and format drives..."
+disko --mode disko /etc/sovran/flake/iso/disko.nix --argstr device "$BOOT_PATH" --argstr dataDevice "$DATA_PATH"
nixos-generate-config --root /mnt
@@ -123,5 +98,21 @@ EOF
nixos-install --root /mnt --flake /mnt/etc/nixos#nixos
-zenity --info --text="Install complete. Rebooting..."
-reboot
\ No newline at end of file
+EOF
+
+nixos-install --root /mnt --flake /mnt/etc/nixos#nixos
+
+zenity --warning --width=600 --title="INSTALLATION COMPLETE! 🚨 PLEASE READ" --text="Installation Successful!
+
+Before you reboot, please write down your main login details:
+
+Username: free
+Password: free
+
+🚨 CRITICAL: Do not lose this password! If you forget this, you will be permanently locked out of your computer.
+
+📁 Other Passwords: Once the system reboots, it will finish building your forts and generate all the passwords for your apps (Nextcloud, Bitcoin, Matrix, etc.). It will save them in a secure PDF in your Documents folder.
+
+Click OK to reboot into your new system!"
+
+reboot
diff --git a/modules/core/sovran_systemsos-desktop.nix b/modules/core/sovran_systemsos-desktop.nix
new file mode 100644
index 0000000..ebc2b55
--- /dev/null
+++ b/modules/core/sovran_systemsos-desktop.nix
@@ -0,0 +1,156 @@
+{ config, pkgs, lib, ... }:
+
+let
+ customWallpaper = pkgs.stdenvNoCC.mkDerivation {
+ pname = "sovran-systemsos-wallpaper";
+ version = "1.0";
+ src = pkgs.fetchurl {
+ url = "https://git.sovransystems.com/Sovran_Systems/Sovran_SystemsOS_iso/raw/branch/main/post-install-scripts/Wallpaper_Dark_Wide.png";
+ sha256 = "0609gy0vp92fywl7pcr4y3mg05ca6pwxsnlsax14jd371fj4y7fn"; # Make sure this hash is correct!
+ };
+ dontUnpack = true;
+ installPhase = ''
+ mkdir -p $out/share/backgrounds/sovran
+ cp $src $out/share/backgrounds/sovran/Wallpaper_Dark_Wide.png
+ '';
+ };
+in
+{
+ # 1. Install the wallpaper package
+ environment.systemPackages = [ customWallpaper ];
+
+ # 2. Enable dconf
+ programs.dconf.enable = true;
+
+ # 3. Apply system-wide default GNOME settings
+ programs.dconf.profiles.user.databases = [{
+ settings = with lib.gvariant; {
+ "org/gnome/desktop/background" = {
+ picture-uri = "file:///run/current-system/sw/share/backgrounds/sovran/Wallpaper_Dark_Wide.png";
+ picture-uri-dark = "file:///run/current-system/sw/share/backgrounds/sovran/Wallpaper_Dark_Wide.png";
+ picture-options = "zoom";
+ primary-color = "#000000";
+ secondary-color = "#000000";
+ };
+
+ "org/gnome/desktop/input-sources" = {
+ sources = [ (mkTuple [ "xkb" "us" ]) ];
+ xkb-options = [ ];
+ };
+
+ "org/gnome/desktop/interface" = {
+ color-scheme = "prefer-dark";
+ enable-animations = true;
+ icon-theme = "Papirus-Dark";
+ };
+
+ "org/gnome/evolution-data-server" = {
+ migrated = true;
+ };
+
+ "org/gnome/mutter" = {
+ edge-tiling = false;
+ };
+
+ "org/gnome/nautilus/icon-view" = {
+ default-zoom-level = "large";
+ };
+
+ "org/gnome/nautilus/preferences" = {
+ default-folder-viewer = "icon-view";
+ migrated-gtk-settings = true;
+ search-filter-time-type = "last_modified";
+ };
+
+ "org/gnome/shell" = {
+ disabled-extensions = [ "just-perfection-desktop@just-perfection" ];
+ enabled-extensions = [
+ "appindicatorsupport@rgcjonas.gmail.com"
+ "dash-to-dock-cosmic-@halfmexicanhalfamazing@gmail.com"
+ "Vitals@CoreCoding.com"
+ "dash-to-dock@micxgx.gmail.com"
+ "pop-shell@system76.com"
+ "date-menu-formatter@marcinjakubowski.github.com"
+ "systemd-manager@hardpixel.eu"
+ "light-style@gnome-shell-extensions.gcampax.github.com"
+ ];
+ favorite-apps = [
+ "brave-browser.desktop"
+ "org.gnome.Settings.desktop"
+ "org.gnome.Nautilus.desktop"
+ "Sovran_SystemsOS_Updater.desktop"
+ "org.gnome.Software.desktop"
+ "org.gnome.Geary.desktop"
+ "org.gnome.Contacts.desktop"
+ "org.gnome.Calendar.desktop"
+ "sparrow-desktop.desktop"
+ "Bisq.desktop"
+ "bisq2.desktop"
+ ];
+ welcome-dialog-last-shown-version = "48.4";
+ };
+
+ "org/gnome/shell/extensions/dash-to-dock" = {
+ background-color = "rgb(0,0,0)";
+ background-opacity = 0.5;
+ custom-background-color = true;
+ dash-max-icon-size = 47;
+ dock-position = "BOTTOM";
+ height-fraction = 0.9;
+ preferred-monitor = -2;
+ preferred-monitor-by-connector = "Virtual-1";
+ show-trash = false;
+ transparency-mode = "FIXED";
+ };
+
+ "org/gnome/shell/extensions/date-menu-formatter" = {
+ font-size = 12;
+ pattern = "EEEE, MMM d h:mm a";
+ text-align = "center";
+ update-level = 1;
+ };
+
+ "org/gnome/shell/extensions/just-perfection" = {
+ support-notifier-showed-version = 34;
+ support-notifier-type = 0;
+ };
+
+ "org/gnome/shell/extensions/pop-shell" = {
+ tile-by-default = true;
+ };
+
+ "org/gnome/shell/extensions/systemd-manager" = {
+ command-method = "systemctl";
+ systemd = [
+ "{\"name\":\"Bitcoind\",\"service\":\"bitcoind.service\",\"type\":\"system\"}"
+ "{\"name\":\"Electrs\",\"service\":\"electrs.service\",\"type\":\"system\"}"
+ "{\"name\":\"CLN\",\"service\":\"clightning.service\",\"type\":\"system\"}"
+ "{\"name\":\"LND\",\"service\":\"lnd.service\",\"type\":\"system\"}"
+ "{\"name\":\"Ride The Lightning\",\"service\":\"rtl.service\",\"type\":\"system\"}"
+ "{\"name\":\"BTCPayserver\",\"service\":\"btcpayserver.service\",\"type\":\"system\"}"
+ "{\"name\":\"Matrix-Synapse\",\"service\":\"matrix-synapse.service\",\"type\":\"system\"}"
+ "{\"name\":\"Coturn\",\"service\":\"coturn.service\",\"type\":\"system\"}"
+ "{\"name\":\"VaultWarden\",\"service\":\"vaultwarden.service\",\"type\":\"system\"}"
+ "{\"name\":\"Caddy\",\"service\":\"caddy.service\",\"type\":\"system\"}"
+ "{\"name\":\"Tor\",\"service\":\"tor.service\",\"type\":\"system\"}"
+ ];
+ };
+
+ "org/gnome/shell/extensions/vitals" = {
+ hot-sensors = [
+ "_storage_free_"
+ "_processor_usage_"
+ "_memory_usage_"
+ ];
+ };
+
+ "org/gnome/software" = {
+ first-run = false;
+ };
+
+ "org/gtk/gtk4/settings/color-chooser" = {
+ selected-color = mkTuple [ true 0.0 0.0 0.0 1.0 ];
+ };
+ };
+ }];
+}
\ No newline at end of file
diff --git a/modules/credentials-pdf.nix b/modules/credentials-pdf.nix
new file mode 100644
index 0000000..1bb54d2
--- /dev/null
+++ b/modules/credentials-pdf.nix
@@ -0,0 +1,234 @@
+{ config, pkgs, lib, ... }:
+
+{
+ # ── 1. Auto-Generate Root Password (Runs once) ─────────────
+ systemd.services.root-password-setup = {
+ description = "Generate and set a random root password";
+ wantedBy = [ "multi-user.target" ];
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ };
+ path = [ pkgs.pwgen pkgs.shadow pkgs.coreutils ];
+ script = ''
+ set -euo pipefail
+
+ SECRET_FILE="/var/lib/secrets/root-password"
+
+ if [ ! -f "$SECRET_FILE" ]; then
+ mkdir -p /var/lib/secrets
+ ROOT_PASS=$(pwgen -s 20 1)
+
+ # Apply the password to the root user
+ echo "root:$ROOT_PASS" | chpasswd
+
+ # Save it for the PDF generator to read
+ echo "$ROOT_PASS" > "$SECRET_FILE"
+ chmod 600 "$SECRET_FILE"
+ fi
+ '';
+ };
+
+ # ── 2. The Path Watcher (The Magic Trigger!) ───────────────
+ # This tells NixOS: "If any files inside these folders change,
+ # instantly run the generate-credentials-pdf service."
+ systemd.paths.generate-credentials-pdf-trigger = {
+ description = "Watch for new secret files to regenerate Magic Keys PDF";
+ wantedBy = [ "multi-user.target" ];
+ pathConfig = {
+ # Watch these directories for new passwords
+ PathChanged = [
+ "/var/lib/secrets"
+ "/var/lib/gnome-remote-desktop"
+ "/var/lib/domains"
+ "/etc/nix-bitcoin-secrets"
+ ];
+ # Watch for these specific Tor links to be generated
+ PathExists = [
+ "/var/lib/tor/onion/rtl/hostname"
+ "/var/lib/tor/onion/electrs/hostname"
+ "/var/lib/tor/onion/bitcoind/hostname"
+ ];
+ Unit = "generate-credentials-pdf.service";
+ };
+ };
+
+ # ── 3. Generate the Magic Keys PDF ─────────────────────────
+ systemd.services.generate-credentials-pdf = {
+ description = "Generate Magic Keys PDF for Sovran_SystemsOS";
+ # We remove RemainAfterExit so this service can be triggered over and over again!
+ serviceConfig = {
+ Type = "oneshot";
+ };
+
+ path = [ pkgs.pandoc pkgs.typst pkgs.coreutils ];
+
+ script = ''
+ set -euo pipefail
+
+ # Give it a tiny delay so multiple files being created at once don't trigger it 10 times in a row
+ sleep 3
+
+ DOC_DIR="/home/free/Documents"
+ mkdir -p "$DOC_DIR"
+ FILE="/tmp/magic_keys.md"
+
+ ROOT_PASS="Generating..."
+ if [ -f "/var/lib/secrets/root-password" ]; then
+ ROOT_PASS=$(cat /var/lib/secrets/root-password)
+ fi
+
+ cat << 'EOF' > "$FILE"
+# Your Sovran SystemsOS Magic Keys! 🗝️
+
+Welcome to your new computer! We have built a lot of cool secret forts (services) for you. To get into your forts, you need your magic keys (passwords).
+
+Here are all of your keys in one place. **Keep this document safe and do not share it with strangers!**
+
+## 🖥️ Your Computer
+These are the master keys to the actual machine.
+
+### 1. Main Screen Unlock (The 'free' account)
+When you turn the computer on, it usually logs you in automatically. However, if the screen goes to sleep, or **if you enable Remote Desktop (RDP)**, you will need this to log in:
+- **Username:** `free`
+- **Password:** `free`
+
+🚨 **VERY IMPORTANT:** You MUST write this password down and keep it safe! If you lose it, you will be locked out of your computer!
+EOF
+
+ cat << EOF >> "$FILE"
+
+### 2. The Big Boss (Root)
+Sometimes a pop-up box might ask for an Administrator (Root) password to change a setting. We created a super-secret password just for this!
+- **Root Password:** \`$ROOT_PASS\`
+EOF
+
+ cat << 'EOF' >> "$FILE"
+
+### 3. The Hacker Terminal (`ssh root@localhost`)
+Because your main account is so safe, you cannot just type normal commands to become the boss. If you open a black terminal box and want to make big changes, you must use your special factory key!
+
+Type this exact command into the terminal:
+`ssh root@localhost`
+
+When it asks for a passphrase, type:
+- **Terminal Password:** `gosovransystems`
+
+---
+EOF
+
+ # --- BITCOIN ECOSYSTEM ---
+ if [ -f "/etc/nix-bitcoin-secrets/rtl-password" ] || [ -f "/var/lib/tor/onion/rtl/hostname" ]; then
+ echo "## ⚡ Your Bitcoin & Lightning Node" >> "$FILE"
+ echo "Your computer is a real Bitcoin node! It talks to the network secretly using Tor. Here is how to connect your wallet apps to it:" >> "$FILE"
+
+ RTL_ONION="Not generated yet"
+ if [ -f "/var/lib/tor/onion/rtl/hostname" ]; then
+ RTL_ONION=$(cat /var/lib/tor/onion/rtl/hostname)
+ fi
+ RTL_PASS="Not found"
+ if [ -f "/etc/nix-bitcoin-secrets/rtl-password" ]; then
+ RTL_PASS=$(cat /etc/nix-bitcoin-secrets/rtl-password)
+ fi
+
+ ELECTRS_ONION="Not generated yet"
+ if [ -f "/var/lib/tor/onion/electrs/hostname" ]; then
+ ELECTRS_ONION=$(cat /var/lib/tor/onion/electrs/hostname)
+ fi
+
+ BITCOIN_ONION="Not generated yet"
+ if [ -f "/var/lib/tor/onion/bitcoind/hostname" ]; then
+ BITCOIN_ONION=$(cat /var/lib/tor/onion/bitcoind/hostname)
+ fi
+
+ cat << BITCOIN >> "$FILE"
+### 1. Ride The Lightning (RTL)
+*This is the control panel for your Lightning Node.*
+Open the **Tor Browser** and go to this website. Use this password to log in:
+- **Website:** \`http://$RTL_ONION\`
+- **Password:** \`$RTL_PASS\`
+
+### 2. Electrs (Your Private Bank Teller)
+*If you use a wallet app on your phone or computer (like Sparrow or BlueWallet), tell it to connect here so nobody can spy on your money!*
+- **Tor Address:** \`$ELECTRS_ONION\`
+- **Port:** \`50001\`
+
+### 3. Bitcoin Core
+*This is the heartbeat of your node. It uses this address to talk to other Bitcoiners securely.*
+- **Tor Address:** \`$BITCOIN_ONION\`
+
+---
+BITCOIN
+ fi
+
+ # --- MATRIX / ELEMENT ---
+ if [ -f "/var/lib/secrets/matrix-users" ]; then
+ echo "## 💬 Your Private Chat (Matrix / Element)" >> "$FILE"
+ echo "This is your very own private messaging app! We created an Admin account for you, and a Test account you can give to a friend to try it out. Log in using an app like Element with these details:" >> "$FILE"
+ echo '```text' >> "$FILE"
+ cat /var/lib/secrets/matrix-users >> "$FILE"
+ echo '```' >> "$FILE"
+ echo "---" >> "$FILE"
+ fi
+
+ # --- GNOME RDP ---
+ if [ -f "/var/lib/gnome-remote-desktop/rdp-credentials" ]; then
+ echo "## 🌎 Connect from Far Away (Remote Desktop)" >> "$FILE"
+ echo "This lets you control your computer screen from another device! Open your Remote Desktop app and type in these keys:" >> "$FILE"
+ echo '```text' >> "$FILE"
+ cat /var/lib/gnome-remote-desktop/rdp-credentials >> "$FILE"
+ echo '```' >> "$FILE"
+ echo "---" >> "$FILE"
+ fi
+
+ # --- NEXTCLOUD ---
+ if [ -f "/var/lib/secrets/nextcloud-admin" ]; then
+ echo "## ☁️ Your Personal Cloud (Nextcloud)" >> "$FILE"
+ echo "This is like your own private Google Drive! You can save photos and files here. Go to the URL below and use these keys:" >> "$FILE"
+ echo '```text' >> "$FILE"
+ cat /var/lib/secrets/nextcloud-admin >> "$FILE"
+ echo '```' >> "$FILE"
+ echo "---" >> "$FILE"
+ fi
+
+ # --- WORDPRESS ---
+ if [ -f "/var/lib/secrets/wordpress-admin" ]; then
+ echo "## 📝 Your Website (WordPress)" >> "$FILE"
+ echo "This is your very own website where you can write blogs or make pages. Go to the URL below to log in:" >> "$FILE"
+ echo '```text' >> "$FILE"
+ cat /var/lib/secrets/wordpress-admin >> "$FILE"
+ echo '```' >> "$FILE"
+ echo "---" >> "$FILE"
+ fi
+
+ # --- VAULTWARDEN ---
+ if [ -f "/var/lib/domains/vaultwarden" ]; then
+ DOMAIN=$(cat /var/lib/domains/vaultwarden)
+ echo "## 🔐 Your Password Manager (Vaultwarden)" >> "$FILE"
+ echo "This keeps all your other passwords safe! Go to this website to use it:" >> "$FILE"
+ echo "- **Website:** https://$DOMAIN" >> "$FILE"
+ echo "*(Note: You get to make up your own Master Password the very first time you visit this website!)*" >> "$FILE"
+ echo "---" >> "$FILE"
+ fi
+
+ # --- BTCPAY SERVER ---
+ if [ -f "/var/lib/domains/btcpayserver" ]; then
+ DOMAIN=$(cat /var/lib/domains/btcpayserver)
+ echo "## ₿ Your Bitcoin Store (BTCPay Server)" >> "$FILE"
+ echo "This lets you accept Bitcoin like a real shop! Go to this website to set it up:" >> "$FILE"
+ echo "- **Website:** https://$DOMAIN" >> "$FILE"
+ echo "*(Note: You get to make up your own Admin Password the very first time you visit this website!)*" >> "$FILE"
+ echo "---" >> "$FILE"
+ fi
+
+ # Convert the Markdown text into a beautiful PDF!
+ pandoc "$FILE" -o "$DOC_DIR/Sovran_SystemsOS_Magic_Keys.pdf" --pdf-engine=typst
+
+ # Make sure the 'free' user owns the file so they can open it
+ chown -R free:users "$DOC_DIR"
+
+ # Secure the markdown file
+ chmod 600 "$FILE"
+ '';
+ };
+}
diff --git a/modules/modules.nix b/modules/modules.nix
index 7529fda..efdafeb 100755
--- a/modules/modules.nix
+++ b/modules/modules.nix
@@ -9,10 +9,12 @@
./core/njalla.nix
./core/ssh-bootstrap.nix
./core/sovran-manage-domains.nix
+ ./core/sovran_systemsos-desktop.nix
# ── Always on (no flag) ───────────────────────────────────
./php.nix
./Sovran_SystemsOS_File_Fixes_And_New_Services.nix
+ ./credentials-pdf.nix
# ── Services (default ON — disable in custom.nix) ─────────
./synapse.nix
diff --git a/modules/synapse.nix b/modules/synapse.nix
index ae6d58f..8e407f4 100755
--- a/modules/synapse.nix
+++ b/modules/synapse.nix
@@ -139,6 +139,72 @@ EOF
systemd.services.matrix-synapse.after = [ "matrix-synapse-secret-init.service" ];
systemd.services.matrix-synapse.wants = [ "matrix-synapse-secret-init.service" ];
+
+
+ # ── Auto-generate Admin and Test users ──────────────────────
+ systemd.services.matrix-synapse-create-users = {
+ description = "Create Admin and Test users for Matrix Synapse";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "matrix-synapse.service" ];
+ requires = [ "matrix-synapse.service" ];
+ serviceConfig = {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ };
+ path = [ pkgs.pwgen pkgs.matrix-synapse pkgs.curl pkgs.coreutils pkgs.jq ];
+ script = ''
+ set -euo pipefail
+
+ # Wait for Synapse to be fully responsive
+ for i in {1..30}; do
+ if curl -s http://localhost:8008/_matrix/client/versions > /dev/null; then
+ break
+ fi
+ sleep 2
+ done
+
+ DOMAIN=$(cat /var/lib/domains/matrix)
+ CREDS_FILE="/var/lib/secrets/matrix-users"
+ SECRET=$(cat /var/lib/matrix-synapse/registration-secret)
+
+ # Only run if we haven't already generated the file
+ if [ ! -f "$CREDS_FILE" ]; then
+ mkdir -p /var/lib/secrets
+
+ ADMIN_USER="admin"
+ ADMIN_PASS=$(pwgen -s 24 1)
+
+ TEST_USER="test"
+ TEST_PASS=$(pwgen -s 24 1)
+
+ # Create Admin user
+ register_new_matrix_user -c /run/matrix-synapse/runtime-config.yaml \
+ -u "$ADMIN_USER" -p "$ADMIN_PASS" -a -S "$SECRET" http://localhost:8008
+
+ # Create Test user (non-admin)
+ register_new_matrix_user -c /run/matrix-synapse/runtime-config.yaml \
+ -u "$TEST_USER" -p "$TEST_PASS" --no-admin -S "$SECRET" http://localhost:8008
+
+ # Save the credentials
+ cat > "$CREDS_FILE" << CREDS
+Matrix (Element) Credentials
+════════════════════════════
+Homeserver URL: https://$DOMAIN
+
+[ Admin Account ]
+Username: @$ADMIN_USER:$DOMAIN
+Password: $ADMIN_PASS
+
+[ Test Account ]
+Username: @$TEST_USER:$DOMAIN
+Password: $TEST_PASS
+CREDS
+
+ chmod 600 "$CREDS_FILE"
+ echo "Matrix users created successfully."
+ fi
+ '';
+ };
sovran_systemsOS.domainRequirements = [
{ name = "matrix"; label = "Matrix Synapse"; example = "matrix.yourdomain.com"; }