clawdie-iso/vps/firstboot-vps.sh

277 lines
9.7 KiB
Bash
Raw Normal View History

#!/bin/sh
# firstboot-vps.sh — Phase 1: Install FreeBSD to disk from mfsBSD
#
# Runs on mfsBSD (FreeBSD in RAM) after SSH login.
# Partitions disk, installs base FreeBSD, injects the clawdie-iso
# firstboot payload, and reboots. On first HDD boot, the standard
# firstboot.sh pipeline handles everything else (wizard, packages,
# GPU, .env, deploy).
#
# Usage:
# Interactive: /usr/local/share/clawdie-iso/firstboot-vps.sh
# Headless: copy clawdie.conf.tpl → clawdie.conf, edit, then run
#
# Flow:
# 1. Detect target disk
# 2. Optionally read clawdie.conf for pre-baked values
# 3. Partition disk (GPT: EFI + swap + ZFS)
# 4. Create ZFS pool "clawdie" with standard datasets
# 5. Install FreeBSD base + kernel
# 6. Inject firstboot payload (same as installerconfig on USB)
# 7. Install bootloader + reboot
#
# After reboot, firstboot.sh runs the modular shell-*.sh pipeline:
# zfs detect → wizard → gpu → pkg → ssh → env → system → tailscale → deploy
set -e
SHARE="${SHARE:-/usr/local/share/clawdie-iso}"
LOG="/var/log/clawdie-vps-install.log"
. "${SHARE}/build.cfg"
# ── Helpers <20><>──────────────────────────────────────────────────────────────
die() { echo "ERROR: $1" >&2; exit 1; }
log_vps() { echo "$(date '+%H:%M:%S') $1" | tee -a "$LOG"; }
gen_password() { openssl rand -base64 32 | tr -d '\n/+=' | head -c 24; }
detect_disk() {
for d in /dev/nda0 /dev/nvd0 /dev/da0 /dev/vtbd0 /dev/ada0; do
if [ -e "$d" ]; then
echo "$d"
return 0
fi
done
die "Cannot detect target disk"
}
# ── Pre-baked config (headless) ───────────────────────────────────────────
if [ -f "${SHARE}/clawdie.conf" ]; then
. "${SHARE}/clawdie.conf"
log_vps "[vps] Loaded clawdie.conf"
fi
# ── Detect disk ───────────────────────────────────────────────────────────
DISK=$(detect_disk)
log_vps "[vps] Target disk: ${DISK}"
# ── Interactive confirmation (if TTY available) ──────────────────────────
if [ -t 0 ] && command -v bsddialog >/dev/null 2>&1; then
_dialog() { bsddialog --backtitle "Clawdie-VPS Setup" "$@" 2>&1; }
_dialog --msgbox "\
Clawdie-VPS Installer
Target disk: ${DISK}
ALL DATA ON THIS DISK WILL BE ERASED.
After installation, the system will reboot and
run the standard Clawdie firstboot wizard." 12 60
if ! _dialog --yesno "\
Confirm: ERASE ALL DATA on ${DISK} and install FreeBSD?" 8 60; then
die "Cancelled."
fi
# Optional: pre-set TARGET for vps (skip wizard on reboot)
if [ -n "${ASSISTANT_NAME:-}" ] && [ -n "${AGENT_DOMAIN:-}" ] && [ -n "${TZ:-}" ]; then
_vps_target="vps"
_dialog --msgbox "\
Pre-baked config detected:
Agent: ${ASSISTANT_NAME}
Domain: ${AGENT_DOMAIN}
TZ: ${TZ}
Wizard will be skipped on first boot." 12 60
else
_vps_target="baremetal"
fi
elif [ -t 0 ]; then
echo "============================================"
echo " Clawdie-VPS Installer"
echo " Target disk: ${DISK}"
echo " ALL DATA WILL BE ERASED."
echo "============================================"
printf "Continue? (yes/no): "
read _confirm
[ "$_confirm" = "yes" ] || die "Cancelled."
_vps_target="baremetal"
else
# No TTY — require clawdie.conf
[ -n "${ASSISTANT_NAME:-}" ] || die "No TTY and no ASSISTANT_NAME in clawdie.conf"
_vps_target="vps"
fi
# ── Step 1: Partition disk ────────────────────────────────────────────────
log_vps "[vps] [1/5] Partitioning ${DISK}..."
gpart destroy -F "${DISK}" 2>/dev/null || true
gpart create -s gpt "${DISK}"
gpart add -t efi -s 260M -l boot "${DISK}"
gpart add -t freebsd-swap -s 2G -l swap "${DISK}"
gpart add -t freebsd-zfs -l zroot "${DISK}"
newfs_msdos /dev/gpt/boot
# ── Step 2: Create ZFS pool ──────────────────────────────────────────────
log_vps "[vps] [2/5] Creating ZFS pool 'clawdie'..."
ZFS_PART="${DISK}p3"
[ -e "/dev/gpt/zroot" ] && ZFS_PART="/dev/gpt/zroot"
zpool create -f -m none -R /mnt clawdie "$ZFS_PART"
zfs create -o mountpoint=none clawdie/ROOT
zfs create -o mountpoint=/ clawdie/ROOT/default
zfs create -o mountpoint=/tmp -o setuid=off clawdie/tmp
zfs create -o mountpoint=/usr -o canmount=off clawdie/usr
zfs create -o mountpoint=/home clawdie/home
zfs create -o mountpoint=/var -o canmount=off clawdie/var
zfs create -o mountpoint=/var/cache -o setuid=off clawdie/var/cache
zfs create -o mountpoint=/var/log -o setuid=off clawdie/var/log
zfs create -o mountpoint=/var/tmp -o setuid=off clawdie/var/tmp
zfs create -o mountpoint=/var/db clawdie/var/db
zfs set compression=lz4 clawdie
zfs set atime=off clawdie
# ── Step 3: Install FreeBSD base ─────────────────────────────────────────
log_vps "[vps] [3/5] Installing FreeBSD ${FREEBSD_VERSION} base..."
BASE_URL="https://download.freebsd.org/releases/${FREEBSD_ARCH}/${FREEBSD_VERSION}"
cd /tmp
fetch "${BASE_URL}/base.txz" || die "Failed to fetch base.txz"
fetch "${BASE_URL}/kernel.txz" || die "Failed to fetch kernel.txz"
tar -xpf base.txz -C /mnt
tar -xpf kernel.txz -C /mnt
# fstab
cat > /mnt/etc/fstab <<EOF
/dev/gpt/boot /boot/efi msdosfs rw 0 0
/dev/gpt/swap none swap sw 0 0
EOF
# loader.conf
cat > /mnt/boot/loader.conf <<EOF
zfs_load="YES"
EOF
# Minimal rc.conf (firstboot.sh will configure the rest)
cat > /mnt/etc/rc.conf <<EOF
zfs_enable="YES"
sshd_enable="YES"
ifconfig_DEFAULT="DHCP"
clawdie_firstboot_enable="YES"
EOF
# Timezone
chroot /mnt tzsetup "${TZ:-UTC}"
# Create clawdie user
chroot /mnt pw useradd -n clawdie -u 1001 -m -G wheel -s /bin/sh
_root_pw=$(gen_password)
_user_pw=$(gen_password)
echo "root:$(openssl passwd -6 "$_root_pw")" | chroot /mnt chpass -H root
echo "clawdie:$(openssl passwd -6 "$_user_pw")" | chroot /mnt chpass -H clawdie
# sudo for wheel
mkdir -p /mnt/usr/local/etc/sudoers.d
cat > /mnt/usr/local/etc/sudoers.d/wheel <<EOF
%wheel ALL=(ALL) NOPASSWD: ALL
EOF
chmod 440 /mnt/usr/local/etc/sudoers.d/wheel
# ── Step 4: Inject firstboot payload ─────────────────────────────────────
# Same as installerconfig does for USB — copy firstboot scripts + packages + tarball
log_vps "[vps] [4/5] Injecting firstboot payload..."
HDD_SHARE="/mnt/usr/local/share/clawdie-iso"
HDD_RCD="/mnt/usr/local/etc/rc.d"
mkdir -p "$HDD_SHARE"
cp -r "${SHARE}/firstboot" "${HDD_SHARE}/"
cp "${SHARE}/build.cfg" "${HDD_SHARE}/"
# Override TARGET in the HDD copy of build.cfg if pre-baked
if [ "$_vps_target" = "vps" ]; then
sed -i '' "s/^TARGET=.*/TARGET=\"vps\"/" "${HDD_SHARE}/build.cfg"
# Bake identity vars
[ -n "${ASSISTANT_NAME:-}" ] && sed -i '' "s/^ASSISTANT_NAME=.*/ASSISTANT_NAME=\"${ASSISTANT_NAME}\"/" "${HDD_SHARE}/build.cfg"
[ -n "${AGENT_DOMAIN:-}" ] && sed -i '' "s/^AGENT_DOMAIN=.*/AGENT_DOMAIN=\"${AGENT_DOMAIN}\"/" "${HDD_SHARE}/build.cfg"
[ -n "${TZ:-}" ] && sed -i '' "s|^TZ=.*|TZ=\"${TZ}\"|" "${HDD_SHARE}/build.cfg"
[ -n "${AGENT_GENDER:-}" ] && sed -i '' "s/^AGENT_GENDER=.*/AGENT_GENDER=\"${AGENT_GENDER}\"/" "${HDD_SHARE}/build.cfg"
log_vps "[vps] Pre-baked config written to build.cfg (TARGET=vps)"
fi
# Copy tarball
if [ -f "${SHARE}/clawdie-ai.tar.gz" ]; then
cp "${SHARE}/clawdie-ai.tar.gz" "${HDD_SHARE}/"
log_vps "[vps] Clawdie-AI tarball copied"
else
log_vps "[vps] WARNING: No clawdie-ai.tar.gz found — deploy step will fail"
fi
# Copy packages if bundled
if [ -d "${SHARE}/packages" ]; then
cp -r "${SHARE}/packages" "${HDD_SHARE}/"
log_vps "[vps] Package repo copied"
fi
# Make scripts executable
chmod +x "${HDD_SHARE}/firstboot/firstboot.sh"
for sh in "${HDD_SHARE}/firstboot/shell-"*.sh; do
chmod +x "$sh"
done
chmod +x "${HDD_SHARE}/firstboot/zfs-pool-detect.sh" 2>/dev/null || true
chmod +x "${HDD_SHARE}/firstboot/zfs-pool-migrate.sh" 2>/dev/null || true
chmod +x "${HDD_SHARE}/firstboot/maintenance-mode.sh" 2>/dev/null || true
# Install rc.d service
mkdir -p "$HDD_RCD"
cp "${SHARE}/firstboot/rc.d/clawdie-firstboot" "${HDD_RCD}/clawdie-firstboot"
chmod +x "${HDD_RCD}/clawdie-firstboot"
# ── Step 5: Install bootloader + reboot ──────────────────────────────────
log_vps "[vps] [5/5] Installing EFI bootloader..."
[ -f /mnt/boot/loader.efi ] || die "loader.efi not found after base extraction"
mkdir -p /mnt/boot/efi
mount -t msdosfs /dev/gpt/boot /mnt/boot/efi
mkdir -p /mnt/boot/efi/EFI/BOOT
cp /mnt/boot/loader.efi /mnt/boot/efi/EFI/BOOT/BOOTX64.EFI
umount /mnt/boot/efi
zpool set bootfs=clawdie/ROOT/default clawdie
log_vps "[vps] Installation complete."
echo ""
echo "============================================"
echo " FreeBSD installed to ${DISK}"
echo " Pool: clawdie"
echo ""
echo " On first HDD boot, the Clawdie firstboot"
echo " wizard will run automatically."
echo ""
echo " Root password: ${_root_pw}"
echo " User password: ${_user_pw}"
echo ""
echo " SAVE THESE PASSWORDS NOW — they are not logged."
echo "============================================"
echo ""
# Export and reboot
log_vps "[vps] Exporting pool and rebooting in 10 seconds..."
sleep 10
umount /mnt/boot/efi 2>/dev/null || true
zpool export clawdie
reboot