From d14e25c29fff3595c3b73803d6dafbb93c18189f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 13:58:07 +0000 Subject: [PATCH 1/2] Initial plan From f80c8a0481125e4df163255f4bfb5a2be09ed94e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 7 Apr 2026 14:23:59 +0000 Subject: [PATCH 2/2] Factory security: per-device SSH passphrase, factory seal, password onboarding, remove PDF generator Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/4222f228-615c-4303-8286-979264c6f782 Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com> --- iso/installer.py | 104 +++++++++- modules/core/factory-seal.nix | 115 ++++++++++++ modules/core/sovran-hub.nix | 2 +- modules/core/ssh-bootstrap.nix | 23 ++- modules/credentials-pdf.nix | 333 +-------------------------------- modules/modules.nix | 1 + 6 files changed, 244 insertions(+), 334 deletions(-) create mode 100644 modules/core/factory-seal.nix diff --git a/iso/installer.py b/iso/installer.py index 8cdaf58..5aef89d 100644 --- a/iso/installer.py +++ b/iso/installer.py @@ -930,7 +930,107 @@ class InstallerWindow(Adw.ApplicationWindow): path = os.path.join(nixos_dir, entry) run(["sudo", "rm", "-rf", path]) - GLib.idle_add(self.push_complete) + GLib.idle_add(self.push_create_password) + + # ── Step 5b: Create Password ────────────────────────────────────────── + + def push_create_password(self): + outer = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=0) + + status = Adw.StatusPage() + status.set_title("Create Your Password") + status.set_description( + "Choose a password for your 'free' user account. " + "This will be your login password." + ) + status.set_vexpand(True) + + form_group = Adw.PreferencesGroup() + form_group.set_margin_start(40) + form_group.set_margin_end(40) + + pw_row = Adw.PasswordEntryRow() + pw_row.set_title("Password") + form_group.add(pw_row) + + confirm_row = Adw.PasswordEntryRow() + confirm_row.set_title("Confirm Password") + form_group.add(confirm_row) + + error_lbl = Gtk.Label() + error_lbl.set_margin_start(40) + error_lbl.set_margin_end(40) + error_lbl.set_margin_top(8) + error_lbl.set_visible(False) + error_lbl.add_css_class("error") + + content_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=16) + content_box.append(status) + content_box.append(form_group) + content_box.append(error_lbl) + outer.append(content_box) + + def on_submit(btn): + password = pw_row.get_text() + confirm = confirm_row.get_text() + + if not password: + error_lbl.set_text("Password cannot be empty.") + error_lbl.set_visible(True) + return + if len(password) < 8: + error_lbl.set_text("Password must be at least 8 characters.") + error_lbl.set_visible(True) + return + if password != confirm: + error_lbl.set_text("Passwords do not match.") + error_lbl.set_visible(True) + return + + btn.set_sensitive(False) + error_lbl.set_visible(False) + + try: + run(["sudo", "mkdir", "-p", "/mnt/var/lib/secrets"]) + proc = subprocess.run( + ["sudo", "tee", "/mnt/var/lib/secrets/free-password"], + input=password, text=True, capture_output=True + ) + if proc.returncode != 0: + raise RuntimeError(proc.stderr.strip() or "Failed to write password file") + run(["sudo", "chmod", "600", "/mnt/var/lib/secrets/free-password"]) + + proc = subprocess.run( + ["sudo", "chroot", "/mnt", "chpasswd"], + input=f"free:{password}", + capture_output=True, text=True + ) + if proc.returncode != 0: + raise RuntimeError(proc.stderr.strip() or "Failed to set password in chroot") + + run(["sudo", "touch", "/mnt/var/lib/sovran-customer-onboarded"]) + except Exception as e: + error_lbl.set_text(str(e)) + error_lbl.set_visible(True) + btn.set_sensitive(True) + return + + GLib.idle_add(self.push_complete) + + submit_btn = Gtk.Button(label="Set Password & Continue") + submit_btn.add_css_class("suggested-action") + submit_btn.add_css_class("pill") + submit_btn.connect("clicked", on_submit) + + nav = Gtk.Box() + nav.set_margin_bottom(24) + nav.set_margin_end(40) + nav.set_halign(Gtk.Align.END) + nav.append(submit_btn) + outer.append(nav) + + self.push_page("Create Password", outer) + return False # ── Step 6: Complete ─────────────────────────────────────────────────── @@ -954,7 +1054,7 @@ class InstallerWindow(Adw.ApplicationWindow): pass_row = Adw.ActionRow() pass_row.set_title("Password") - pass_row.set_subtitle("free") + pass_row.set_subtitle("The password you just created") creds_group.add(pass_row) note_row = Adw.ActionRow() diff --git a/modules/core/factory-seal.nix b/modules/core/factory-seal.nix new file mode 100644 index 0000000..781d010 --- /dev/null +++ b/modules/core/factory-seal.nix @@ -0,0 +1,115 @@ +{ config, pkgs, lib, ... }: + +let + sovran-factory-seal = pkgs.writeShellScriptBin "sovran-factory-seal" '' + set -euo pipefail + + if [ "$(id -u)" -ne 0 ]; then + echo "Error: must be run as root." >&2 + exit 1 + fi + + echo "" + echo "╔══════════════════════════════════════════════════════════════╗" + echo "║ ⚠ SOVRAN FACTORY SEAL — WARNING ⚠ ║" + echo "╠══════════════════════════════════════════════════════════════╣" + echo "║ This command will PERMANENTLY DELETE: ║" + echo "║ • All generated passwords and secrets ║" + echo "║ • LND wallet data (seed words, channels, macaroons) ║" + echo "║ • SSH factory login key ║" + echo "║ • Application databases (Matrix, Nextcloud, WordPress) ║" + echo "║ • Vaultwarden database ║" + echo "║ ║" + echo "║ After sealing, all credentials will be regenerated fresh ║" + echo "║ when the customer boots the device for the first time. ║" + echo "║ ║" + echo "║ DO NOT run this on a customer's live system. ║" + echo "╚══════════════════════════════════════════════════════════════╝" + echo "" + echo -n "Type SEAL to confirm: " + read -r CONFIRM + if [ "$CONFIRM" != "SEAL" ]; then + echo "Aborted." >&2 + exit 1 + fi + + echo "" + echo "Sealing system..." + + # ── 1. Delete all generated secrets ────────────────────────────── + echo " Wiping secrets..." + [ -d /var/lib/secrets ] && find /var/lib/secrets -mindepth 1 -delete || true + rm -rf /var/lib/matrix-synapse/registration-secret + rm -rf /var/lib/matrix-synapse/db-password + rm -rf /var/lib/gnome-remote-desktop/rdp-password + rm -rf /var/lib/gnome-remote-desktop/rdp-username + rm -rf /var/lib/gnome-remote-desktop/rdp-credentials + rm -rf /var/lib/livekit/livekit_keyFile + rm -rf /etc/nix-bitcoin-secrets/* + + # ── 2. Wipe LND wallet (seed words, wallet DB, macaroons) ──────── + echo " Wiping LND wallet data..." + rm -rf /var/lib/lnd/* + + # ── 3. Wipe SSH factory key so it regenerates with new passphrase ─ + echo " Removing SSH factory key..." + rm -f /home/free/.ssh/factory_login /home/free/.ssh/factory_login.pub + if [ -f /root/.ssh/authorized_keys ]; then + sed -i '/factory_login/d' /root/.ssh/authorized_keys + fi + + # ── 4. Drop application databases ──────────────────────────────── + echo " Dropping application databases..." + sudo -u postgres psql -c "DROP DATABASE IF EXISTS \"matrix-synapse\";" 2>/dev/null || true + sudo -u postgres psql -c "DROP DATABASE IF EXISTS nextclouddb;" 2>/dev/null || true + mysql -u root -e "DROP DATABASE IF EXISTS wordpressdb;" 2>/dev/null || true + + # ── 5. Remove application config files (so init services re-run) ─ + echo " Removing application config files..." + rm -rf /var/lib/www/wordpress/wp-config.php + rm -rf /var/lib/www/nextcloud/config/config.php + + # ── 6. Wipe Vaultwarden database ────────────────────────────────── + echo " Wiping Vaultwarden data..." + rm -rf /var/lib/bitwarden_rs/* + rm -rf /var/lib/vaultwarden/* + + # ── 7. Set sealed flag and remove onboarded flag ───────────────── + echo " Setting sealed flag..." + touch /var/lib/sovran-factory-sealed + rm -f /var/lib/sovran-customer-onboarded + + echo "" + echo "System sealed. Power off now or the system will shut down in 10 seconds." + sleep 10 + poweroff + ''; + +in +{ + environment.systemPackages = [ sovran-factory-seal ]; + + # ── Legacy security check: warn existing (pre-seal) machines ─────── + systemd.services.sovran-legacy-security-check = { + description = "Check for legacy (pre-factory-seal) security status"; + wantedBy = [ "multi-user.target" ]; + after = [ "local-fs.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + path = [ pkgs.coreutils ]; + script = '' + # If already onboarded or sealed, nothing to do + [ -f /var/lib/sovran-customer-onboarded ] && exit 0 + [ -f /var/lib/sovran-factory-sealed ] && exit 0 + + # If secrets exist but no sealed/onboarded flag, this is a legacy machine + if [ -f /var/lib/secrets/root-password ]; then + mkdir -p /var/lib/sovran + echo "legacy" > /var/lib/sovran/security-status + echo "This system was deployed before the factory seal feature. Your passwords may be known to the factory. Please change your passwords through the Sovran Hub." > /var/lib/sovran/security-warning + fi + ''; + }; +} diff --git a/modules/core/sovran-hub.nix b/modules/core/sovran-hub.nix index 01db940..b2927fa 100644 --- a/modules/core/sovran-hub.nix +++ b/modules/core/sovran-hub.nix @@ -10,7 +10,7 @@ let { label = "Free Account — Username"; value = "free"; } { label = "Free Account — Password"; file = "/var/lib/secrets/free-password"; } { label = "Root Password"; file = "/var/lib/secrets/root-password"; } - { label = "SSH Local Access"; value = "ssh root@localhost / Passphrase: gosovransystems"; } + { label = "SSH Passphrase"; file = "/var/lib/secrets/ssh-passphrase"; } ]; } ] # ── Infrastructure — Caddy + Tor (NOT desktop-only) ──────── diff --git a/modules/core/ssh-bootstrap.nix b/modules/core/ssh-bootstrap.nix index 1a4e48d..e88dcd8 100644 --- a/modules/core/ssh-bootstrap.nix +++ b/modules/core/ssh-bootstrap.nix @@ -12,9 +12,29 @@ lib.mkIf userExists { "d /home/${userName}/.ssh 0700 ${userName} users -" ]; + systemd.services.ssh-passphrase-setup = { + description = "Generate per-device SSH key passphrase"; + wantedBy = [ "multi-user.target" ]; + before = [ "factory-ssh-keygen.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + path = [ pkgs.pwgen pkgs.coreutils ]; + script = '' + if [ ! -f "/var/lib/secrets/ssh-passphrase" ]; then + mkdir -p /var/lib/secrets + pwgen -s 20 1 > /var/lib/secrets/ssh-passphrase + chmod 600 /var/lib/secrets/ssh-passphrase + fi + ''; + }; + systemd.services.factory-ssh-keygen = { description = "Generate factory SSH key for ${userName} if missing"; wantedBy = [ "multi-user.target" ]; + after = [ "ssh-passphrase-setup.service" ]; + requires = [ "ssh-passphrase-setup.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; @@ -22,7 +42,8 @@ lib.mkIf userExists { path = [ pkgs.openssh pkgs.coreutils ]; script = '' if [ ! -f "${keyPath}" ]; then - ssh-keygen -q -N "gosovransystems" -t ed25519 -f "${keyPath}" + PASSPHRASE=$(cat /var/lib/secrets/ssh-passphrase) + ssh-keygen -q -N "$PASSPHRASE" -t ed25519 -f "${keyPath}" chown ${userName}:users "${keyPath}" "${keyPath}.pub" chmod 600 "${keyPath}" chmod 644 "${keyPath}.pub" diff --git a/modules/credentials-pdf.nix b/modules/credentials-pdf.nix index 09aa04b..1976ed1 100644 --- a/modules/credentials-pdf.nix +++ b/modules/credentials-pdf.nix @@ -1,8 +1,6 @@ { config, pkgs, lib, ... }: let - fonts = pkgs.liberation_ttf; - # ── Helper: change 'free' password and save it ───────────── change-free-password = pkgs.writeShellScriptBin "change-free-password" '' set -euo pipefail @@ -50,7 +48,7 @@ in echo "║ ⚠ Use 'sudo change-free-password' instead. ║" echo "║ ║" echo "║ 'passwd free' only updates /etc/shadow. ║" - echo "║ The Hub and Magic Keys PDF will NOT be updated. ║" + echo "║ The Hub credentials view will NOT be updated. ║" echo "╚══════════════════════════════════════════════════════╝" echo "" return 1 @@ -67,7 +65,7 @@ in echo "║ ⚠ Use 'sudo change-free-password' instead. ║" echo "║ ║" echo "║ 'passwd free' only updates /etc/shadow. ║" - echo "║ The Hub and Magic Keys PDF will NOT be updated. ║" + echo "║ The Hub credentials view will NOT be updated. ║" echo "╚════════════════════════════��═════════════════════════╝" echo "" return 1 @@ -116,329 +114,4 @@ in ''; }; - # ── 1c. Save Zeus/lndconnect URL for hub credentials ──────── - systemd.services.zeus-connect-setup = { - description = "Save Zeus lndconnect URL"; - wantedBy = [ "multi-user.target" ]; - after = [ "lnd.service" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - path = [ pkgs.coreutils "/run/current-system/sw" ]; - script = '' - SECRET_FILE="/var/lib/secrets/zeus-connect-url" - mkdir -p /var/lib/secrets - - URL="" - if command -v lndconnect >/dev/null 2>&1; then - URL=$(lndconnect --url 2>/dev/null || true) - elif command -v lnconnect-clnrest >/dev/null 2>&1; then - URL=$(lnconnect-clnrest --url 2>/dev/null || true) - fi - - if [ -n "$URL" ]; then - echo "$URL" > "$SECRET_FILE" - chmod 600 "$SECRET_FILE" - echo "Zeus connect URL saved." - else - echo "No lndconnect URL available yet." - fi - ''; - }; - - # ── Refresh Zeus URL periodically (certs/macaroons may rotate) - systemd.timers.zeus-connect-setup = { - wantedBy = [ "timers.target" ]; - timerConfig = { - OnBootSec = "2min"; - OnUnitActiveSec = "30min"; - Unit = "zeus-connect-setup.service"; - }; - }; - - # ── 2. Timer: Check every 5 minutes ──────────────────────── - systemd.timers.generate-credentials-pdf = { - description = "Periodically check if Magic Keys PDF needs regenerating"; - wantedBy = [ "timers.target" ]; - timerConfig = { - OnBootSec = "30s"; - OnUnitActiveSec = "5min"; - Unit = "generate-credentials-pdf.service"; - }; - }; - - # ── 3. Generate the Magic Keys PDF ───────────────────────── - systemd.services.generate-credentials-pdf = { - description = "Generate Magic Keys PDF for Sovran_SystemsOS"; - serviceConfig = { - Type = "oneshot"; - }; - - path = [ - pkgs.pandoc - pkgs.typst - pkgs.coreutils - pkgs.qrencode - pkgs.gnugrep - fonts - "/run/current-system/sw" - ]; - - environment = { - TYPST_FONT_PATHS = "${fonts}/share/fonts"; - }; - - script = '' - DOC_DIR="/home/free/Documents" - OUTPUT="$DOC_DIR/Sovran_SystemsOS_Magic_Keys.pdf" - WORK_DIR="/tmp/magic_keys_build" - FILE="$WORK_DIR/magic_keys.md" - HASH_FILE="/var/lib/secrets/.magic-keys-hash" - - FENCE='```' - - # ── Collect all secret sources into a single hash ── - SECRET_SOURCES="" - for f in \ - /var/lib/secrets/root-password \ - /var/lib/secrets/free-password \ - /etc/nix-bitcoin-secrets/rtl-password \ - /var/lib/tor/onion/rtl/hostname \ - /var/lib/tor/onion/electrs/hostname \ - /var/lib/tor/onion/bitcoind/hostname \ - /var/lib/secrets/matrix-users \ - /var/lib/gnome-remote-desktop/rdp-credentials \ - /var/lib/secrets/nextcloud-admin \ - /var/lib/secrets/wordpress-admin \ - /var/lib/secrets/vaultwarden/vaultwarden.env \ - /var/lib/domains/vaultwarden \ - /var/lib/domains/btcpayserver \ - /var/lib/secrets/zeus-connect-url; do - if [ -f "$f" ]; then - SECRET_SOURCES="$SECRET_SOURCES$(cat "$f")" - fi - done - - # Add lndconnect URL to hash sources (changes if certs/macaroons rotate) - if command -v lndconnect >/dev/null 2>&1; then - SECRET_SOURCES="$SECRET_SOURCES$(lndconnect --url 2>/dev/null || true)" - elif command -v lnconnect-clnrest >/dev/null 2>&1; then - SECRET_SOURCES="$SECRET_SOURCES$(lnconnect-clnrest --url 2>/dev/null || true)" - fi - - CURRENT_HASH=$(echo -n "$SECRET_SOURCES" | sha256sum | cut -d' ' -f1) - OLD_HASH="" - if [ -f "$HASH_FILE" ]; then - OLD_HASH=$(cat "$HASH_FILE") - fi - - # ── Skip if PDF exists and nothing changed ── - if [ -f "$OUTPUT" ] && [ "$CURRENT_HASH" = "$OLD_HASH" ]; then - echo "No changes detected, skipping PDF regeneration." - exit 0 - fi - - echo "Changes detected (or PDF missing), regenerating..." - mkdir -p "$DOC_DIR" "$WORK_DIR" - - # ── Read secrets (default to placeholder if missing) ── - read_secret() { if [ -f "$1" ]; then cat "$1"; else echo "$2"; fi; } - - ROOT_PASS=$(read_secret /var/lib/secrets/root-password "Generating...") - FREE_PASS=$(read_secret /var/lib/secrets/free-password "free") - RTL_PASS=$(read_secret /etc/nix-bitcoin-secrets/rtl-password "Not found") - RTL_ONION=$(read_secret /var/lib/tor/onion/rtl/hostname "Not generated yet") - ELECTRS_ONION=$(read_secret /var/lib/tor/onion/electrs/hostname "Not generated yet") - BITCOIN_ONION=$(read_secret /var/lib/tor/onion/bitcoind/hostname "Not generated yet") - - # ── Generate Zeus QR code PNG if lndconnect URL is available ── - ZEUS_URL="" - HAS_ZEUS_QR="" - if command -v lndconnect >/dev/null 2>&1; then - ZEUS_URL=$(lndconnect --url 2>/dev/null || true) - elif command -v lnconnect-clnrest >/dev/null 2>&1; then - ZEUS_URL=$(lnconnect-clnrest --url 2>/dev/null || true) - fi - - if [ -n "$ZEUS_URL" ]; then - qrencode -o "$WORK_DIR/zeus-qr.png" -s 4 -m 1 -l H "$ZEUS_URL" 2>/dev/null && HAS_ZEUS_QR="1" - fi - - # ── Build the Markdown document ── - cat > "$FILE" << ENDOFFILE ---- -title: "Sovran SystemsOS Magic Keys" ---- - -# 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!** - -> **How this document works:** This PDF is automatically generated by your computer. If any of your passwords, services, or connection details change, this document will automatically update itself within a few minutes. You can always find the latest version right here in your Documents folder. If you accidentally delete it, don't worry — your computer will recreate it for you! - -## 🖥️ 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_PASS\` - -🚨 **VERY IMPORTANT:** You MUST write this password down and keep it safe! If you lose it, you will be locked out of your computer! - -### 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\` - -### 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\` -ENDOFFILE - - # --- BITCOIN ECOSYSTEM --- - if [ -f "/etc/nix-bitcoin-secrets/rtl-password" ] || [ -f "/var/lib/tor/onion/rtl/hostname" ]; then - cat >> "$FILE" << BITCOIN - -## ⚡ Your Bitcoin & Lightning Node -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: - -### 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 - - # --- ZEUS MOBILE WALLET QR CODE --- - if [ "$HAS_ZEUS_QR" = "1" ]; then - echo "" >> "$FILE" - echo "## 📱 Connect Zeus Mobile Wallet" >> "$FILE" - echo "" >> "$FILE" - echo "Take your Bitcoin Lightning node anywhere in the world! Scan this QR code with the **Zeus** app on your phone to instantly connect your mobile wallet to your Lightning node." >> "$FILE" - echo "" >> "$FILE" - echo "1. Download **Zeus** from the App Store or Google Play" >> "$FILE" - echo "2. Open Zeus and tap **\"Scan Node Config\"**" >> "$FILE" - echo "3. Point your phone's camera at this QR code:" >> "$FILE" - echo "" >> "$FILE" - echo "![Zeus Connection QR Code](zeus-qr.png){ width=200px }" >> "$FILE" - echo "" >> "$FILE" - echo "That's it! You're now mobile. Send and receive Bitcoin anywhere in the world, powered by your very own node! ⚡" >> "$FILE" - elif [ -n "$ZEUS_URL" ]; then - echo "" >> "$FILE" - echo "## 📱 Connect Zeus Mobile Wallet" >> "$FILE" - echo "" >> "$FILE" - echo "Take your Bitcoin Lightning node anywhere in the world! Paste this connection URL into the **Zeus** app on your phone:" >> "$FILE" - echo "" >> "$FILE" - echo "1. Download **Zeus** from the App Store or Google Play" >> "$FILE" - echo "2. Open Zeus and tap **\"Scan Node Config\"** then **\"Paste Node Config\"**" >> "$FILE" - echo "3. Paste this URL:" >> "$FILE" - echo "" >> "$FILE" - echo "$FENCE" >> "$FILE" - echo "$ZEUS_URL" >> "$FILE" - echo "$FENCE" >> "$FILE" - echo "" >> "$FILE" - echo "That's it! You're now mobile. Send and receive Bitcoin anywhere in the world, powered by your very own node! ⚡" >> "$FILE" - fi - - # --- MATRIX / ELEMENT --- - if [ -f "/var/lib/secrets/matrix-users" ]; then - echo "" >> "$FILE" - echo "## 💬 Your Private Chat (Matrix / Element)" >> "$FILE" - echo "This is your very own private messaging app! Log in using an app like Element with these details:" >> "$FILE" - echo "$FENCE" >> "$FILE" - cat /var/lib/secrets/matrix-users >> "$FILE" - echo "$FENCE" >> "$FILE" - fi - - # --- GNOME RDP --- - if [ -f "/var/lib/gnome-remote-desktop/rdp-credentials" ]; then - echo "" >> "$FILE" - echo "## 🌎 Connect from Far Away (Remote Desktop)" >> "$FILE" - echo "This lets you control your computer screen from another device!" >> "$FILE" - echo "$FENCE" >> "$FILE" - cat /var/lib/gnome-remote-desktop/rdp-credentials >> "$FILE" - echo "$FENCE" >> "$FILE" - fi - - # --- NEXTCLOUD --- - if [ -f "/var/lib/secrets/nextcloud-admin" ]; then - echo "" >> "$FILE" - echo "## ☁️ Your Personal Cloud (Nextcloud)" >> "$FILE" - echo "This is like your own private Google Drive!" >> "$FILE" - echo "$FENCE" >> "$FILE" - cat /var/lib/secrets/nextcloud-admin >> "$FILE" - echo "$FENCE" >> "$FILE" - fi - - # --- WORDPRESS --- - if [ -f "/var/lib/secrets/wordpress-admin" ]; then - echo "" >> "$FILE" - echo "## 📝 Your Website (WordPress)" >> "$FILE" - echo "This is your very own website where you can write blogs or make pages." >> "$FILE" - echo "$FENCE" >> "$FILE" - cat /var/lib/secrets/wordpress-admin >> "$FILE" - echo "$FENCE" >> "$FILE" - fi - - # --- VAULTWARDEN --- - if [ -f "/var/lib/domains/vaultwarden" ]; then - DOMAIN=$(cat /var/lib/domains/vaultwarden) - VW_ADMIN_TOKEN="Not found" - if [ -f "/var/lib/secrets/vaultwarden/vaultwarden.env" ]; then - VW_ADMIN_TOKEN=$(grep -oP 'ADMIN_TOKEN=\K.*' /var/lib/secrets/vaultwarden/vaultwarden.env || echo "Not found") - fi - echo "" >> "$FILE" - 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 "- **Admin Panel:** https://$DOMAIN/admin" >> "$FILE" - echo "- **Admin Token:** \`$VW_ADMIN_TOKEN\`" >> "$FILE" - echo "" >> "$FILE" - echo "*(Create your own account on the main page. Use the Admin Token to access the admin panel and manage your server.)*" >> "$FILE" - fi - - # --- BTCPAY SERVER --- - if [ -f "/var/lib/domains/btcpayserver" ]; then - DOMAIN=$(cat /var/lib/domains/btcpayserver) - echo "" >> "$FILE" - echo "## ₿ Your Bitcoin Store (BTCPay Server)" >> "$FILE" - echo "This lets you accept Bitcoin like a real shop!" >> "$FILE" - echo "- **Website:** https://$DOMAIN" >> "$FILE" - echo "*(You make up your own Admin Password the first time you visit!)*" >> "$FILE" - fi - - # ── Generate PDF (cd into work dir so Typst finds images) ── - cd "$WORK_DIR" - pandoc magic_keys.md -o "$OUTPUT" --pdf-engine=typst \ - -V mainfont="Liberation Sans" \ - -V monofont="Liberation Mono" - - chown free:users "$OUTPUT" - - # ── Save hash so we skip next time if nothing changed ── - mkdir -p "$(dirname "$HASH_FILE")" - echo "$CURRENT_HASH" > "$HASH_FILE" - - rm -rf "$WORK_DIR" - echo "PDF generated successfully." - ''; - }; -} \ No newline at end of file +} diff --git a/modules/modules.nix b/modules/modules.nix index a8eb10f..794bdaa 100755 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -12,6 +12,7 @@ ./core/sovran_systemsos-desktop.nix ./core/sshd-localhost.nix ./core/sovran-hub.nix + ./core/factory-seal.nix # ── Always on (no flag) ─────────────────────────────────── ./php.nix