clawdie-ai/docs/public/install/install.md
Operator & Codex 771e19e1c7 Drop FreeBSD 14 support (Sam & Claude)
Require the tracked FreeBSD 15.x line during install and environment checks, and align docs and skill compatibility metadata with 15.x only.

---
Build: pass
Tests: pass — 37 passed (2 files)

---
Build: pass | Tests: pass — 2363 passed (701 files)
2026-05-10 16:31:40 +02:00

13 KiB

title description
Install Orchestrator Single-command install flow for Clawdie.

File: setup/install.ts Script: just install (or npm run install) Status: current main — replaces manual step-by-step sequence


Quick start

Clawdie tracks the FreeBSD 15.x line. The installer rejects FreeBSD 14.x and any unvalidated future major version.

just install

ISO path

If you started from a bootable ISO, follow First boot (V1) first — the first-boot setup (setup.txt) seeds the install non-interactively. After the initial boot, the same install flow described here runs in the background.

Resume from a specific step after a failure:

just install-from-db

Skip service jails (db, git, cms) when running DB_RUNTIME=host or installing a second agent on an existing host:

just install-from hosts

Print the step plan without running anything:

just install -- --dry-run

Skip ZFS checkpoints (e.g. no ZFS pool):

just install -- --no-snapshots

Step flow

just install
        │
        ▼
  ┌─────────────────────────────────────────────────────────┐
  │  Detect ZFS dataset (zroot/bastille)                    │
  │  Parse --from / --no-snapshots / --dry-run              │
  └─────────────────────────────────────────────────────────┘
        │
        ▼
[ 1] onboarding        first-boot setup seed (V1) or TUI fallback REQUIRED
        │
[ 2] environment       host pkg baseline, bridge, locale   REQUIRED
        │
[ 3] pi-config         validate/write pi provider          optional ─── warn on missing provider auth
        │                                                        └── pi missing → warn, continue
[ 4] pf                write PF include (NAT egress)       REQUIRED
        │                         └── 📸 snapshot: post-pf
[ 5] jails             create worker jail (--create)       REQUIRED
        │                         └── 📸 snapshot: post-jails
[ 6] git               Git Service (git jail)              DEFAULT  ─── DB_RUNTIME=host → skip or use install-from hosts
        │                         └── 📸 snapshot: post-git
[ 7] forgejo           Forgejo for Git Service             DEFAULT  ─── FEATURE_GITEA=NO → skip
        │
[ 8] db                Data Service (PostgreSQL)           DEFAULT  ─── DB_RUNTIME=host → skip; DB on host instead
        │                         └── 📸 snapshot: post-db
[ 9] skills-memory     built-in knowledge import           DEFAULT  ─── artifact.sql ships in tarball
        │
[10] skills-init       Skills engine init (.nanoclaw)      DEFAULT
        │
[11] cms               Web Service (cms jail: Astro+nginx) DEFAULT  ─── use install-from hosts to skip
        │                         └── 📸 snapshot: post-cms
        │
[12] ollama            Local AI Models (Ollama jail .5)    optional ─── FEATURE_OLLAMA≠YES → skip
        │
[13] llama-cpp         Local AI Models (llama-cpp jail .5) optional ─── FEATURE_LLAMA_CPP≠YES → skip
        │
[14] hosts             /etc/hosts + jail hostnames         REQUIRED
        │
[15] mounts            validate jail mounts                REQUIRED
        │
[16] telegram-auth     verify bot token                    optional ─── TELEGRAM_BOT_TOKEN unset → skip
        │
[17] service           build + install rc.d service         REQUIRED
        │
[18] hostd             privileged host daemon (hostd)       REQUIRED
        │
[19] identity-restore  Supabase restore                     optional ─── SUPABASE_URL unset → skip
        │
[20] verify            integrity check                      optional
        │                         └── 📸 snapshot: install-complete
        │
        ▼
  ┌─────────────────────────────────────────────────────────┐
  │  Summary: N ok  N warnings  N skipped  N failed         │
  │  Snapshots taken: zroot/bastille@post-pf-…  …          │
  │  LLM providers: anthropic ✓  openai ✗  ollama ✗  …     │
  └─────────────────────────────────────────────────────────┘

The onboarding step prefers the first-boot setup file (setup.txt) (V1 first-boot model — see First boot). The interactive TUI wizard is the explicit fallback when the first-boot setup is absent or invalid; it sources locales from FreeBSD itself, so any installed locale can be selected (en_US.UTF-8, zh_CN.UTF-8, etc.) and is applied consistently. setup.txt is now a versioned operator-intent contract, and system.env is the matching hardware-intent contract. Inspect can populate both before the installer runs. Set DB_RUNTIME=host in .env to provision PostgreSQL directly on the host instead of a db jail; DB_HOST defaults to ${AGENT_SUBNET_BASE}.1 so jails can reach it. Use DB_COMPRESSION=lz4 (default) or DB_COMPRESSION=zstd for ZFS compression on host datasets.

The root install owns shared platform services. It is not modeled as tenant zero. ASSISTANT_NAME is display-only. Later additive tenants consume shared services such as:

  • Git Service
  • Web Service
  • Local AI Models

