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)
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.
- Start Pi on the host:
pi
- Run
/logininside Pi and selectChatGPT Plus/Pro (Codex). - 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.
- Complete login in the browser. It will redirect to something like
http://localhost:1455/auth/callback?.... - On a headless host, that browser callback usually fails. That is expected. Copy the full redirect URL from the browser address bar anyway.
- Paste that full redirect URL back into Pi at:
Paste redirect URL below, or complete login in browser:
- 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.