clawdie-iso/firstboot/firstboot.sh

328 lines
14 KiB
Bash
Raw Normal View History

#!/bin/sh
# Clawdie-AI Firstboot Orchestrator
# Runs once on first boot (rc.d/clawdie-firstboot, REQUIRE: NETWORKING LOGIN)
# Dispatches to clawdie-shell-*.sh modules for cloud or baremetal path
#
# Usage:
# firstboot.sh — Normal run (wizard on baremetal, pre-baked on cloud)
# firstboot.sh --resume — Skip already-completed steps, continue from last failure
# firstboot.sh --reset — Clear progress and start over from the beginning
set -eu
SHARE="${SHARE:-/usr/local/share/clawdie-iso}"
LOG_FILE="${LOG_FILE:-/var/log/clawdie-firstboot.log}"
PROGRESS_FILE="${PROGRESS_FILE:-/var/log/clawdie-firstboot.progress}"
RC_CONF="${RC_CONF:-/etc/rc.conf}"
# ── Arg parsing ────────────────────────────────────────────────────────────
RESUME=0
case "${1:-}" in
--resume) RESUME=1 ;;
--reset)
rm -f "$PROGRESS_FILE"
echo "$(date '+%H:%M:%S') [firstboot] Progress reset — starting over" | tee -a "$LOG_FILE"
;;
--help|-h)
echo "Usage: firstboot.sh [--resume|--reset]"
echo " --resume Skip completed steps, continue from last failure"
echo " --reset Clear progress file and start from the beginning"
exit 0
;;
esac
log_msg() { echo "$(date '+%H:%M:%S') $1" | tee -a "$LOG_FILE"; }
# ── Checkpoint helpers ─────────────────────────────────────────────────────
# Mark a step done in the progress file
step_done() {
echo "$1" >> "$PROGRESS_FILE"
}
# Return 0 (true) if the step was already completed
step_completed() {
[ "$RESUME" -eq 1 ] && grep -qx "$1" "$PROGRESS_FILE" 2>/dev/null
}
# Run a module function with checkpoint guard.
fix(installer): Phase A — stable ZFS, safe upgrades, module matrix Four critical fixes before v1.0.0 VM test, informed by PC-BSD failure modes and GhostBSD's improvements: 1. shell-zfs.sh: zpool labelclear on fresh install Clear ZFS labels from every device that was in the old pool before bsdinstall writes new ones. Prevents the "can't find pool by GUID" boot failure that made PC-BSD reinstalls unreliable. 2. shell-zfs.sh: pre-upgrade snapshot When operator selects Upgrade, take zfs snapshot -r pool@pre-upgrade-{timestamp} before any changes. One reboot to roll back if the upgrade goes wrong. UPGRADE_SNAPSHOT exported for downstream modules to reference. 3. shell-env.sh: never overwrite secrets on upgrade clawdie_shell_env_generate() now checks CLAWDIE_BOOT_MODE. In upgrade mode it calls clawdie_shell_env_append_new_keys() instead of regenerating — reads existing .env and appends only keys that are absent. Existing DB passwords, JWT secrets, API keys are never touched. This fixes the root cause of the orphaned-database bug: new passwords that don't match the existing pool's data. 4. firstboot.sh: module execution matrix via run_step_if New run_step_if "<modes>" wrapper marks steps as done without running them when not applicable to the current boot mode. Upgrade skips: gpu, nvidia, ssh, system, desktop, pf, tailscale Upgrade runs: pkg, env (append-only), npm-globals, deploy Prevents SSH key resets, rc.conf overwrites, and firewall rewrites during upgrade — all of which undid operator customisations. Also adds INSTALLER-PLAN.md: full architecture plan for unified GUI/TUI installer with Fresh / Upgrade / Repair modes, boot environment support, and a clear phase roadmap to v1.1.0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 11:09:51 +00:00
# Usage: run_step <step_name> <function> [description] [step_num]
run_step() {
_step="$1"
_fn="$2"
_desc="${3:-$_fn}"
_step_num="${4:-0}" # Optional: step number for progress tracking
if step_completed "$_step"; then
log_msg "[firstboot] Skipping $_step (already completed)"
[ "$_step_num" -gt 0 ] && echo "PROGRESS=$_step_num" >> "$PROGRESS_FILE"
return 0
fi
log_msg "[firstboot] Running: $_desc"
"$_fn"
step_done "$_step"
[ "$_step_num" -gt 0 ] && echo "PROGRESS=$_step_num" >> "$PROGRESS_FILE"
}
fix(installer): Phase A — stable ZFS, safe upgrades, module matrix Four critical fixes before v1.0.0 VM test, informed by PC-BSD failure modes and GhostBSD's improvements: 1. shell-zfs.sh: zpool labelclear on fresh install Clear ZFS labels from every device that was in the old pool before bsdinstall writes new ones. Prevents the "can't find pool by GUID" boot failure that made PC-BSD reinstalls unreliable. 2. shell-zfs.sh: pre-upgrade snapshot When operator selects Upgrade, take zfs snapshot -r pool@pre-upgrade-{timestamp} before any changes. One reboot to roll back if the upgrade goes wrong. UPGRADE_SNAPSHOT exported for downstream modules to reference. 3. shell-env.sh: never overwrite secrets on upgrade clawdie_shell_env_generate() now checks CLAWDIE_BOOT_MODE. In upgrade mode it calls clawdie_shell_env_append_new_keys() instead of regenerating — reads existing .env and appends only keys that are absent. Existing DB passwords, JWT secrets, API keys are never touched. This fixes the root cause of the orphaned-database bug: new passwords that don't match the existing pool's data. 4. firstboot.sh: module execution matrix via run_step_if New run_step_if "<modes>" wrapper marks steps as done without running them when not applicable to the current boot mode. Upgrade skips: gpu, nvidia, ssh, system, desktop, pf, tailscale Upgrade runs: pkg, env (append-only), npm-globals, deploy Prevents SSH key resets, rc.conf overwrites, and firewall rewrites during upgrade — all of which undid operator customisations. Also adds INSTALLER-PLAN.md: full architecture plan for unified GUI/TUI installer with Fresh / Upgrade / Repair modes, boot environment support, and a clear phase roadmap to v1.1.0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 11:09:51 +00:00
# Run a step only when the current boot mode is in the allowed list.
# Usage: run_step_if "<modes>" <step_name> <function> [description] [step_num]
# Modes: space-separated list of CLAWDIE_BOOT_MODE values (install upgrade repair)
# Steps not applicable to the current mode are marked done (so --resume works).
run_step_if() {
_allowed="$1"; shift
_step="$1"
_mode="${CLAWDIE_BOOT_MODE:-install}"
if echo "$_allowed" | grep -qw "$_mode"; then
run_step "$@"
else
if ! step_completed "$_step"; then
log_msg "[firstboot] Skipping $_step (not applicable in $_mode mode)"
step_done "$_step"
fi
fi
}
# ── Set package path to bundled packages on HDD ──────────────────────────────
# After bsdinstall, packages live at SHARE/packages (not /mnt/media)
export USB_PKG_PATH="${SHARE}/packages"
# ── Prevent modules from auto-running when sourced ─────────────────────────
export SHELL_GPU_TEST=1
export SHELL_NVIDIA_TEST=1
export SHELL_PKG_TEST=1
export SHELL_ENV_TEST=1
export SHELL_DEPLOY_TEST=1
export SHELL_NPM_GLOBALS_TEST=1
export SHELL_TAILSCALE_TEST=1
export SHELL_ZFS_TEST=1
export SHELL_PF_TEST=1
Unify ISO and fix GPU installation gap (Sam & ZAI) BREAKING CHANGE: Removes --target and --gpu-driver flags, unified ISO for all use cases ## Phase 0: GPU Fix + Unified ISO ### Core Changes **GPU Package Installation (FIXES CRITICAL GAP):** - Add clawdie_shell_nvidia_install() function to shell-nvidia.sh - NVIDIA drivers now installed after detection (previously only configured) - Works offline (USB packages) or online (pkg install) - Resolves issue where rc.conf was set but driver not installed **Unified ISO Architecture:** - Remove --target flag from build.sh (no more vps/baremetal branching) - Remove --gpu-driver flag from build.sh (runtime detection instead) - All packages included on every ISO (desktop + all GPU drivers) - Single image works on VPS, baremetal, and cloud **Runtime Detection:** - Add shell-desktop.sh for display detection at firstboot - VPS/cloud: no display → lightdm disabled (headless) - Baremetal: display detected → lightdm enabled (Lumina desktop) - GPU detection always runs, installs correct driver version **Sudo Unification:** - Replace all doas references with sudo across entire codebase - Update AGENTS.md with system configuration guidelines - Update all documentation (BUILD.md, README.md, REQUIREMENTS.md, etc.) - Admin panel now uses sudo for privileged operations ### Files Modified **Core System:** - build.sh: Remove target/gpu-driver logic, unified package selection - firstboot/firstboot.sh: Add desktop detection module - firstboot/shell-nvidia.sh: Add package installation function (+33 lines) **New Files:** - firstboot/shell-desktop.sh: Display detection and desktop enablement - packages/pkg-list-nvidia-all.txt: All three NVIDIA driver versions (390/470/590) - .opencode/plans/phase0-gpu-fix-unified-iso.md: Implementation plan **Documentation:** - PLAN-UNIFY.md: Update Step 3 for unified approach - REQUIREMENTS.md: Simplify (no target choice), update for sudo - BUILD.md: Update for unified ISO, sudo commands - README.md: Update installation instructions - AGENTS.md: Add system configuration section (sudo standardization) - ADMIN-PANEL.md: Update privileged operations to use sudo - CLAWDIE-SHELL.md: Update example commands to sudo - CLAWDIE-ISO-REFACTORED.md: Update access paths to sudo - REFACTOR-SUMMARY.md: Update permissions section to sudo ### Benefits **Simplicity:** - One build command: ./build.sh (no flags needed) - One ISO to test and maintain - No wrong choices for users - No documentation explaining target differences **Flexibility:** - VPS can use GUI via VNC (wayvnc always available) - Baremetal can run headless (disable lightdm) - Repurpose hardware without reinstall - All GPU drivers available for any hardware **Technical:** - Fixes critical GPU driver installation gap - Runtime detection replaces build-time decisions - Disk overhead: ~650MB (1-2% of 50GB - acceptable) - No runtime overhead on VPS (services disabled by detection) ### Testing Required - [ ] Build unified ISO: ./build.sh - [ ] Test on VPS (no display): lightdm disabled, packages installed - [ ] Test on baremetal (display): lightdm enabled, Lumina boots - [ ] Test on NVIDIA hardware: driver installed and loaded - [ ] Test sudo commands work without password prompts - [ ] Verify all doas references removed
2026-04-06 13:28:56 +02:00
export SHELL_DESKTOP_TEST=1
export SETUP_IMPORT_TEST=1
# shell-ssh.sh and shell-system.sh use case/${0##*/} — no flag needed
# ── Source modules (functions only, nothing runs yet) ─────────────────────────
. "${SHARE}/build.cfg"
. "${SHARE}/firstboot/setup-import.sh"
. "${SHARE}/firstboot/shell-zfs.sh"
. "${SHARE}/firstboot/shell-gpu.sh"
. "${SHARE}/firstboot/shell-nvidia.sh"
. "${SHARE}/firstboot/shell-pkg.sh"
. "${SHARE}/firstboot/shell-ssh.sh"
. "${SHARE}/firstboot/shell-env.sh"
. "${SHARE}/firstboot/shell-system.sh"
Unify ISO and fix GPU installation gap (Sam & ZAI) BREAKING CHANGE: Removes --target and --gpu-driver flags, unified ISO for all use cases ## Phase 0: GPU Fix + Unified ISO ### Core Changes **GPU Package Installation (FIXES CRITICAL GAP):** - Add clawdie_shell_nvidia_install() function to shell-nvidia.sh - NVIDIA drivers now installed after detection (previously only configured) - Works offline (USB packages) or online (pkg install) - Resolves issue where rc.conf was set but driver not installed **Unified ISO Architecture:** - Remove --target flag from build.sh (no more vps/baremetal branching) - Remove --gpu-driver flag from build.sh (runtime detection instead) - All packages included on every ISO (desktop + all GPU drivers) - Single image works on VPS, baremetal, and cloud **Runtime Detection:** - Add shell-desktop.sh for display detection at firstboot - VPS/cloud: no display → lightdm disabled (headless) - Baremetal: display detected → lightdm enabled (Lumina desktop) - GPU detection always runs, installs correct driver version **Sudo Unification:** - Replace all doas references with sudo across entire codebase - Update AGENTS.md with system configuration guidelines - Update all documentation (BUILD.md, README.md, REQUIREMENTS.md, etc.) - Admin panel now uses sudo for privileged operations ### Files Modified **Core System:** - build.sh: Remove target/gpu-driver logic, unified package selection - firstboot/firstboot.sh: Add desktop detection module - firstboot/shell-nvidia.sh: Add package installation function (+33 lines) **New Files:** - firstboot/shell-desktop.sh: Display detection and desktop enablement - packages/pkg-list-nvidia-all.txt: All three NVIDIA driver versions (390/470/590) - .opencode/plans/phase0-gpu-fix-unified-iso.md: Implementation plan **Documentation:** - PLAN-UNIFY.md: Update Step 3 for unified approach - REQUIREMENTS.md: Simplify (no target choice), update for sudo - BUILD.md: Update for unified ISO, sudo commands - README.md: Update installation instructions - AGENTS.md: Add system configuration section (sudo standardization) - ADMIN-PANEL.md: Update privileged operations to use sudo - CLAWDIE-SHELL.md: Update example commands to sudo - CLAWDIE-ISO-REFACTORED.md: Update access paths to sudo - REFACTOR-SUMMARY.md: Update permissions section to sudo ### Benefits **Simplicity:** - One build command: ./build.sh (no flags needed) - One ISO to test and maintain - No wrong choices for users - No documentation explaining target differences **Flexibility:** - VPS can use GUI via VNC (wayvnc always available) - Baremetal can run headless (disable lightdm) - Repurpose hardware without reinstall - All GPU drivers available for any hardware **Technical:** - Fixes critical GPU driver installation gap - Runtime detection replaces build-time decisions - Disk overhead: ~650MB (1-2% of 50GB - acceptable) - No runtime overhead on VPS (services disabled by detection) ### Testing Required - [ ] Build unified ISO: ./build.sh - [ ] Test on VPS (no display): lightdm disabled, packages installed - [ ] Test on baremetal (display): lightdm enabled, Lumina boots - [ ] Test on NVIDIA hardware: driver installed and loaded - [ ] Test sudo commands work without password prompts - [ ] Verify all doas references removed
2026-04-06 13:28:56 +02:00
. "${SHARE}/firstboot/shell-desktop.sh"
. "${SHARE}/firstboot/shell-pf.sh"
. "${SHARE}/firstboot/shell-tailscale.sh"
. "${SHARE}/firstboot/shell-npm-globals.sh"
. "${SHARE}/firstboot/shell-deploy.sh"
# ── Load GUI config if present ───────────────────────────────────────────────
SETUP_HANDOFF_LOADED=0
if [ -f "/tmp/clawdie-install.conf" ]; then
log_msg "[firstboot] Loading GUI installer configuration"
. "/tmp/clawdie-install.conf"
step_done "wizard"
SETUP_HANDOFF_LOADED=1
elif [ -f "/var/db/clawdie-installer/clawdie-handoff.sealed" ]; then
log_msg "[firstboot] Loading installed handoff payload"
. "/var/db/clawdie-installer/clawdie-handoff.sealed"
step_done "wizard"
SETUP_HANDOFF_LOADED=1
fi
log_msg "[firstboot] Starting — target: ${TARGET:-baremetal}${RESUME:+, resume mode}"
2026-05-12 17:10:54 +02:00
log_msg "[firstboot] ISO ${ISO_VERSION:-unknown}-${BUILD_CHANNEL:-unknown}; Clawdie-AI ${CLAWDIE_REF:-unknown}@${CLAWDIE_AI_COMMIT:-unknown}"
# ── Import setup.txt/system.env from USB config partition ───────────────────
if [ "$SETUP_HANDOFF_LOADED" -eq 1 ]; then
log_msg "[firstboot] Skipping setup-import (installer handoff already loaded)"
step_done "setup-import"
else
run_step "setup-import" clawdie_setup_import_load "Load first-boot setup from USB partition"
fi
# ── ZFS pool detection (baremetal only) ───────────────────────────────────
# Runs early to decide boot mode: install | upgrade | maintenance
# Maintenance mode exec's away and never returns.
CLAWDIE_BOOT_MODE="${CLAWDIE_BOOT_MODE:-install}"
if [ "${TARGET:-baremetal}" != "vps" ]; then
run_step "zfs" clawdie_shell_zfs_detect "ZFS pool detection"
fi
export CLAWDIE_BOOT_MODE
# ── Collect configuration ──────────────────────────────────────────────────
if step_completed "wizard"; then
log_msg "[firstboot] Skipping wizard (already completed)"
elif [ "$CLAWDIE_BOOT_MODE" = "upgrade" ]; then
# Upgrade: import existing pool, load .env from previous install
log_msg "[firstboot] Upgrade mode — loading existing configuration"
kldload zfs 2>/dev/null || true
zpool import "$POOL_NAME" 2>/dev/null || true
_existing_env="/home/clawdie/clawdie-ai/.env"
if [ -f "$_existing_env" ]; then
ENV_FILE="${ENV_FILE:-/home/clawdie/.env}"
cp "$_existing_env" "$ENV_FILE"
log_msg "[firstboot] Loaded existing .env from previous install"
else
log_msg "[firstboot] WARNING: No existing .env found — falling through to wizard"
fi
step_done "wizard"
elif [ "${TARGET:-baremetal}" = "vps" ]; then
# VPS: all values must be pre-baked in build.cfg — validate
[ -z "${ASSISTANT_NAME:-}" ] && log_msg "ERROR: ASSISTANT_NAME not baked" && exit 1
[ -z "${AGENT_DOMAIN:-}" ] && log_msg "ERROR: AGENT_DOMAIN not baked" && exit 1
[ -z "${TZ:-}" ] && log_msg "ERROR: TZ not baked" && exit 1
log_msg "[firstboot] VPS — pre-baked config OK"
step_done "wizard"
else
# Baremetal: minimal wizard — identity, network, keys only
# All jails (db, git/forgejo, cms) are provisioned by default.
# API keys are deferred to web UI on first desktop login.
_dialog() { bsddialog --backtitle "Clawdie-AI Setup" "$@" 2>&1; }
_dialog --msgbox "\
EXPERIMENTAL BUILD
This is pre-release software.
Not recommended for production use.
Data loss or service interruption possible.
By continuing, you assume all risks." 12 60
# Tailscale (recommended, but optional)
if _dialog --yesno \
"Enable Tailscale for secure remote access?\n\n" \
"Tailscale creates a private network for SSH access.\n" \
"Without it, SSH will be exposed on public port 22.\n\n" \
"Recommended: Yes (you can add auth key later if needed)" 14 70; then
FEATURE_TAILSCALE="YES"
TAILSCALE_AUTHKEY=$(_dialog --passwordbox \
"Tailscale device auth key (tskey-...).\n\n" \
"Leave blank to skip auth (you can run 'tailscale up' later).\n" \
"Generate at: https://login.tailscale.com/admin/settings/keys" 13 72 "")
if [ -z "$TAILSCALE_AUTHKEY" ]; then
_dialog --msgbox "No auth key provided.\n\nTailscale will be installed but not authenticated.\nRun 'tailscale up' after first boot to connect." 10 60
fi
else
FEATURE_TAILSCALE="NO"
TAILSCALE_AUTHKEY=""
_dialog --msgbox "WARNING: SSH will be publicly accessible on port 22.\n\nYou are responsible for securing network access." 10 60
fi
ASSISTANT_NAME=$(_dialog --inputbox "Assistant name:" 8 50 "Clawdie")
# Derive default domain from assistant name (e.g., Clawdie → clawdie.home.arpa)
_agent_name_lower=$(echo "$ASSISTANT_NAME" | tr 'A-Z' 'a-z' | sed 's/[^a-z0-9]//g')
_default_domain="${_agent_name_lower}.home.arpa"
AGENT_DOMAIN=$(_dialog --inputbox \
"Domain zone (public or local):" 8 60 "$_default_domain")
if command -v route >/dev/null 2>&1 && command -v ifconfig >/dev/null 2>&1; then
HOST_IF="$(route -n get default 2>/dev/null | awk '/interface:/ { print $2; exit }')"
HOST_IPS=""
if [ -n "$HOST_IF" ]; then
HOST_IPS="$(ifconfig "$HOST_IF" 2>/dev/null | awk '/inet / { print $2 }')"
fi
if [ -z "$HOST_IPS" ]; then
HOST_IPS="$(ifconfig 2>/dev/null | awk '/inet / && $2 != "127.0.0.1" { print $2 }')"
fi
if [ -n "$HOST_IPS" ]; then
HOST_IPS_LINE="$(echo "$HOST_IPS" | tr '\n' ' ' | sed 's/ $//')"
_dialog --msgbox "\
DNS note: If you use *.home.arpa, create an A record for
${AGENT_DOMAIN} pointing to this host IP.
Detected IP(s): ${HOST_IPS_LINE}" 10 70
fi
fi
TZ=$(_dialog --inputbox \
"Timezone (e.g. Europe/Ljubljana):" 8 50 "UTC")
SYSTEM_LOCALE=$(_dialog --inputbox \
"Locale (e.g. en_US.UTF-8):" 8 50 "en_US.UTF-8")
KEYMAP=$(_dialog --inputbox \
"Console keymap (e.g. us):" 8 50 "us")
[ -z "${SYSTEM_LOCALE:-}" ] && SYSTEM_LOCALE="en_US.UTF-8"
DISPLAY_LOCALE="${SYSTEM_LOCALE}"
ASSISTANT_LOCALE="${SYSTEM_LOCALE}"
SSH_PUBLIC_KEY=$(_dialog --inputbox \
"SSH public key (optional — paste ssh-ed25519 or ssh-rsa):" 12 70 "")
# Defaults: all jails enabled, no local LLM (can be enabled post-install)
: "${AGENT_GENDER:=f}"
FEATURE_GIT="YES"
FEATURE_GITEA="NO"
CODE_HOSTING_MODE="git"
LOCAL_LLM_PROVIDER="none"
FEATURE_OLLAMA="NO"
FEATURE_LLAMA_CPP="NO"
FEATURE_OLLAMA_HPP="NO"
# Summary screen
SUMMARY_MSG="Configuration Summary:\n\n"
SUMMARY_MSG+="Name: ${ASSISTANT_NAME}\n"
SUMMARY_MSG+="Domain: ${AGENT_DOMAIN}\n"
SUMMARY_MSG+="Timezone: ${TZ}\n"
SUMMARY_MSG+="Locale: ${SYSTEM_LOCALE}\n"
SUMMARY_MSG+="Keymap: ${KEYMAP}\n"
SUMMARY_MSG+="SSH key: $([ -n "$SSH_PUBLIC_KEY" ] && echo "✓ Provided" || echo "✗ None")\n"
if [ "${FEATURE_TAILSCALE}" = "YES" ]; then
if [ -n "$TAILSCALE_AUTHKEY" ]; then
SUMMARY_MSG+="Tailscale: ✓ Enabled (auth key provided)\n"
else
SUMMARY_MSG+="Tailscale: ⚠ Enabled (no auth key - run 'tailscale up' later)\n"
fi
else
SUMMARY_MSG+="Tailscale: ✗ Disabled (SSH on public port 22)\n"
fi
SUMMARY_MSG+="Provider keys: configure after first boot in the controlplane\n"
SUMMARY_MSG+="Telegram: configure after first boot in the controlplane\n"
SUMMARY_MSG+="\nProceed with installation?"
if ! _dialog --yesno "$SUMMARY_MSG" 16 70; then
_dialog --msgbox "Installation cancelled. Rebooting..." 6 40
reboot
fi
step_done "wizard"
fi
export CLAWDIE_BOOT_MODE POOL_NAME
export ASSISTANT_NAME AGENT_GENDER AGENT_DOMAIN TZ SSH_PUBLIC_KEY
export HOSTNAME INSTALL_MODE PROFILE
export SYSTEM_LOCALE DISPLAY_LOCALE ASSISTANT_LOCALE KEYMAP
export EMBED_BASE_URL EMBED_MODEL EMBED_API_KEY EMBED_DIMENSIONS
export FEATURE_TAILSCALE TAILSCALE_AUTHKEY
export CODE_HOSTING_MODE FEATURE_GIT FEATURE_GITEA FORGEJO_DISK_ESTIMATE
export LOCAL_LLM_PROVIDER FEATURE_OLLAMA FEATURE_LLAMA_CPP FEATURE_OLLAMA_HPP
export OLLAMA_RAM_ESTIMATE OLLAMA_DISK_ESTIMATE LLAMA_CPP_RAM_ESTIMATE LLAMA_CPP_DISK_ESTIMATE
export USB_LLM_MODELS_PATH
export ZFS_POOL ZFS_LAYOUT ZFS_DATA_DISKS ZFS_HOT_SPARES ZFS_PREFIX
export NETWORK_EXTERNAL_IF NETWORK_INTERNAL_IF TAILSCALE_IF ZFS_DISKS ZFS_SPARE_DISKS GPU_DEVICE SND_DEVICE
# ── Run modules ────────────────────────────────────────────────────────────
log_msg "[firstboot] Running modules..."
fix(installer): Phase A — stable ZFS, safe upgrades, module matrix Four critical fixes before v1.0.0 VM test, informed by PC-BSD failure modes and GhostBSD's improvements: 1. shell-zfs.sh: zpool labelclear on fresh install Clear ZFS labels from every device that was in the old pool before bsdinstall writes new ones. Prevents the "can't find pool by GUID" boot failure that made PC-BSD reinstalls unreliable. 2. shell-zfs.sh: pre-upgrade snapshot When operator selects Upgrade, take zfs snapshot -r pool@pre-upgrade-{timestamp} before any changes. One reboot to roll back if the upgrade goes wrong. UPGRADE_SNAPSHOT exported for downstream modules to reference. 3. shell-env.sh: never overwrite secrets on upgrade clawdie_shell_env_generate() now checks CLAWDIE_BOOT_MODE. In upgrade mode it calls clawdie_shell_env_append_new_keys() instead of regenerating — reads existing .env and appends only keys that are absent. Existing DB passwords, JWT secrets, API keys are never touched. This fixes the root cause of the orphaned-database bug: new passwords that don't match the existing pool's data. 4. firstboot.sh: module execution matrix via run_step_if New run_step_if "<modes>" wrapper marks steps as done without running them when not applicable to the current boot mode. Upgrade skips: gpu, nvidia, ssh, system, desktop, pf, tailscale Upgrade runs: pkg, env (append-only), npm-globals, deploy Prevents SSH key resets, rc.conf overwrites, and firewall rewrites during upgrade — all of which undid operator customisations. Also adds INSTALLER-PLAN.md: full architecture plan for unified GUI/TUI installer with Fresh / Upgrade / Repair modes, boot environment support, and a clear phase roadmap to v1.1.0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 11:09:51 +00:00
# Module execution matrix — controls which modules run in each mode.
# fresh install: all modules
# upgrade: pkg + env (append-only) + npm-globals + deploy only
# repair: deploy only (targeted repair actions inside shell-deploy)
#
# GPU, SSH, system, desktop, PF, Tailscale are skipped on upgrade/repair —
# they would reset keys, overwrite rc.conf, and undo operator customisations.
run_step_if "install" "gpu" clawdie_shell_gpu_detect "GPU driver detection" 1
run_step_if "install" "nvidia" clawdie_shell_nvidia_detect "NVIDIA version selection" 2
run_step_if "install upgrade" "pkg" clawdie_shell_pkg_setup "Package repo configuration" 3
run_step_if "install" "ssh" clawdie_shell_ssh_setup "SSH keys + system passwords" 4
run_step_if "install upgrade" "env" clawdie_shell_env_generate "Generate .env with secrets" 5
run_step_if "install" "system" clawdie_shell_system_config "Hostname, rc.conf, services" 6
run_step_if "install" "desktop" clawdie_shell_desktop_detect "Desktop enablement" 7
run_step_if "install" "pf" clawdie_shell_pf "PF firewall + jail NAT" 8
run_step_if "install" "tailscale" clawdie_shell_tailscale_setup "Tailscale remote access" 8
run_step_if "install upgrade" "npm-globals" clawdie_shell_npm_globals_install "Install bundled npm CLIs (claude/gemini/pi)" 8
run_step_if "install upgrade" "deploy" clawdie_shell_deploy "Extract tarball + just install" 8
log_msg "[firstboot] Complete."
log_msg "[firstboot] Aider (primary harness): aider --help"
log_msg "[firstboot] Pi (primary harness): pi --help"
log_msg "[firstboot] Optional: Codex CLI (headless): codex login --device-auth"
log_msg "[firstboot] Optional: Codex CLI (API key): printenv OPENAI_API_KEY | codex login --with-api-key"