Commit 9b8a3c191ad2

Vincent Demeester <vincent@sbr.pm>
2026-06-10 11:12:22
feat(aomi): add system-manager config and bootstrap for Fedora CSB
Added system-manager configuration for aomi (ThinkPad P1) running Fedora CSB with WireGuard VPN and Syncthing services. Included bootstrap script using Determinate Nix installer and documentation for the setup workflow.
1 parent 167ed30
Changed files (4)
imperative/aomi/bootstrap.sh
@@ -0,0 +1,161 @@
+#!/usr/bin/env bash
+
+# Aomi (Fedora CSB) Bootstrap Script
+# Description: Installs Nix (Determinate) and deploys system-manager config
+#
+# Prerequisites: Fedora CSB installed, user has sudo access
+# Usage: ./bootstrap.sh
+#   Or remotely: ssh vincent@aomi.home 'bash -s' < imperative/aomi/bootstrap.sh
+
+set -euo pipefail
+
+readonly GREEN='\033[0;32m'
+readonly YELLOW='\033[1;33m'
+readonly RED='\033[0;31m'
+readonly NC='\033[0m'
+
+REPO_URL="${REPO_URL:-https://git.sbr.pm/home.git}"
+REPO_PATH="${REPO_PATH:-$HOME/src/home}"
+SYSTEM_CONFIG="${SYSTEM_CONFIG:-aomi}"
+
+log_info()  { echo -e "${GREEN}[INFO]${NC} $*"; }
+log_warn()  { echo -e "${YELLOW}[WARN]${NC} $*"; }
+log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
+
+check_root() {
+	if [[ $EUID -eq 0 ]]; then
+		log_error "Run as your regular user, not root (sudo is used when needed)"
+		exit 1
+	fi
+}
+
+# --- Phase 1: Nix ---
+
+install_nix() {
+	if command -v nix &>/dev/null; then
+		log_info "Nix already installed: $(nix --version)"
+		return 0
+	fi
+
+	log_info "Installing Nix (Determinate Systems installer)..."
+	curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
+
+	# Source nix for this session
+	if [[ -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]]; then
+		# shellcheck disable=SC1091
+		. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
+	fi
+
+	log_info "Nix installed: $(nix --version)"
+}
+
+configure_nix() {
+	local nix_conf="${XDG_CONFIG_HOME:-$HOME/.config}/nix/nix.conf"
+	mkdir -p "$(dirname "$nix_conf")"
+
+	# Determinate installer enables flakes by default, but ensure our preferences
+	if [[ ! -f "$nix_conf" ]] || ! grep -q 'use-xdg-base-directories' "$nix_conf" 2>/dev/null; then
+		cat > "$nix_conf" <<-'EOF'
+		experimental-features = nix-command flakes
+		use-xdg-base-directories = true
+		EOF
+		log_info "Nix config written to $nix_conf"
+	else
+		log_info "Nix config already exists"
+	fi
+}
+
+# --- Phase 2: Repository ---
+
+clone_repo() {
+	if [[ -d "$REPO_PATH/.git" ]]; then
+		log_info "Repository exists at $REPO_PATH, pulling..."
+		git -C "$REPO_PATH" pull --ff-only || log_warn "Pull failed, continuing with existing"
+		return 0
+	fi
+
+	log_info "Cloning $REPO_URL → $REPO_PATH"
+	mkdir -p "$(dirname "$REPO_PATH")"
+	git clone "$REPO_URL" "$REPO_PATH"
+}
+
+# --- Phase 3: System-manager ---
+
+build_and_activate() {
+	log_info "Building system-manager config: $SYSTEM_CONFIG"
+
+	# Ensure nix is in PATH
+	if ! command -v nix &>/dev/null; then
+		# shellcheck disable=SC1091
+		. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
+	fi
+
+	cd "$REPO_PATH"
+
+	local system_path
+	system_path=$(nix build ".#systemConfigs.${SYSTEM_CONFIG}" --no-link --print-out-paths)
+
+	log_info "Built: $system_path"
+	log_info "Activating system-manager..."
+	sudo "${system_path}/bin/activate"
+
+	log_info "System-manager activated!"
+}
+
+# --- Phase 4: WireGuard key ---
+
+setup_wireguard_key() {
+	if [[ -f /etc/wireguard/private.key ]]; then
+		log_info "WireGuard private key already exists"
+		return 0
+	fi
+
+	log_warn "WireGuard private key not found at /etc/wireguard/private.key"
+	log_warn ""
+	log_warn "To set up WireGuard, either:"
+	log_warn "  1. Copy existing key:  sudo cp /path/to/backup/private.key /etc/wireguard/"
+	log_warn "  2. Generate new key:   wg genkey | sudo tee /etc/wireguard/private.key"
+	log_warn "     Then update globals.nix with new pubkey: sudo cat /etc/wireguard/private.key | wg pubkey"
+	log_warn ""
+	log_warn "After placing the key:  sudo chmod 600 /etc/wireguard/private.key"
+	log_warn "Then re-run:  sudo systemctl restart wireguard-wg0"
+}
+
+# --- Main ---
+
+print_summary() {
+	log_info ""
+	log_info "╔══════════════════════════════════════════╗"
+	log_info "║     Aomi (Fedora CSB) Bootstrap Done     ║"
+	log_info "╠══════════════════════════════════════════╣"
+	log_info "║  Nix:            ✓ installed             ║"
+	log_info "║  Repository:     ✓ $REPO_PATH"
+	log_info "║  System-manager: ✓ activated             ║"
+	log_info "╠══════════════════════════════════════════╣"
+	log_info "║  To rebuild:                             ║"
+	log_info "║    cd $REPO_PATH"
+	log_info "║    nix build .#systemConfigs.aomi        ║"
+	log_info "║    sudo ./result/bin/activate             ║"
+	log_info "╠══════════════════════════════════════════╣"
+	log_info "║  Next: home-manager for dev tools        ║"
+	log_info "║    nix run home-manager -- switch \\      ║"
+	log_info "║      --flake .#vincent@aomi              ║"
+	log_info "╚══════════════════════════════════════════╝"
+}
+
+main() {
+	log_info "Bootstrapping aomi (Fedora CSB)..."
+	log_info "  Repo:   $REPO_URL → $REPO_PATH"
+	log_info "  Config: $SYSTEM_CONFIG"
+	echo
+
+	check_root
+	install_nix
+	configure_nix
+	clone_repo
+	build_and_activate
+	setup_wireguard_key
+	print_summary
+}
+
+main "$@"
imperative/aomi/README.md
@@ -0,0 +1,68 @@
+# Aomi — Fedora CSB + Nix
+
+ThinkPad P1 Gen 3 running Red Hat CSB (Fedora), managed with Nix
+system-manager and home-manager.
+
+## Architecture
+
+```
+Fedora CSB (base OS, IT-managed)
+  └── Nix (Determinate installer)
+       ├── system-manager  → WireGuard, Syncthing, system services
+       └── home-manager    → shell, editors, dev tools
+```
+
+## Bootstrap
+
+From another machine with SSH access to aomi:
+
+```bash
+ssh vincent@192.168.1.39 'bash -s' < imperative/aomi/bootstrap.sh
+```
+
+Or on aomi directly:
+
+```bash
+curl -sL https://git.sbr.pm/home/raw/branch/main/imperative/aomi/bootstrap.sh | bash
+```
+
+## Rebuilding
+
+```bash
+cd ~/src/home
+
+# System-manager (WireGuard, Syncthing)
+nix build .#systemConfigs.aomi && sudo ./result/bin/activate
+
+# Home-manager (dev tools, shell) — TODO: not yet configured
+# nix run home-manager -- switch --flake .#vincent@aomi
+```
+
+## WireGuard
+
+The system-manager config writes `/etc/wireguard/wg0.conf` but uses a
+placeholder for the private key. After first activation:
+
+```bash
+# Option 1: Restore backed-up key
+sudo cp /path/to/backup/private.key /etc/wireguard/
+sudo chmod 600 /etc/wireguard/private.key
+
+# Option 2: Generate new keypair (update globals.nix with new pubkey)
+wg genkey | sudo tee /etc/wireguard/private.key
+sudo cat /etc/wireguard/private.key | wg pubkey
+# → update globals.nix machines.aomi.net.vpn.pubkey
+
+sudo systemctl restart wireguard-wg0
+```
+
+## Syncthing
+
+After activation, Syncthing runs as a system service under the
+`vincent` user. It will generate a new device ID — update
+`globals.nix` and accept the device on other nodes.
+
+```bash
+# Get new device ID
+curl -s http://localhost:8384/rest/system/status | jq -r .myID
+```
systems/aomi/system.nix
@@ -0,0 +1,83 @@
+{
+  pkgs,
+  globals,
+  ...
+}:
+let
+  machine = globals.machines.aomi;
+  vpnServer = globals.machines.carthage;
+in
+{
+  config = {
+    # Platform
+    nixpkgs.hostPlatform = "x86_64-linux";
+
+    # Required for non-NixOS (Fedora CSB)
+    system-manager.allowAnyDistro = true;
+
+    # System packages
+    environment.systemPackages = with pkgs; [
+      wireguard-tools
+      syncthing
+      vim
+      htop
+      curl
+      git
+    ];
+
+    # WireGuard wg0 service
+    systemd.services.wireguard-wg0 = {
+      description = "WireGuard VPN (wg0)";
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      wantedBy = [ "system-manager.target" ];
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+        ExecStart = "${pkgs.wireguard-tools}/bin/wg-quick up wg0";
+        ExecStop = "${pkgs.wireguard-tools}/bin/wg-quick down wg0";
+      };
+    };
+
+    # WireGuard configuration file
+    # NOTE: Private key must be added manually to /etc/wireguard/private.key
+    environment.etc."wireguard/wg0.conf" = {
+      text = ''
+        [Interface]
+        PrivateKey = PLACEHOLDER_REPLACE_MANUALLY
+        Address = ${builtins.head machine.net.vpn.ips}/24
+
+        [Peer]
+        PublicKey = ${vpnServer.net.vpn.pubkey}
+        AllowedIPs = 10.100.0.0/24
+        Endpoint = ${globals.net.vpn.endpoint}:51820
+        PersistentKeepalive = 25
+      '';
+      mode = "0600";
+    };
+
+    # Syncthing user service for vincent
+    systemd.services.syncthing = {
+      description = "Syncthing - Open Source Continuous File Synchronization";
+      wants = [ "network-online.target" ];
+      after = [ "network-online.target" ];
+      wantedBy = [ "system-manager.target" ];
+      serviceConfig = {
+        Type = "simple";
+        User = "vincent";
+        Group = "vincent";
+        ExecStart = "${pkgs.syncthing}/bin/syncthing serve --no-browser --no-restart";
+        Restart = "on-failure";
+        RestartSec = "10";
+        SuccessExitStatus = "3 4";
+        RestartForceExitStatus = "3 4";
+        # Hardening
+        ProtectSystem = "full";
+        PrivateTmp = true;
+        SystemCallArchitectures = "native";
+        MemoryDenyWriteExecute = true;
+        NoNewPrivileges = true;
+      };
+    };
+  };
+}
flake.nix
@@ -157,6 +157,10 @@
           hostname = "aion";
           system = "aarch64-linux";
         };
+        aomi = libx.mkSystemManager {
+          hostname = "aomi";
+          system = "x86_64-linux";
+        };
         nagoya = libx.mkSystemManager {
           hostname = "nagoya";
           system = "aarch64-linux";