770 lines
21 KiB
Markdown
770 lines
21 KiB
Markdown
# Clawdie Shell Modules — Implementation Reference
|
||
|
||
**Version:** v0.9.0
|
||
**Implementation Date:** 26.mar.2026
|
||
**Status:** ✅ Complete (12/12 modules)
|
||
|
||
---
|
||
|
||
## Overview
|
||
|
||
The Clawdie Shell comprises 12 POSIX-compliant shell modules that orchestrate the complete installation and configuration of Clawdie-AI from ISO boot to running system.
|
||
|
||
Each module is independent and can be sourced individually for testing. Together, they form the firstboot pipeline that executes on first HDD boot.
|
||
|
||
---
|
||
|
||
## Module Index
|
||
|
||
| Module | Function | Lines | Purpose | Status |
|
||
|--------|----------|-------|---------|--------|
|
||
| shell-zfs.sh | `clawdie_shell_zfs_detect()` | 192 | ZFS pool detection | ✅ Complete |
|
||
| shell-gpu.sh | `clawdie_shell_gpu_detect()` | 184 | GPU detection + kld_list | ✅ Complete |
|
||
| shell-nvidia.sh | `clawdie_shell_nvidia_detect()` | 229 | NVIDIA driver versioning | ✅ Complete |
|
||
| shell-pkg.sh | `clawdie_shell_pkg_setup()` | 199 | Package repos + USB cache | ✅ Complete |
|
||
| shell-ssh.sh | `clawdie_shell_ssh_setup()` | 260 | SSH keys + system passwords | ✅ Complete |
|
||
| shell-env.sh | `clawdie_shell_env_generate()` | 192 | .env + secrets generation | ✅ Complete |
|
||
| shell-system.sh | `clawdie_shell_system_config()` | 233 | rc.conf + hostname + services | ✅ Complete |
|
||
| shell-desktop.sh | `clawdie_shell_desktop_detect()` | 101 | Desktop detection + enablement | ✅ Complete |
|
||
| shell-pf.sh | `clawdie_shell_pf()` | 148 | PF firewall + jail NAT | ✅ Complete |
|
||
| shell-tailscale.sh | `clawdie_shell_tailscale_setup()` | 121 | Tailscale remote access (optional) | ✅ Complete |
|
||
| shell-npm-globals.sh | `clawdie_shell_npm_globals_install()` | 101 | Bundled npm CLIs | ✅ Complete |
|
||
| shell-deploy.sh | `clawdie_shell_deploy()` | 424 | Tarball extract + just install | ✅ Complete |
|
||
|
||
**Total:** 2,384 lines of production code (POSIX shell, no bash-isms)
|
||
|
||
---
|
||
|
||
## Module: shell-gpu.sh
|
||
|
||
**File:** `firstboot/shell-gpu.sh`
|
||
**Main Function:** `clawdie_shell_gpu_detect()`
|
||
|
||
### Purpose
|
||
|
||
Detect the system's GPU (if any) and configure appropriate kernel modules for X11/Wayland rendering. Writes `kld_list` to `/etc/rc.conf`.
|
||
|
||
### Entry Point
|
||
|
||
```bash
|
||
. firstboot/shell-gpu.sh
|
||
clawdie_shell_gpu_detect
|
||
```
|
||
|
||
### Functions
|
||
|
||
#### `clawdie_shell_gpu_detect()`
|
||
Main orchestrator. Detects GPU vendor via `pciconf`, matches to driver, writes rc.conf.
|
||
|
||
**Sets:** `DETECTED_GPU` (intel, amd, nvidia, vmware, or vesa)
|
||
**Side effects:** Writes to rc.conf, progress file, log file
|
||
|
||
#### `clawdie_shell_gpu_detect_pci()`
|
||
Queries `pciconf -lv` to find VGA device (class 0x030000).
|
||
|
||
**Returns:** GPU vendor string (intel, amd, nvidia, vmware, or empty)
|
||
|
||
#### `clawdie_shell_gpu_match_driver(vendor)`
|
||
Maps vendor to FreeBSD kernel module list.
|
||
|
||
**Mappings:**
|
||
| Vendor | Module | Note |
|
||
|--------|--------|------|
|
||
| intel | i915kms | Intel integrated GPU (iGPU) |
|
||
| amd | amdgpu | AMD Radeon / RDNA |
|
||
| nvidia | nvidia-modeset nvidia | NVIDIA discrete GPU (see shell-nvidia.sh for version) |
|
||
| vmware | vmwgfx | VMware SVGA 3D |
|
||
| (other) | (empty) | VESA fallback, no kld needed |
|
||
|
||
#### `clawdie_shell_gpu_write_rcconf(kld_list)`
|
||
Writes `kld_list="..."` to `/etc/rc.conf`, replacing any existing kld_list.
|
||
|
||
**Side effects:**
|
||
- Removes existing kld_list (idempotent)
|
||
- Appends new kld_list entry
|
||
- Skips if kld_list is empty (VESA case)
|
||
|
||
#### `clawdie_shell_gpu_validate()`
|
||
Verifies rc.conf has valid kld_list.
|
||
|
||
### Environment Variables
|
||
|
||
**Input (from caller):**
|
||
- (none required; uses pciconf on system)
|
||
|
||
**Output (sets for downstream modules):**
|
||
- `DETECTED_GPU` — GPU vendor (used by shell-nvidia.sh)
|
||
|
||
**Configuration (can override for testing):**
|
||
- `RC_CONF` (default: `/etc/rc.conf`)
|
||
- `LOG_FILE` (default: `/var/log/clawdie-firstboot.log`)
|
||
- `PROGRESS_FILE` (default: `/var/log/clawdie-firstboot.progress`)
|
||
|
||
### Error Handling
|
||
|
||
Returns 0 on success. Returns 1 on critical failure (e.g., can't write rc.conf).
|
||
|
||
Gracefully handles:
|
||
- Missing pciconf output → defaults to VESA
|
||
- Unknown GPU vendor → defaults to VESA
|
||
- Read-only /etc/rc.conf → returns error
|
||
|
||
### Testing
|
||
|
||
```bash
|
||
# Quick test
|
||
export RC_CONF="/tmp/test-rc.conf"
|
||
export LOG_FILE="/tmp/test.log"
|
||
. firstboot/shell-gpu.sh
|
||
clawdie_shell_gpu_detect
|
||
cat /tmp/test-rc.conf # Should have kld_list
|
||
```
|
||
|
||
---
|
||
|
||
## Module: shell-nvidia.sh
|
||
|
||
**File:** `firstboot/shell-nvidia.sh`
|
||
**Main Function:** `clawdie_shell_nvidia_detect()`
|
||
|
||
### Purpose
|
||
|
||
For NVIDIA GPUs, detect the specific GPU model and select the correct driver version (590, 470, or 390). Modern GPUs use 590; Maxwell-era use 470; Kepler and older use 390.
|
||
|
||
### Entry Point
|
||
|
||
```bash
|
||
export DETECTED_GPU="nvidia" # Set by shell-gpu.sh
|
||
. firstboot/shell-nvidia.sh
|
||
clawdie_shell_nvidia_detect
|
||
```
|
||
|
||
### Functions
|
||
|
||
#### `clawdie_shell_nvidia_detect()`
|
||
Main orchestrator. Only executes if `DETECTED_GPU=nvidia`.
|
||
|
||
**Sets:** `NVIDIA_DRIVER` (590, 470, or 390)
|
||
**Side effects:** Writes updated kld_list to rc.conf
|
||
|
||
#### `clawdie_shell_nvidia_get_device_id()`
|
||
Queries pciconf for NVIDIA GPU device ID (PCI vendor 0x10de).
|
||
|
||
**Returns:** 4-digit hex device ID (e.g., "2388" for RTX 4090)
|
||
|
||
#### `clawdie_shell_nvidia_select_driver(device_id)`
|
||
Maps device ID to driver version using lookup table.
|
||
|
||
**Device ranges:**
|
||
| Range | GPU Era | Driver |
|
||
|-------|---------|--------|
|
||
| 0x2700+ (≥9984) | Ada (RTX 40xx) | 590 |
|
||
| 0x2060–0x2500 (8288–9472) | Turing/Ampere (RTX 20xx/30xx) | 590 |
|
||
| 0x1340–0x2186 (4928–8582) | Maxwell (GTX 750–980 Ti) | 470 |
|
||
| 0x1180–0x139F (4480–5023) | Kepler (GTX 650–780 Ti) | 390 |
|
||
| (unknown) | — | 590 (safe default) |
|
||
|
||
#### `clawdie_shell_nvidia_write_rcconf(nvidia_version)`
|
||
Updates rc.conf with correct nvidia-driver and nvidia-modeset modules.
|
||
|
||
**Side effects:**
|
||
- Removes any existing nvidia entries from kld_list
|
||
- Adds `nvidia-modeset nvidia` at front of kld_list
|
||
- Preserves other kld modules (e.g., i915kms from dual GPU)
|
||
|
||
#### `clawdie_shell_nvidia_validate()`
|
||
Checks that nvidia is in rc.conf.
|
||
|
||
### Environment Variables
|
||
|
||
**Input (required):**
|
||
- `DETECTED_GPU` — Must equal "nvidia" to execute
|
||
|
||
**Output (sets):**
|
||
- `NVIDIA_DRIVER` — Selected version (590, 470, or 390)
|
||
|
||
**Configuration (can override):**
|
||
- `RC_CONF` (default: `/etc/rc.conf`)
|
||
- `LOG_FILE` (default: `/var/log/clawdie-firstboot.log`)
|
||
- `PROGRESS_FILE` (default: `/var/log/clawdie-firstboot.progress`)
|
||
|
||
### Fallback Behavior
|
||
|
||
- No GPU detected → Silently returns 0 (NVIDIA module skipped)
|
||
- Unknown device ID → Defaults to driver 590
|
||
|
||
### Testing
|
||
|
||
```bash
|
||
# Test with simulated NVIDIA GPU
|
||
export DETECTED_GPU="nvidia"
|
||
export RC_CONF="/tmp/test-rc.conf"
|
||
. firstboot/shell-nvidia.sh
|
||
clawdie_shell_nvidia_detect
|
||
cat /tmp/test-rc.conf # Should have nvidia-modeset nvidia
|
||
```
|
||
|
||
---
|
||
|
||
## Module: shell-pkg.sh
|
||
|
||
**File:** `firstboot/shell-pkg.sh`
|
||
**Main Function:** `clawdie_shell_pkg_setup()`
|
||
|
||
### Purpose
|
||
|
||
Configure FreeBSD package repositories (online and offline USB) and seed Bastille jail cache with pre-downloaded packages for offline provisioning.
|
||
|
||
### Entry Point
|
||
|
||
```bash
|
||
. firstboot/shell-pkg.sh
|
||
clawdie_shell_pkg_setup
|
||
```
|
||
|
||
### Functions
|
||
|
||
#### `clawdie_shell_pkg_setup()`
|
||
Main orchestrator. Detects ABI, writes repo configs, seeds cache.
|
||
|
||
**Returns:** 0 on success, 1 on critical error
|
||
**Side effects:** Creates /etc/pkg/repos, /var/cache/pkg/bastille directories
|
||
|
||
#### `clawdie_shell_pkg_detect_abi()`
|
||
Queries `pkg config abi` or derives from `uname`.
|
||
|
||
**Returns:** ABI string like "FreeBSD:15:amd64"
|
||
|
||
#### `clawdie_shell_pkg_write_freebsd_conf(abi)`
|
||
Writes `/etc/pkg/repos/FreeBSD.conf` pointing to pkg.FreeBSD.org.
|
||
|
||
**Uses:** `DEFAULT_PKG_BRANCH` from build.cfg (latest or quarterly)
|
||
|
||
**Output file format:**
|
||
```
|
||
FreeBSD: {
|
||
url: "pkg+https://pkg.FreeBSD.org/${ABI}/latest",
|
||
mirror_type: "srv",
|
||
enabled: yes
|
||
}
|
||
```
|
||
|
||
#### `clawdie_shell_pkg_write_clawdie_conf()`
|
||
Writes `/etc/pkg/repos/Clawdie-USB.conf` pointing to bundled USB packages.
|
||
|
||
**Output file format:**
|
||
```
|
||
Clawdie-USB: {
|
||
url: "file:///usr/local/share/clawdie-iso/packages",
|
||
mirror_type: "none",
|
||
enabled: yes,
|
||
priority: 100
|
||
}
|
||
```
|
||
|
||
Priority 100 ensures USB packages take precedence if online repo is also available.
|
||
|
||
#### `clawdie_shell_pkg_seed_cache()`
|
||
Copies all .pkg files from USB to `/var/cache/pkg/bastille`.
|
||
|
||
**Purpose:** Allows jails to install packages offline after first boot
|
||
|
||
**Side effects:**
|
||
- Creates /var/cache/pkg/bastille if missing
|
||
- Copies *.pkg files (non-fatal if USB path missing)
|
||
|
||
### Environment Variables
|
||
|
||
**Input (optional):**
|
||
- `DEFAULT_PKG_BRANCH` — "latest" or "quarterly" (from build.cfg)
|
||
|
||
**Configuration (can override):**
|
||
- `PKG_CONF_DIR` (default: `/etc/pkg/repos`)
|
||
- `FREEBSD_REPO_CONF` (default: `/etc/pkg/repos/FreeBSD.conf`)
|
||
- `CLAWDIE_USB_REPO_CONF` (default: `/etc/pkg/repos/Clawdie-USB.conf`)
|
||
- `USB_PKG_PATH` (default: `/usr/local/share/clawdie-iso/packages`)
|
||
- `BASTILLE_PKG_CACHE` (default: `/var/cache/pkg/bastille`)
|
||
- `LOG_FILE`, `PROGRESS_FILE`
|
||
|
||
### Error Handling
|
||
|
||
Non-fatal errors:
|
||
- Missing /etc/pkg/repos directory → created
|
||
- Missing USB packages → skipped with warning
|
||
- FreeBSD repo already exists → replaced
|
||
|
||
Returns 1 only if critical failure (e.g., can't write config).
|
||
|
||
### Testing
|
||
|
||
```bash
|
||
# Test with mock directories
|
||
export PKG_CONF_DIR="/tmp/test-repos"
|
||
export USB_PKG_PATH="/tmp/fake-usb/packages"
|
||
mkdir -p "$USB_PKG_PATH"
|
||
touch "$USB_PKG_PATH/test.pkg"
|
||
|
||
. firstboot/shell-pkg.sh
|
||
clawdie_shell_pkg_setup
|
||
|
||
# Verify
|
||
cat /tmp/test-repos/FreeBSD.conf
|
||
cat /tmp/test-repos/Clawdie-USB.conf
|
||
```
|
||
|
||
---
|
||
|
||
## Module: shell-env.sh
|
||
|
||
**File:** `firstboot/shell-env.sh`
|
||
**Main Function:** `clawdie_shell_env_generate()`
|
||
|
||
### Purpose
|
||
|
||
Generate `.env` configuration file with secrets, identity variables, and jail IP allocation. This file is used by Clawdie-AI at runtime.
|
||
|
||
### Entry Point
|
||
|
||
```bash
|
||
export ASSISTANT_NAME="MyAssistant"
|
||
export AGENT_DOMAIN="clawdie.local"
|
||
export TZ="Europe/Ljubljana"
|
||
. firstboot/shell-env.sh
|
||
clawdie_shell_env_generate
|
||
```
|
||
|
||
### Functions
|
||
|
||
#### `clawdie_shell_env_generate()`
|
||
Main orchestrator. Validates inputs, generates secrets, writes .env file.
|
||
|
||
**Requires:**
|
||
- `ASSISTANT_NAME` (from wizard)
|
||
- `AGENT_DOMAIN` (from wizard)
|
||
- `TZ` (from wizard, defaults to UTC)
|
||
|
||
**Side effects:**
|
||
- Creates /home/clawdie/.env
|
||
- Sets permissions 600
|
||
- Changes owner to clawdie:clawdie
|
||
|
||
#### `clawdie_shell_env_write_file()`
|
||
Generates complete .env file with:
|
||
- Identity (ASSISTANT_NAME, AGENT_DOMAIN, TZ)
|
||
- Auto-generated secrets (JWT_SECRET, API_KEY, DB_PASSWORD, REDIS_PASSWORD)
|
||
- Jail IP allocation (derived from AGENT_SUBNET_BASE)
|
||
- Feature flags (FEATURE_MANAGEMENT_JAIL, FEATURE_OLLAMA, FEATURE_TELEGRAM)
|
||
- LLM provider config
|
||
- System metadata
|
||
|
||
**File format:** Shell-compatible KEY="value" (can be sourced in other scripts)
|
||
|
||
**Variables generated:**
|
||
- Identity: ASSISTANT_NAME, AGENT_NAME, AGENT_DOMAIN, TZ
|
||
- Secrets: JWT_SECRET, ANTHROPIC_API_KEY, DB_PASSWORD, REDIS_PASSWORD (32-char base64 each)
|
||
- Network: AGENT_SUBNET_BASE, AGENT_GATEWAY_IP, MGMT_JAIL_IP, DB_JAIL_IP, GIT_JAIL_IP, CMS_JAIL_IP, WORKER_JAIL_IP_START
|
||
- Database: DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD
|
||
- Features: FEATURE_MANAGEMENT_JAIL, FEATURE_OLLAMA, FEATURE_TELEGRAM
|
||
- Telegram: TELEGRAM_BOT_TOKEN, TELEGRAM_ADMIN_IDS (optional, blank initially)
|
||
- LLM: LLM_PROVIDER
|
||
- SSH: SSH_PUBLIC_KEY (if provided at install)
|
||
- System: FREEBSD_VERSION, INSTALL_DATE
|
||
|
||
#### `clawdie_shell_env_gen_secret()`
|
||
Generates random 32-character secrets suitable for JWT/API keys.
|
||
|
||
**Method:** `openssl rand -base64 32` with fallback to /dev/urandom
|
||
|
||
**Returns:** 32-char base64 string (alphanumeric + +/)
|
||
|
||
#### `clawdie_shell_env_validate()`
|
||
Verifies .env file has:
|
||
- Correct permissions (600)
|
||
- All required variables
|
||
- Non-empty secret values
|
||
|
||
### Environment Variables
|
||
|
||
**Input (required):**
|
||
- `ASSISTANT_NAME` — Display name for agent
|
||
- `AGENT_DOMAIN` — FQDN for internal network
|
||
- `TZ` — Timezone (e.g., Europe/Ljubljana)
|
||
|
||
**Input (optional):**
|
||
- `SSH_PUBLIC_KEY` — User's SSH public key (if provided at install)
|
||
|
||
**Configuration (can override):**
|
||
- `CLAWDIE_HOME` (default: `/home/clawdie`)
|
||
- `ENV_FILE` (default: `/home/clawdie/.env`)
|
||
- `AGENT_SUBNET_BASE` (default: `10.0.0`)
|
||
- `LOG_FILE`, `PROGRESS_FILE`
|
||
|
||
### Jail IP Allocation
|
||
|
||
IPs are auto-derived from AGENT_SUBNET_BASE:
|
||
|
||
| Slot | Service | IP |
|
||
|------|---------|-----|
|
||
| .1 | Gateway (host bridge) | 10.0.0.1 |
|
||
| .2 | Management jail | 10.0.0.2 |
|
||
| .3 | Database (PostgreSQL) | 10.0.0.3 |
|
||
| .4 | Code service (git mirror) | 10.0.0.4 |
|
||
| .5 | Web service (nginx) | 10.0.0.5 |
|
||
| .101+ | Worker jails (dynamic) | 10.0.0.101+ |
|
||
|
||
### Testing
|
||
|
||
```bash
|
||
# Test .env generation
|
||
export ASSISTANT_NAME="TestAgent"
|
||
export AGENT_DOMAIN="test.local"
|
||
export TZ="UTC"
|
||
export CLAWDIE_HOME="/tmp/test-clawdie"
|
||
|
||
. firstboot/shell-env.sh
|
||
clawdie_shell_env_generate
|
||
|
||
# Verify
|
||
cat /tmp/test-clawdie/.env
|
||
wc -l /tmp/test-clawdie/.env # Should have ~45 lines
|
||
grep "JWT_SECRET" /tmp/test-clawdie/.env
|
||
```
|
||
|
||
---
|
||
|
||
## Module: shell-deploy.sh
|
||
|
||
**File:** `firstboot/shell-deploy.sh`
|
||
**Main Function:** `clawdie_shell_deploy()`
|
||
|
||
### Purpose
|
||
|
||
Extract Clawdie-AI tarball to /home/clawdie/ and run `just install`, which provisions jails, installs databases, configures services.
|
||
|
||
### Entry Point
|
||
|
||
```bash
|
||
. firstboot/shell-deploy.sh
|
||
clawdie_shell_deploy
|
||
```
|
||
|
||
### Functions
|
||
|
||
#### `clawdie_shell_deploy()`
|
||
Main orchestrator. Extracts tarball, ensures dependencies exist (offline tarball includes `node_modules`), runs installer, verifies services.
|
||
|
||
**Requires:**
|
||
- `ASSISTANT_NAME`, `AGENT_DOMAIN`, `TZ` (from env module)
|
||
|
||
**Returns:** 0 on success, 1 on failure
|
||
**Side effects:** Creates /home/clawdie/clawdie-ai, provisions jails, starts services
|
||
|
||
#### `clawdie_shell_deploy_extract_tarball()`
|
||
Extracts clawdie-ai.tar.gz to /home/clawdie/.
|
||
|
||
**Tarball location:** `/usr/local/share/clawdie-iso/clawdie-ai.tar.gz`
|
||
|
||
**Side effects:**
|
||
- Creates /home/clawdie/clawdie-ai
|
||
- Renames Clawdie-AI/ → clawdie-ai/ if needed
|
||
- Fixes ownership to clawdie:clawdie
|
||
|
||
#### `clawdie_shell_deploy_run_install_all()`
|
||
Executes `just install` from /home/clawdie/clawdie-ai/.
|
||
|
||
**What it does (from Clawdie-AI's package.json):**
|
||
- Uses bundled `node_modules` (offline); if missing, deploy falls back to `npm ci` (network required)
|
||
- Creates and starts Bastille jails (worker, db, git, cms)
|
||
- Sets up PostgreSQL in db jail
|
||
- Configures nginx in cms jail
|
||
- Installs rc.d service script for clawdie agent
|
||
- Enables service
|
||
|
||
**Runs as:** clawdie user (drops from root if needed via `su - clawdie`)
|
||
|
||
#### `clawdie_shell_deploy_verify()`
|
||
Checks post-install state:
|
||
- At least one jail running (jls -N)
|
||
- clawdie service enabled in rc.conf
|
||
- .env file exists
|
||
|
||
**Warnings:** Non-fatal if any check fails (jails may still be starting)
|
||
|
||
### Environment Variables
|
||
|
||
**Input (optional):**
|
||
- `ASSISTANT_NAME`, `AGENT_DOMAIN`, `TZ` (from env module if available)
|
||
|
||
**Configuration (can override):**
|
||
- `CLAWDIE_HOME` (default: `/home/clawdie`)
|
||
- `CLAWDIE_AI_DIR` (default: `/home/clawdie/clawdie-ai`)
|
||
- `CLAWDIE_TARBALL` (default: `/usr/local/share/clawdie-iso/clawdie-ai.tar.gz`)
|
||
- `ENV_FILE` (default: `/home/clawdie/.env`)
|
||
- `LOG_FILE`, `PROGRESS_FILE`
|
||
|
||
### Error Handling
|
||
|
||
Returns 1 (fails) if:
|
||
- Tarball not found
|
||
- Extraction fails
|
||
- package.json not found after extraction
|
||
- just install returns error
|
||
|
||
Returns 0 (succeeds) even if:
|
||
- Verification finds missing jails (they may still be starting)
|
||
- .env file missing (it's created by env module, but this module doesn't require it)
|
||
|
||
### Testing
|
||
|
||
```bash
|
||
# Minimal test (mock)
|
||
export CLAWDIE_HOME="/tmp/test-clawdie"
|
||
export CLAWDIE_AI_DIR="$CLAWDIE_HOME/clawdie-ai"
|
||
mkdir -p "$CLAWDIE_AI_DIR"
|
||
|
||
# Create mock package.json
|
||
cat > "$CLAWDIE_AI_DIR/package.json" <<'EOF'
|
||
{"name":"clawdie-ai","version":"0.9.0","scripts":{"install":"echo 'Mock install'"}}
|
||
EOF
|
||
|
||
. firstboot/shell-deploy.sh
|
||
clawdie_shell_deploy
|
||
|
||
# Should complete without errors
|
||
```
|
||
|
||
---
|
||
|
||
## Module: shell-system.sh
|
||
|
||
**File:** `firstboot/shell-system.sh`
|
||
**Main Function:** `clawdie_shell_system_config()`
|
||
|
||
### Purpose
|
||
|
||
Configure system-level settings: timezone, hostname, rc.conf services (dbus, hald, seatd, lightdm), and Lumina desktop environment.
|
||
|
||
### Functions
|
||
|
||
#### `clawdie_shell_system_config()`
|
||
Main orchestrator.
|
||
|
||
**Requires:**
|
||
- `TZ` (timezone from wizard)
|
||
- `AGENT_DOMAIN` (from wizard, used as hostname)
|
||
|
||
**Side effects:**
|
||
- Writes /etc/rc.conf
|
||
- Writes /etc/hostname
|
||
- Creates /etc/profile.d/clawdie.sh
|
||
- Enables services
|
||
|
||
#### `clawdie_shell_system_write_rcconf()`
|
||
Configures rc.conf with:
|
||
- Timezone
|
||
- Service enables (dbus, hald, seatd, lightdm)
|
||
- Lumina desktop settings
|
||
|
||
#### `clawdie_shell_system_set_hostname()`
|
||
Sets hostname from AGENT_DOMAIN
|
||
|
||
#### `clawdie_shell_system_setup_env()`
|
||
Creates /etc/profile.d/clawdie.sh with environment variables
|
||
|
||
#### `clawdie_shell_system_enable_services()`
|
||
Enables required services in rc.conf:
|
||
- dbus (message bus)
|
||
- hald (hardware abstraction)
|
||
- seatd (seat management for Wayland)
|
||
- lightdm (display manager for Lumina)
|
||
|
||
---
|
||
|
||
## Module: shell-ssh.sh
|
||
|
||
**File:** `firstboot/shell-ssh.sh`
|
||
**Main Function:** `clawdie_shell_ssh_setup()`
|
||
|
||
### Purpose
|
||
|
||
Configure SSH access and system user passwords. If SSH key is provided, disables password auth. Otherwise, generates secure password for clawdie user.
|
||
|
||
### Functions
|
||
|
||
#### `clawdie_shell_ssh_setup()`
|
||
Main orchestrator.
|
||
|
||
**Optional input:**
|
||
- `SSH_PUBLIC_KEY` (from install, if provided)
|
||
|
||
**Configures:**
|
||
- SSH keys for clawdie user
|
||
- System user passwords (root + clawdie)
|
||
- rc.conf for sshd
|
||
|
||
---
|
||
|
||
## Module: shell-zfs.sh
|
||
|
||
**Purpose:** Detect existing ZFS pools and choose boot mode
|
||
|
||
**Main Function:** `clawdie_shell_zfs_detect()`
|
||
|
||
**Outputs:**
|
||
- `CLAWDIE_BOOT_MODE` — install | upgrade | maintenance
|
||
- `POOL_NAME` — detected pool name (if present)
|
||
|
||
---
|
||
|
||
## Module: shell-desktop.sh
|
||
|
||
**Purpose:** Detect display hardware and enable desktop stack when appropriate
|
||
|
||
**Main Function:** `clawdie_shell_desktop_detect()`
|
||
|
||
**Outputs:**
|
||
- Desktop enablement flags in rc.conf (headless when skipped)
|
||
|
||
---
|
||
|
||
## Module: shell-pf.sh
|
||
|
||
**Purpose:** Configure PF firewall, NAT, and glasspane VNC rules
|
||
|
||
**Main Function:** `clawdie_shell_pf()`
|
||
|
||
**Outputs:**
|
||
- `/etc/pf.conf`, `/usr/local/etc/rc.d/pf_reload`, rc.conf PF settings
|
||
|
||
---
|
||
|
||
## Module: shell-tailscale.sh
|
||
|
||
**Purpose:** Optional Tailscale install + enablement for remote access
|
||
|
||
**Main Function:** `clawdie_shell_tailscale_setup()`
|
||
|
||
**Outputs:**
|
||
- tailscaled enablement in rc.conf, optional auth flow
|
||
|
||
---
|
||
|
||
## Module: shell-npm-globals.sh
|
||
|
||
**Purpose:** Install bundled npm CLI tools from ISO cache
|
||
|
||
**Main Function:** `clawdie_shell_npm_globals_install()`
|
||
|
||
**Outputs:**
|
||
- `/opt/clawdie/npm-global/bin` populated with CLI binaries
|
||
|
||
---
|
||
|
||
## Execution Order (firstboot.sh)
|
||
|
||
The modules execute in this sequence (see firstboot.sh run_step block):
|
||
|
||
```
|
||
1. clawdie_shell_zfs_detect → Sets CLAWDIE_BOOT_MODE (baremetal only)
|
||
2. clawdie_shell_gpu_detect → Sets DETECTED_GPU
|
||
3. clawdie_shell_nvidia_detect → Sets NVIDIA_DRIVER_VERSION
|
||
4. clawdie_shell_pkg_setup → Configures repos
|
||
5. clawdie_shell_ssh_setup → SSH + passwords
|
||
6. clawdie_shell_env_generate → Generates .env
|
||
7. clawdie_shell_system_config → Configures system
|
||
8. clawdie_shell_desktop_detect → Desktop enablement
|
||
9. clawdie_shell_pf → PF firewall + NAT
|
||
10. clawdie_shell_tailscale_setup → Tailscale remote access
|
||
11. clawdie_shell_npm_globals_install → Bundled npm CLIs
|
||
12. clawdie_shell_deploy → Extracts + installs
|
||
```
|
||
|
||
**State handoff:**
|
||
- ZFS module → sets CLAWDIE_BOOT_MODE + POOL_NAME
|
||
- GPU module → sets DETECTED_GPU
|
||
- env module → creates .env (used by deploy)
|
||
- deploy module → provisions jails + services
|
||
|
||
---
|
||
|
||
## POSIX Compliance
|
||
|
||
All modules use POSIX shell features only (no bash-isms):
|
||
- ✅ No `[[ ]]` (use `[ ]`)
|
||
- ✅ No `${var#pattern}` (avoid)
|
||
- ✅ No `function` keyword (use `name()`)
|
||
- ✅ No `local` (use plain variables in functions, prefix with `_` to avoid collisions)
|
||
- ✅ No `>&2` redirects without careful quoting
|
||
|
||
This ensures compatibility with FreeBSD /bin/sh.
|
||
|
||
---
|
||
|
||
## Error Handling Pattern
|
||
|
||
All modules follow this pattern:
|
||
|
||
```bash
|
||
function_name() {
|
||
# Validate inputs
|
||
if [ -z "${VAR:-}" ]; then
|
||
log_msg "ERROR: VAR not set"
|
||
return 1
|
||
fi
|
||
|
||
# Do work
|
||
if ! some_command; then
|
||
log_msg "ERROR: some_command failed"
|
||
return 1
|
||
fi
|
||
|
||
# Log success
|
||
log_msg "Task complete"
|
||
return 0
|
||
}
|
||
```
|
||
|
||
Each module defines its own `log_msg()` function to write to `LOG_FILE`.
|
||
|
||
---
|
||
|
||
## Testing Individual Modules
|
||
|
||
Each module can be tested standalone:
|
||
|
||
```bash
|
||
# Test GPU detection
|
||
export RC_CONF="/tmp/rc.conf"
|
||
. firstboot/shell-gpu.sh
|
||
clawdie_shell_gpu_detect
|
||
|
||
# Test environment generation
|
||
export ASSISTANT_NAME="Test"
|
||
export AGENT_DOMAIN="test.local"
|
||
export TZ="UTC"
|
||
. firstboot/shell-env.sh
|
||
clawdie_shell_env_generate
|
||
```
|
||
|
||
---
|
||
|
||
## Integration with firstboot.sh
|
||
|
||
The orchestrator `firstboot.sh` (lines 1–282):
|
||
|
||
1. Sources all 12 shell-*.sh modules
|
||
2. Collects wizard inputs (or uses pre-baked config)
|
||
3. Exports inputs as environment variables
|
||
4. Calls each module's main function in sequence
|
||
5. Checks progress file for [MODULE] COMPLETE entries
|
||
6. On success, disables clawdie-firstboot service
|
||
|
||
See [firstboot/firstboot.sh](firstboot/firstboot.sh) for full flow.
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
- [TESTING.md](TESTING.md) — Testing procedures
|
||
- [IMPLEMENTATION-PLAN.md](IMPLEMENTATION-PLAN.md) — Task status
|
||
- [BUILD.md](BUILD.md) — Build process
|