Rewrite vps/firstboot-vps.sh as phase-1 only: partition disk, create ZFS pool "clawdie", install FreeBSD base, inject firstboot payload, install bootloader, reboot. On first HDD boot the standard firstboot.sh modular pipeline runs (zfs detect, wizard, gpu, pkg, ssh, env, system, tailscale, deploy). Pre-baked clawdie.conf values get written to build.cfg with TARGET=cloud so the wizard is skipped. Pool named "clawdie" (not zroot) for pool detection compatibility. Remove duplicate clawdie-vps-setup.sh. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
274 lines
9.6 KiB
Bash
274 lines
9.6 KiB
Bash
#!/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 cloud (skip wizard on reboot)
|
||
if [ -n "${ASSISTANT_NAME:-}" ] && [ -n "${AGENT_DOMAIN:-}" ] && [ -n "${TZ:-}" ]; then
|
||
_vps_target="cloud"
|
||
_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="cloud"
|
||
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" = "cloud" ]; then
|
||
sed -i '' "s/^TARGET=.*/TARGET=\"cloud\"/" "${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=cloud)"
|
||
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..."
|
||
|
||
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 " (also available in ${LOG})"
|
||
echo "============================================"
|
||
echo ""
|
||
log_vps "[vps] root=${_root_pw} clawdie=${_user_pw}"
|
||
|
||
# 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
|