ZFS snapshots

Snapshots are taken after each milestone step if a Bastille ZFS dataset is detected. Two paths:

Context Method
Running as root zfs snapshot zroot/bastille@<tag> directly
Non-root, sudo available sudo zfs snapshot zroot/bastille@<tag>
Non-root, hostd socket present nc -U /var/run/<agent>-hostd.sock (hostd zfs-snapshot op)
Neither skip silently

Snapshot tags are suffixed with a Unix timestamp to prevent collisions on re-runs.


LLM providers

The orchestrator never exits on a missing LLM key. At the end of the run it prints a table of all known providers:

△ LLM providers
  anthropic     ANTHROPIC_API_KEY         ✓ configured
  openai        OPENAI_API_KEY            ✗ not set
  openrouter    OPENROUTER_API_KEY        ✗ not set
  groq          GROQ_API_KEY              ✗ not set
  deepseek      DEEPSEEK_API_KEY          ✗ not set
  azure         AZURE_OPENAI_API_KEY      ✗ not set
  ollama        OLLAMA_HOST               ✗ not set

If no key is found, it prints instructions:

  No LLM keys found. Add to .env and restart:
    ANTHROPIC_API_KEY=sk-ant-...
    sudo service clawdie restart

The entire infrastructure (PF, jails, PostgreSQL, nginx, ZFS) has zero LLM dependency. The key is only consumed when the jail-runner spawns a live response. Install and service start succeed without it.

Headless Codex login

Use this when pi runs on a remote or headless host and you want to authenticate with a ChatGPT-backed Codex subscription.

  1. Start Pi on the host:
pi
  1. Run /login inside Pi and select ChatGPT Plus/Pro (Codex).
  2. Pi prints a long OpenAI auth URL. If your terminal wraps it across multiple lines, copy it into one continuous line before opening it in your local browser.
  3. Complete login in the browser. It will redirect to something like http://localhost:1455/auth/callback?....
  4. On a headless host, that browser callback usually fails. That is expected. Copy the full redirect URL from the browser address bar anyway.
  5. Paste that full redirect URL back into Pi at:
Paste redirect URL below, or complete login in browser:
  1. Pi stores the subscription auth in ~/.pi/agent/auth.json.

Quick verification:

pi --provider openai-codex --model gpt-5.5 --no-session --print "Reply with exactly: codex-ok"

Expected output:

codex-ok

If you prefer automatic callback handling instead of copy/paste, create an SSH tunnel before running /login:

ssh -L 1455:127.0.0.1:1455 user@server

Control plane API auth

Agent subprocesses (pi, aider) authenticate back to the control plane API using a shared secret. Generate one after install:

echo "CONTROLPLANE_SHARED_SECRET=$(openssl rand -base64 32)" >> .env
sudo service ${AGENT_NAME} restart

Without it, the startup log warns and agent-to-API calls are rejected.

Service wrapper scripts

The rc.d service (/usr/local/etc/rc.d/${AGENT_NAME}) runs run-${AGENT_NAME}.sh which is generated by just setup-service at install time. It is not tracked in git (run-*.sh is in .gitignore). If missing, re-run just setup-service to regenerate it.

Local LLM runtime is optional and configured via:

LOCAL_LLM_PROVIDER=none|ollama|llama_cpp
FEATURE_OLLAMA=YES|NO
FEATURE_LLAMA_CPP=YES|NO
FEATURE_OLLAMA_HPP=YES|NO   # optional C++ bindings for Ollama clients

Required vs optional steps

Steps are required (failure stops install), default (runs out of the box but can be disabled), or optional (skipped unless explicitly enabled).

Step Status Skip condition
onboarding required
environment required
pi-config optional warn on missing provider auth or missing pi
pf required
jails required
git default DB_RUNTIME=host or install-from hosts
forgejo default FEATURE_GITEA = NO
db default DB_RUNTIME=host skips jail, provisions on host instead
skills-memory default bootstrap/skills-memory/artifact.sql not present
skills-init default
cms default install-from hosts skips; cms is a shared service, not per-agent
ollama optional FEATURE_OLLAMA ≠ YES
llama-cpp optional FEATURE_LLAMA_CPP ≠ YES
hosts required
mounts required
telegram-auth optional TELEGRAM_BOT_TOKEN not set
service required
hostd required
identity-restore optional SUPABASE_URL not set
verify optional warn on most check failures; fail on broken runtime integrity

A required step failure stops the install immediately and prints the resume command. Default steps ship enabled (FEATURE_GITEA=YES, artifact.sql bundled) — they run unless the operator explicitly opts out.


Individual steps

The orchestrator calls each step through setup/index.ts as a subprocess (spawnSync with tsx). This isolates process.exit(1) calls in individual step modules from killing the orchestrator.

Individual steps can still be run directly:

just setup-db
just setup -- --step verify

verify now checks more than the shared docs build. If tenant sites are declared, it also checks their served output and publish-manifest consistency in the CMS webroot. A site that is merely declared but not yet manually published does not fail the step by itself; an inconsistent live publish state does.

See setup/index.ts for the full step registry.