#!/usr/bin/env bash set -euo pipefail # --- suppress colors from commands --- export NO_COLOR=1 export APT_COLOR=never export DEBIAN_FRONTEND=noninteractive # --- colored output for progress --- CYAN='\033[0;36m' NC='\033[0m' log() { echo -e "${CYAN}[*] $1${NC}"; } # --- must run as root --- if [[ "${EUID}" -ne 0 ]]; then echo "Run as root (e.g., sudo $0)" >&2 exit 1 fi # --- figure out the real (non-root) user to write configs for --- TARGET_USER="${SUDO_USER:-}" if [[ -z "${TARGET_USER}" || "${TARGET_USER}" == "root" ]]; then TARGET_USER="$(logname 2>/dev/null || true)" fi if [[ -z "${TARGET_USER}" || "${TARGET_USER}" == "root" ]]; then echo "Can't determine target non-root user. Run with sudo from your user." >&2 exit 1 fi TARGET_HOME="$(getent passwd "${TARGET_USER}" | cut -d: -f6)" if [[ -z "${TARGET_HOME}" || ! -d "${TARGET_HOME}" ]]; then echo "Can't resolve home for user '${TARGET_USER}'." >&2 exit 1 fi log "Target user: ${TARGET_USER}" log "Target home: ${TARGET_HOME}" # --- system update --- log "Updating system packages" apt-get update -qq apt-get upgrade -y -qq # --- X + i3 + tools --- log "Installing X, i3, and tools" apt-get install -y -qq \ xorg i3-wm i3status dmenu i3lock suckless-tools \ lightdm lightdm-gtk-greeter \ alacritty \ firefox \ ca-certificates curl gnupg wget # --- Sparrow (arm64 .deb) --- log "Downloading Sparrow Wallet" SPARROW_URL="https://github.com/sparrowwallet/sparrow/releases/download/2.3.1/sparrowwallet_2.3.1-1_arm64.deb" TMP_DEB="/tmp/sparrowwallet.deb" wget -q -O "${TMP_DEB}" "${SPARROW_URL}" # Create XDG menu infrastructure for xdg-desktop-menu (used in Sparrow's post-install) mkdir -p /etc/xdg/menus /usr/share/desktop-directories /usr/share/applications cat > /etc/xdg/menus/applications.menu <<'MENU' Applications MENU log "Installing Sparrow Wallet" apt-get install -y -qq "${TMP_DEB}" rm -f "${TMP_DEB}" # --- Configure Sparrow to use our electrs server --- log "Configuring Sparrow Wallet" SPARROW_DIR="${TARGET_HOME}/.sparrow" install -d -m 0755 -o "${TARGET_USER}" -g "${TARGET_USER}" "${SPARROW_DIR}" cat > "${SPARROW_DIR}/config" <<'EOF' { "mode": "ONLINE", "bitcoinUnit": "AUTO", "unitFormat": "DOT", "loadRecentWallets": true, "validateDerivationPaths": true, "groupByAddress": true, "includeMempoolOutputs": true, "notifyNewTransactions": true, "checkNewVersions": false, "theme": "LIGHT", "openWalletsInNewWindows": false, "hideEmptyUsedAddresses": false, "hideAmounts": false, "showTransactionHex": true, "showLoadingLog": true, "showAddressTransactionCount": false, "showDeprecatedImportExport": false, "signBsmsExports": false, "preventSleep": false, "dustAttackThreshold": 1000, "enumerateHwPeriod": 30, "mirrorCapture": true, "useZbar": true, "serverType": "ELECTRUM_SERVER", "electrumServer": "tcp://electrs.bitcoin.svc.cluster.local:50001", "useLegacyCoreWallet": false, "legacyServer": false, "useProxy": false, "autoSwitchProxy": true, "maxServerTimeout": 34, "maxPageSize": 100, "usePayNym": false, "mempoolFullRbf": false, "minRelayFeeRate": 1.0 } EOF chown "${TARGET_USER}:${TARGET_USER}" "${SPARROW_DIR}/config" # --- NetBird repo + install --- log "Adding NetBird repository" curl -sSL https://pkgs.netbird.io/debian/public.key \ | gpg --dearmor --output /usr/share/keyrings/netbird-archive-keyring.gpg echo 'deb [signed-by=/usr/share/keyrings/netbird-archive-keyring.gpg] https://pkgs.netbird.io/debian stable main' \ > /etc/apt/sources.list.d/netbird.list apt-get update -qq log "Installing NetBird" apt-get install -y -qq netbird # --- Enable graphical login (LightDM) --- log "Enabling graphical login" systemctl enable lightdm systemctl set-default graphical.target # dpkg-reconfigure is interactive; only run if a TTY is available if [[ -t 0 ]]; then dpkg-reconfigure lightdm else log "Skipping dpkg-reconfigure lightdm (no TTY)" fi # --- Configure LightDM greeter theme --- log "Configuring LightDM greeter" cat > /etc/lightdm/lightdm-gtk-greeter.css <<'EOF' entry { background: #F4EDE4; color: #3A2F2A; border: 2px solid #CBBBA7; border-radius: 4px; } entry:focus { border-color: #6B8BA4; box-shadow: 0 0 0 1px #6B8BA4; } EOF cat > /etc/lightdm/lightdm-gtk-greeter.conf <<'EOF' [greeter] background = #EDD3BD theme-name = Adwaita font-name = JetBrains Mono 11 hide-user-image = true clock-format = %H:%M indicators = extra-css = /etc/lightdm/lightdm-gtk-greeter.css EOF # --- Write user config files with correct ownership/perms --- log "Writing i3 configuration" install -d -m 0755 -o "${TARGET_USER}" -g "${TARGET_USER}" "${TARGET_HOME}/.config" I3_DIR="${TARGET_HOME}/.config/i3" install -d -m 0755 -o "${TARGET_USER}" -g "${TARGET_USER}" "${I3_DIR}" # Create NetBird config directory (avoids permission errors after overlayroot) install -d -m 0755 -o "${TARGET_USER}" -g "${TARGET_USER}" "${TARGET_HOME}/.config/netbird" install -m 0644 -o "${TARGET_USER}" -g "${TARGET_USER}" /dev/null "${I3_DIR}/config" cat > "${I3_DIR}/config" <<'EOF' # ========================= # i3 — minimal & warm # ========================= set $mod Mod4 font pango:JetBrains Mono 10 # ---------- Colors ---------- set $wallpaper #EDD3BD set $bg #F4EDE4 set $fg #3A2F2A set $accent #6B8BA4 set $inactive #CBBBA7 set $urgent #C96A5B client.focused $accent $accent $bg $accent $accent client.unfocused $inactive $inactive $fg $inactive $inactive client.urgent $urgent $urgent $bg $urgent $urgent # ---------- Gaps ---------- gaps inner 10 gaps outer 5 smart_gaps on # ---------- Workspaces ---------- set $ws1 "1:term" set $ws2 "2:sparrow" set $ws3 "3:web" set $ws4 "4:misc" # ---------- Keybindings ---------- bindsym $mod+Shift+q kill bindsym $mod+Shift+r restart bindsym $mod+Shift+e exit bindsym $mod+f fullscreen toggle bindsym $mod+Shift+Left move left bindsym $mod+Shift+Down move down bindsym $mod+Shift+Up move up bindsym $mod+Shift+Right move right bindsym $mod+t workspace $ws1 bindsym $mod+s workspace $ws2 bindsym $mod+w workspace $ws3 bindsym $mod+o workspace $ws4 # ---------- App rules ---------- # Terminal assign [class="Alacritty"] $ws1 for_window [class="Alacritty"] fullscreen enable # Sparrow assign [class="Sparrow"] $ws2 for_window [class="Sparrow"] fullscreen enable # Firefox assign [class="firefox_firefox"] $ws3 for_window [class="firefox_firefox"] fullscreen enable # Anything not explicitly matched goes to misc assign [class="^(?!Alacritty|Sparrow|firefox_firefox).*"] $ws4 # ---------- Autostart ---------- exec --no-startup-id alacritty exec --no-startup-id /opt/sparrowwallet/bin/Sparrow exec --no-startup-id firefox -new-instance -no-remote https://bitcoin.org # Start focused on terminal exec --no-startup-id i3-msg workspace $ws1 EOF chown "${TARGET_USER}:${TARGET_USER}" "${I3_DIR}/config" # helper scripts in home (owned by user) log "Writing netbird.sh helper script" install -m 0755 -o "${TARGET_USER}" -g "${TARGET_USER}" /dev/null "${TARGET_HOME}/netbird.sh" cat > "${TARGET_HOME}/netbird.sh" <<'EOF' #!/usr/bin/env bash set -euo pipefail netbird up --management-url https://network.runar.lucaconlaq.io EOF chown "${TARGET_USER}:${TARGET_USER}" "${TARGET_HOME}/netbird.sh" # --- Clear NetBird state so it regenerates keys on each boot --- # This ensures the system is truly stateless - each boot will require SSO authentication log "Clearing NetBird state for stateless operation" systemctl stop netbird 2>/dev/null || true rm -rf /var/lib/netbird /etc/netbird # --- Enable OverlayFS "freeze on reboot" for root filesystem --- # NOTE: After this is enabled, changes under / (including apt installs, /etc edits, logs, etc.) # will be discarded on every reboot. /home is typically on the same root FS, so its changes # will ALSO be discarded unless you move /home to a separate persistent partition. log "Installing overlayroot" apt-get install -y -qq overlayroot log "Configuring overlayroot" cat > /etc/overlayroot.conf <<'EOF' overlayroot="tmpfs" overlayroot_cfgdisk="disabled" EOF log "Updating initramfs" update-initramfs -u log "Installation complete - rebooting in 5 seconds" sleep 5 reboot