Phase 3: Encrypted secrets - Add secrets module with agenix integration - Create secrets/secrets.nix template for key definitions - Installer generates SSH key if missing - Installer creates personalized secrets.nix with user's key - Full documentation in docs/SECRETS.md Features: - Secrets encrypted with age using SSH keys - Decrypted automatically at system activation - Safe to commit .age files to git - Support for WiFi passwords, API keys, service credentials Usage: agenix -e secrets/my-secret.age age.secrets.my-secret.file = ./secrets/my-secret.age; Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
412 lines
12 KiB
Bash
Executable File
412 lines
12 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Nomarchy Installer
|
|
# Interactive TUI installer for existing NixOS systems
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
BOLD='\033[1m'
|
|
|
|
# Detect if we're running from flake or locally
|
|
NOMARCHY_FLAKE="${NOMARCHY_FLAKE:-github:blu/nomarchy}"
|
|
|
|
print_banner() {
|
|
echo -e "${CYAN}"
|
|
cat << 'EOF'
|
|
_ __ __
|
|
/ | / /___ ____ ___ ____ _____/ /___ __
|
|
/ |/ / __ \/ __ `__ \/ __ `/ __/ / __/ / /
|
|
/ /| / /_/ / / / / / / /_/ / / / / /__ / /
|
|
/_/ |_/\____/_/ /_/ /_/\__,_/_/ /_/\___//_/
|
|
|
|
EOF
|
|
echo -e "${NC}"
|
|
echo -e "${BOLD}An opinionated NixOS configuration with Hyprland${NC}"
|
|
echo ""
|
|
}
|
|
|
|
check_requirements() {
|
|
echo -e "${BLUE}Checking requirements...${NC}"
|
|
|
|
if [ ! -f /etc/NIXOS ]; then
|
|
echo -e "${RED}Error: This installer requires NixOS.${NC}"
|
|
echo "For other Linux distributions, please use the ISO installer."
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$(id -u)" = "0" ]; then
|
|
echo -e "${RED}Error: Do not run this installer as root.${NC}"
|
|
echo "The installer will use sudo when needed."
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${GREEN}Requirements met!${NC}"
|
|
echo ""
|
|
}
|
|
|
|
prompt_username() {
|
|
local current_user
|
|
current_user=$(whoami)
|
|
|
|
echo -e "${BOLD}Username${NC}"
|
|
echo -e "Current user: ${CYAN}${current_user}${NC}"
|
|
read -rp "Enter username (press Enter for '${current_user}'): " username
|
|
username="${username:-$current_user}"
|
|
echo ""
|
|
}
|
|
|
|
prompt_hostname() {
|
|
local current_hostname
|
|
current_hostname=$(hostname)
|
|
|
|
echo -e "${BOLD}Hostname${NC}"
|
|
echo -e "Current hostname: ${CYAN}${current_hostname}${NC}"
|
|
read -rp "Enter hostname (press Enter for '${current_hostname}'): " hostname_input
|
|
hostname_input="${hostname_input:-$current_hostname}"
|
|
echo ""
|
|
}
|
|
|
|
prompt_timezone() {
|
|
local current_tz
|
|
current_tz=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "UTC")
|
|
|
|
echo -e "${BOLD}Timezone${NC}"
|
|
echo -e "Current timezone: ${CYAN}${current_tz}${NC}"
|
|
echo "Common timezones: America/New_York, America/Los_Angeles, Europe/London, Asia/Tokyo"
|
|
read -rp "Enter timezone (press Enter for '${current_tz}'): " timezone
|
|
timezone="${timezone:-$current_tz}"
|
|
echo ""
|
|
}
|
|
|
|
prompt_locale() {
|
|
echo -e "${BOLD}Locale${NC}"
|
|
echo "Default: en_US.UTF-8"
|
|
read -rp "Enter locale (press Enter for 'en_US.UTF-8'): " locale
|
|
locale="${locale:-en_US.UTF-8}"
|
|
echo ""
|
|
}
|
|
|
|
prompt_keyboard() {
|
|
echo -e "${BOLD}Keyboard Layout${NC}"
|
|
echo "Default: us"
|
|
echo "Examples: us, uk, de, fr, es, us,gr (multiple separated by comma)"
|
|
read -rp "Enter keyboard layout(s): " keyboard
|
|
keyboard="${keyboard:-us}"
|
|
|
|
echo "Keyboard variants (leave empty for default):"
|
|
echo "Example: ,polytonic (for Greek polytonic as second layout)"
|
|
read -rp "Enter keyboard variant(s): " keyboard_variant
|
|
keyboard_variant="${keyboard_variant:-}"
|
|
echo ""
|
|
}
|
|
|
|
prompt_location() {
|
|
echo -e "${BOLD}Location (for Gammastep night light)${NC}"
|
|
echo "Enter coordinates for automatic night light adjustment."
|
|
echo "Leave empty to disable or set manually later."
|
|
read -rp "Latitude (e.g., 40.7): " lat
|
|
read -rp "Longitude (e.g., -74.0): " lon
|
|
lat="${lat:-0}"
|
|
lon="${lon:-0}"
|
|
echo ""
|
|
}
|
|
|
|
prompt_bootloader() {
|
|
echo -e "${BOLD}Bootloader${NC}"
|
|
echo "1) systemd-boot (default) - Simple, reliable, well-tested"
|
|
echo "2) Limine - Modern, prettier, themed to match Nomarchy"
|
|
read -rp "Choose bootloader [1/2]: " bootloader_choice
|
|
bootloader_choice="${bootloader_choice:-1}"
|
|
|
|
if [[ "$bootloader_choice" == "2" ]]; then
|
|
bootloader="limine"
|
|
echo -e "${CYAN}Using Limine bootloader${NC}"
|
|
else
|
|
bootloader="systemd-boot"
|
|
echo -e "${CYAN}Using systemd-boot${NC}"
|
|
fi
|
|
|
|
read -rp "Enable Plymouth boot splash? [y/N]: " plymouth
|
|
plymouth="${plymouth:-n}"
|
|
[[ "$plymouth" =~ ^[Yy] ]] && enable_plymouth="true" || enable_plymouth="false"
|
|
|
|
echo ""
|
|
}
|
|
|
|
prompt_features() {
|
|
echo -e "${BOLD}Optional Features${NC}"
|
|
|
|
read -rp "Enable Syncthing file sync? [Y/n]: " syncthing
|
|
syncthing="${syncthing:-y}"
|
|
[[ "$syncthing" =~ ^[Yy] ]] && enable_syncthing="true" || enable_syncthing="false"
|
|
|
|
read -rp "Enable Mullvad VPN? [y/N]: " mullvad
|
|
mullvad="${mullvad:-n}"
|
|
[[ "$mullvad" =~ ^[Yy] ]] && enable_mullvad="true" || enable_mullvad="false"
|
|
|
|
read -rp "Enable printing support? [Y/n]: " printing
|
|
printing="${printing:-y}"
|
|
[[ "$printing" =~ ^[Yy] ]] && enable_printing="true" || enable_printing="false"
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}WARNING: The following option disables CPU security mitigations.${NC}"
|
|
echo "This improves performance but reduces security. Only enable if you understand the risks."
|
|
read -rp "Enable mitigations=off (insecure)? [y/N]: " mitigations
|
|
mitigations="${mitigations:-n}"
|
|
[[ "$mitigations" =~ ^[Yy] ]] && enable_mitigations_off="true" || enable_mitigations_off="false"
|
|
|
|
echo ""
|
|
}
|
|
|
|
generate_config() {
|
|
local config_dir="$HOME/.config/nomarchy"
|
|
mkdir -p "$config_dir/wallpapers"
|
|
|
|
echo -e "${BLUE}Generating configuration...${NC}"
|
|
|
|
# Parse keyboard layouts into Nix list format
|
|
local kb_layouts_nix
|
|
kb_layouts_nix=$(echo "$keyboard" | sed 's/,/" "/g' | sed 's/^/"/' | sed 's/$/"/')
|
|
|
|
local kb_variants_nix
|
|
if [ -n "$keyboard_variant" ]; then
|
|
kb_variants_nix=$(echo "$keyboard_variant" | sed 's/,/" "/g' | sed 's/^/"/' | sed 's/$/"/')
|
|
else
|
|
kb_variants_nix='""'
|
|
fi
|
|
|
|
cat > "$config_dir/config.nix" << EOF
|
|
# Nomarchy User Configuration
|
|
# Generated by nomarchy-install on $(date)
|
|
{
|
|
username = "${username}";
|
|
hostname = "${hostname_input}";
|
|
timezone = "${timezone}";
|
|
locale = "${locale}";
|
|
keyboardLayouts = [${kb_layouts_nix}];
|
|
keyboardVariants = [${kb_variants_nix}];
|
|
|
|
# Location for Gammastep (night light)
|
|
location = {
|
|
lat = ${lat};
|
|
lon = ${lon};
|
|
};
|
|
|
|
# Features
|
|
enableSyncthing = ${enable_syncthing};
|
|
enableMullvad = ${enable_mullvad};
|
|
enablePrinting = ${enable_printing};
|
|
enableBluetooth = true;
|
|
|
|
# Boot
|
|
bootloader = "${bootloader}";
|
|
enablePlymouth = ${enable_plymouth};
|
|
|
|
# Performance (security tradeoff)
|
|
enableMitigationsOff = ${enable_mitigations_off};
|
|
|
|
# Theme
|
|
theme = "classical";
|
|
}
|
|
EOF
|
|
|
|
echo -e "${GREEN}Configuration saved to ${config_dir}/config.nix${NC}"
|
|
echo ""
|
|
}
|
|
|
|
setup_flake() {
|
|
local config_dir="$HOME/.config/nomarchy"
|
|
|
|
echo -e "${BLUE}Setting up flake...${NC}"
|
|
|
|
cat > "$config_dir/flake.nix" << EOF
|
|
{
|
|
description = "My Nomarchy Configuration";
|
|
|
|
inputs = {
|
|
nomarchy.url = "${NOMARCHY_FLAKE}";
|
|
nixpkgs.follows = "nomarchy/nixpkgs";
|
|
};
|
|
|
|
outputs = { self, nomarchy, nixpkgs, ... }@inputs: let
|
|
userConfig = import ./config.nix;
|
|
in {
|
|
nixosConfigurations.\${userConfig.hostname} = nomarchy.lib.mkHost {
|
|
system = "x86_64-linux";
|
|
config = userConfig;
|
|
extraModules = [
|
|
# Add your hardware-configuration.nix here
|
|
/etc/nixos/hardware-configuration.nix
|
|
|
|
# Add any custom modules here
|
|
# ./my-custom-module.nix
|
|
];
|
|
};
|
|
};
|
|
}
|
|
EOF
|
|
|
|
echo -e "${GREEN}Flake created at ${config_dir}/flake.nix${NC}"
|
|
echo ""
|
|
}
|
|
|
|
download_wallpapers() {
|
|
local wallpaper_dir="$HOME/.config/nomarchy/wallpapers"
|
|
|
|
echo -e "${BLUE}Setting up wallpapers...${NC}"
|
|
|
|
# Create a default wallpaper (simple gradient)
|
|
# In production, this would download from the nomarchy repo
|
|
if command -v convert &> /dev/null; then
|
|
convert -size 1920x1080 gradient:'#1a1611-#2d2620' "$wallpaper_dir/default.jpg" 2>/dev/null || true
|
|
fi
|
|
|
|
echo -e "${YELLOW}Note: Copy your wallpapers to ${wallpaper_dir}${NC}"
|
|
echo ""
|
|
}
|
|
|
|
setup_secrets() {
|
|
local secrets_dir="$HOME/.config/nomarchy/secrets"
|
|
mkdir -p "$secrets_dir"
|
|
|
|
echo -e "${BLUE}Setting up secrets management...${NC}"
|
|
|
|
# Check for SSH key
|
|
if [ ! -f "$HOME/.ssh/id_ed25519.pub" ]; then
|
|
echo -e "${YELLOW}No SSH key found. Generating one...${NC}"
|
|
ssh-keygen -t ed25519 -f "$HOME/.ssh/id_ed25519" -N "" -C "${username}@${hostname_input}"
|
|
fi
|
|
|
|
local user_key
|
|
user_key=$(cat "$HOME/.ssh/id_ed25519.pub")
|
|
|
|
# Create secrets.nix template
|
|
cat > "$secrets_dir/secrets.nix" << EOF
|
|
# Nomarchy Secrets Configuration
|
|
# Generated on $(date)
|
|
#
|
|
# To add a secret:
|
|
# 1. Add it to this file with the keys below
|
|
# 2. Run: agenix -e secrets/<name>.age
|
|
# 3. Reference in your NixOS config
|
|
|
|
let
|
|
# Your SSH public key (for encrypting)
|
|
user = "${user_key}";
|
|
|
|
# Host SSH key - add after first boot:
|
|
# Run: ssh-keyscan localhost 2>/dev/null | grep ed25519
|
|
# host = "ssh-ed25519 AAAA...";
|
|
|
|
allKeys = [ user ];
|
|
# After adding host key: allKeys = [ user host ];
|
|
in {
|
|
# Example secrets (uncomment to use):
|
|
# "wifi-password.age".publicKeys = allKeys;
|
|
# "api-key.age".publicKeys = allKeys;
|
|
}
|
|
EOF
|
|
|
|
echo -e "${GREEN}Secrets directory created at ${secrets_dir}${NC}"
|
|
echo -e "${YELLOW}After first boot, add your host's SSH key to secrets.nix${NC}"
|
|
echo ""
|
|
}
|
|
|
|
show_summary() {
|
|
echo -e "${BOLD}Configuration Summary${NC}"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo -e "Username: ${CYAN}${username}${NC}"
|
|
echo -e "Hostname: ${CYAN}${hostname_input}${NC}"
|
|
echo -e "Timezone: ${CYAN}${timezone}${NC}"
|
|
echo -e "Locale: ${CYAN}${locale}${NC}"
|
|
echo -e "Keyboard: ${CYAN}${keyboard}${NC}"
|
|
echo -e "Bootloader: ${CYAN}${bootloader}${NC}"
|
|
echo -e "Plymouth: ${CYAN}${enable_plymouth}${NC}"
|
|
echo -e "Syncthing: ${CYAN}${enable_syncthing}${NC}"
|
|
echo -e "Mullvad: ${CYAN}${enable_mullvad}${NC}"
|
|
echo -e "Printing: ${CYAN}${enable_printing}${NC}"
|
|
echo -e "Mitigations: ${CYAN}${enable_mitigations_off}${NC}"
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
}
|
|
|
|
apply_config() {
|
|
local config_dir="$HOME/.config/nomarchy"
|
|
|
|
echo -e "${YELLOW}Ready to apply configuration.${NC}"
|
|
read -rp "Apply now? This will rebuild NixOS. [y/N]: " apply
|
|
|
|
if [[ "$apply" =~ ^[Yy] ]]; then
|
|
echo -e "${BLUE}Applying configuration...${NC}"
|
|
echo ""
|
|
|
|
cd "$config_dir"
|
|
|
|
# Update flake inputs
|
|
nix flake update
|
|
|
|
# Rebuild
|
|
sudo nixos-rebuild switch --flake ".#${hostname_input}"
|
|
|
|
echo ""
|
|
echo -e "${GREEN}Configuration applied successfully!${NC}"
|
|
echo -e "Please ${BOLD}log out and log back in${NC} to start Hyprland."
|
|
else
|
|
echo ""
|
|
echo -e "${YELLOW}Configuration saved but not applied.${NC}"
|
|
echo "To apply later, run:"
|
|
echo -e " ${CYAN}cd ~/.config/nomarchy && sudo nixos-rebuild switch --flake .#${hostname_input}${NC}"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
print_banner
|
|
check_requirements
|
|
|
|
echo -e "${BOLD}Let's configure your Nomarchy installation.${NC}"
|
|
echo ""
|
|
|
|
prompt_username
|
|
prompt_hostname
|
|
prompt_timezone
|
|
prompt_locale
|
|
prompt_keyboard
|
|
prompt_location
|
|
prompt_bootloader
|
|
prompt_features
|
|
|
|
show_summary
|
|
|
|
read -rp "Proceed with this configuration? [Y/n]: " proceed
|
|
proceed="${proceed:-y}"
|
|
|
|
if [[ ! "$proceed" =~ ^[Yy] ]]; then
|
|
echo "Installation cancelled."
|
|
exit 0
|
|
fi
|
|
|
|
generate_config
|
|
setup_flake
|
|
download_wallpapers
|
|
setup_secrets
|
|
apply_config
|
|
|
|
echo ""
|
|
echo -e "${GREEN}${BOLD}Nomarchy installation complete!${NC}"
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Log out and select 'Hyprland' from the session menu"
|
|
echo " 2. Press Super+/ for Quick Actions menu"
|
|
echo " 3. Press Super+R for app launcher"
|
|
echo " 4. See ~/.config/nomarchy/README.md for more info"
|
|
echo ""
|
|
}
|
|
|
|
main "$@"
|