#!/bin/bash -i # # Purpose : CoreDesktop installer # Print the logo print_logo() { cat << "EOF" ██ █ ██ ██ ██████ █████ ██ █████ █████ ████ ██ █████ ██ ██ ██ ██ ███ ███████ ██ ██ ██ ██ ██ ████████ ██ ██ ███████ █████ ██████ ██ ██ ██████ ██ ████████ ██ █ ███████ ██ ██ ██ ██████ ███████ ██████ ██ ██████ ██████ ██████ ███████ EOF } # Clear screen and show logo clear print_logo cd # Log the install to ~/.local/state/coredesktop/logs (update deletes old ones). # It's usually piped into bash, so it can't run itself again under script(1) # the way update does; send its output through tee instead. if mkdir -p -m 700 "$HOME/.local/state/coredesktop/logs"; then INSTALL_LOG="$HOME/.local/state/coredesktop/logs/install-$(date +%Y-%m-%d-%H%M%S).log" exec > >(tee -a "$INSTALL_LOG") 2>&1 TEE_PID=$! fi # Exit on any error set -e # What can't be fixed from here, checked before anything changes. The rest of # the checks need the repo (system/preflight.sh, below). if [ "$(id -u)" -eq 0 ]; then echo "Run this as your own user, not as root. It asks for your password when it needs sudo." exit 1 fi if [ ! -f /etc/arch-release ] || ! command -v pacman &> /dev/null; then echo "CoreDesktop needs Arch Linux. See \"Installing Arch\" in the README." exit 1 fi if [ "$(uname -m)" != x86_64 ]; then echo "CoreDesktop needs a 64-bit PC (x86_64); this is $(uname -m)." exit 1 fi if ! curl -fsS --max-time 15 -o /dev/null https://archlinux.org; then echo "Couldn't reach archlinux.org. Connect to the internet and run this again." exit 1 fi # Ask for the password once, then keep sudo's cached credentials fresh until # the install exits, so long builds don't stop for a password again. Unlike a # passwordless sudoers entry, nothing is left behind if the install is killed. if ! sudo -v; then echo "Couldn't use sudo. If your password was right, your user needs sudo rights (in" echo "archinstall, make the user a superuser): https://wiki.archlinux.org/title/Sudo" exit 1 fi while kill -0 $$ 2> /dev/null; do sudo -n -v; sleep 60; done > /dev/null 2>&1 & SUDO_KEEPALIVE_PID=$! on_exit() { local status=$? kill "$SUDO_KEEPALIVE_PID" 2> /dev/null if (( status != 0 )) && [ -n "$INSTALL_LOG" ]; then echo "The install stopped with an error. The full output is in $INSTALL_LOG" # Offer to send the log to us, once tee has written all of it (a second at # most). report asks on the terminal, so this needs one. local report="$HOME/.local/share/coredesktop/report" i if [ -f "$report" ] && { : < /dev/tty; } 2> /dev/null; then exec > /dev/tty 2>&1 for (( i = 0; i < 10; i++ )); do kill -0 "$TEE_PID" 2> /dev/null || break sleep 0.1 done bash "$report" --offer "$INSTALL_LOG" install.sh fi fi } trap on_exit EXIT # Ensure git is available before attempting to clone anything if ! command -v git &> /dev/null; then echo "Installing git..." sudo pacman -S git --noconfirm else echo "git is installed." fi # Update or clone the CoreDesktop repo echo "Getting the latest version of CoreDesktop..." if [ -d "$HOME/.local/share/coredesktop/.git" ]; then git -C ~/.local/share/coredesktop pull --ff-only else git clone https://gitea.federated.computer/Federated_Computer/coredesktop.git ~/.local/share/coredesktop fi cd ~/.local/share/coredesktop # Source utility functions (provides install_packages and other helpers) source utils.sh # Install the refresh, update, sibling, version, configure, audit and report scripts to the user's bin mkdir -p ~/.local/bin install_script ~/.local/share/coredesktop/refresh ~/.local/bin/refresh install_script ~/.local/share/coredesktop/update ~/.local/bin/update install_script ~/.local/share/coredesktop/sibling ~/.local/bin/sibling install_script ~/.local/share/coredesktop/version ~/.local/bin/version install_script ~/.local/share/coredesktop/configure ~/.local/bin/configure install_script ~/.local/share/coredesktop/audit ~/.local/bin/audit install_script ~/.local/share/coredesktop/report ~/.local/bin/report # Source the package list (defines SYSTEM_UTILS, DEV_TOOLS, DESKTOP, etc. arrays) if [ ! -f "packages.conf" ]; then echo "Error: packages.conf not found!" exit 1 fi source packages.conf source remove-packages.conf # Disk space, btrfs, display manager and sound server: stops, asks, or offers # to fix what it finds bash ~/.local/share/coredesktop/system/preflight.sh echo "Starting full system setup..." # Federated Computer's own packages (Federated Calendar, ...) echo "Adding the Federated Computer package repo..." ensure_federated_repo # Bring the system fully up to date before installing anything new echo "Updating system..." sudo pacman -Sy --needed --noconfirm archlinux-keyring sudo pacman -Su --noconfirm cd ~/ # Install yay AUR helper if not present — required for AUR packages installed later if ! command -v yay &> /dev/null; then echo "Installing yay AUR helper..." sudo pacman -S --needed git base-devel --noconfirm if [[ -d "yay" ]]; then echo "yay directory already exists, removing it..." rm -rf yay fi echo "Cloning yay repository..." git clone https://aur.archlinux.org/yay.git cd yay echo "Building yay..." makepkg -si --noconfirm cd .. rm -rf yay else echo "yay is already installed." fi # Remove replaced packages first, so their replacements don't conflict with them echo "Removing unwanted packages..." remove_packages "${REMOVE_PACMAN[@]}" remove_flatpaks "${REMOVE_FLATPAKS[@]}" # Install packages grouped by category as defined in packages.conf echo "Installing system utilities..." install_packages "${SYSTEM_UTILS[@]}" echo "Installing development tools..." install_packages "${DEV_TOOLS[@]}" echo "Installing system maintenance tools..." install_packages "${MAINTENANCE[@]}" echo "Installing desktop environment..." install_packages "${DESKTOP[@]}" echo "Installing media packages..." install_packages "${MEDIA[@]}" echo "Installing fonts..." install_packages "${FONTS[@]}" echo "Installing pipx applications..." install_pipx_apps "${PIPX[@]}" # Enable the services in SERVICES, and enable and start those in START_SERVICES, # then register the SSH Avahi service so the machine is discoverable over mDNS echo "Configuring services..." enable_services "${SERVICES[@]}" enable_services --now "${START_SERVICES[@]}" sudo cp /usr/share/doc/avahi/ssh.service /etc/avahi/services/ cd ~/.local/share/coredesktop # Install GNOME shell extensions via the dedicated script echo "Installing GNOME extensions..." . gnome/gnome-extensions.sh # Install Flatpak apps — some programs run better sandboxed as Flatpaks echo "Installing flatpaks..." . install-flatpaks.sh # Install TPM (Tmux Plugin Manager) and configured plugins echo "Installing tmux plug-in manager..." . install-tpm.sh # Install Kickstart Neovim config as the default Neovim configuration echo "Installing Kickstart Neovim..." . install-kickstart-nvim.sh # Install Claude Code and configure desktop notification hooks echo "Installing Claude Code..." bash claude.sh # Run AI agents (opencode, hermes) in a sandbox, and turn on Claude Code's own echo "Setting up sandboxed AI agents..." bash ~/.local/share/coredesktop/sandbox/setup.sh # Configure Bitwarden SSH agent echo "Configuring Bitwarden SSH agent..." . bitwarden.sh # Add the current user to the docker group and start the Docker daemon # Group membership takes effect at next login (newgrp would start an interactive shell) sudo usermod -aG docker $USER sudo systemctl enable docker.service sudo systemctl start docker.service # Enable and start Tailscale daemon if ! systemctl is-enabled tailscaled &> /dev/null; then sudo systemctl enable tailscaled fi if ! systemctl is-active tailscaled &> /dev/null; then sudo systemctl start tailscaled fi # SSH keepalives, login lockout, shutdown timeout, OOM protection, boot, SSH PATH echo "Applying system defaults..." bash ~/.local/share/coredesktop/system/defaults.sh # Leave Wi-Fi to NetworkManager alone if archinstall turned on iwd as well echo "Checking Wi-Fi..." bash ~/.local/share/coredesktop/system/wifi.sh # SSH logins with keys only, not passwords echo "Configuring the SSH server..." bash ~/.local/share/coredesktop/system/sshd.sh # Block incoming connections except SSH, mDNS, LocalSend and Tailscale echo "Configuring the firewall..." bash ~/.local/share/coredesktop/system/firewall.sh # Keep pacman on mirrors that are up to date with Arch echo "Configuring package mirrors..." bash ~/.local/share/coredesktop/system/mirrors.sh # Snapshot / before each update (btrfs only) echo "Configuring Snapper..." bash ~/.local/share/coredesktop/system/snapper.sh echo "Installing GPU drivers..." bash ~/.local/share/coredesktop/system/vulkan.sh bash ~/.local/share/coredesktop/system/nvidia.sh # Prompt, zoxide, fzf key bindings, aliases and functions echo "Configuring the shell..." bash ~/.local/share/coredesktop/shell/setup.sh # Keep WinBoat's Windows container from starting at boot (if it's set up) bash ~/.local/share/coredesktop/system/winboat.sh # Notify in GNOME when a new version of CoreDesktop is available echo "Configuring update notifications..." bash ~/.local/share/coredesktop/system/update-notify.sh # Pause a Core's email when the mail server stops taking its password echo "Configuring email password checks..." bash ~/.local/share/coredesktop/system/email-watch.sh # Apply GNOME dconf settings only if the settings file has changed since last run bash ~/.local/share/coredesktop/gnome/apply-gnome-settings.sh # Fix mDNS configuration if systemd-resolved is managing DNS on this system if systemctl is-enabled systemd-resolved.service &> /dev/null; then echo "systemd-resolved is enabled, applying mDNS fix..." bash ~/.local/share/coredesktop/fixmdns.sh fi # Run one-time migrations not yet recorded on this machine. On a fresh install # they repeat setup done above, which is safe because each one is idempotent. echo "Running migrations..." run_migrations CONFIGURE_HINT="configure connects to your Federated Core, sets up backups and signs you in to the AI agents." # If the running kernel's modules are gone, a newer kernel was installed during # this run. configure waits for the reboot: until then, modules it needs, such # as Tailscale's tun and davfs2's fuse, can't be loaded. if [ ! -d "/usr/lib/modules/$(uname -r)" ]; then echo "A newer kernel has been installed. Reboot, then run: configure" echo "$CONFIGURE_HINT" exit 0 fi echo "Kernel is up to date, no reboot needed. You may want to consider a reboot if other significant software was updated or installed." # Offer configure only if something is still to do, as refresh runs this too. # This script is usually piped into bash, so ask on the terminal, not stdin. if ~/.local/bin/configure --list | grep -q '^to do'; then echo echo "$CONFIGURE_HINT" if { : < /dev/tty; } 2> /dev/null && read -rp "Run configure now? [Y/n] " answer < /dev/tty \ && [[ "$answer" != [nN]* ]]; then # Straight to the terminal, outside the log: sign-ins need a terminal, and # their codes and links don't belong in a log ~/.local/bin/configure < /dev/tty > /dev/tty 2>&1 || true else echo "To do it later, run: configure" fi fi