Clawdie AI

The setup wizard guides you through first-time configuration of your self-hosted AI assistant. Paths are auto-detected, secrets are protected, and screenshots are captured at every step.

First test install

On 8 March 2026, pi-tui completed the first full test installation on osa.smilepowered.org — creating jails, generating credentials, setting up PostgreSQL, configuring nginx authentication, and taking ZFS snapshots. The screenshots below were captured during that session.

Prerequisites

Before running the setup wizard, ensure your system meets these requirements. Everything is designed for FreeBSD 15 with Bastille jails on ZFS.

Required

  • FreeBSD 15.0 (amd64)
  • Bastille 0.10+
  • ZFS root pool
  • 8 GB RAM minimum
  • 50 GB disk space
  • Node.js v22+

Network

  • Static IP or DHCP reservation
  • Internet access for packages
  • Optional: Tailscale for mesh VPN
  • Port 443 open for HTTPS
  • Telegram bot token (from @BotFather)
  • AI provider API key
# Quick start — clone and run
git clone https://codeberg.org/Clawdie/Clawdie-AI.git
cd Clawdie-AI
npm install
npm run wizard

Wizard Steps

The curses-style wizard auto-detects your environment and walks you through the installer flow. Architecture choices stay early so jail roles and reserved IPs remain consistent.

IP Address Scheme

Jails use a logical numbering convention — lower numbers for more foundational services. The subnet base is configurable (default: 10.0.0).

.1 Gateway — warden0 bridge interface (host)
.2 controlplane — AI agent runtime, glasspane console, Telegram interface
.3 db — PostgreSQL + pgvector for persistent memory
.4 git — Local bare repositories (if enabled)
.5 cms — Astro + Strapi content management (if enabled)
.6 ollama — Local LLM inference (optional)
.7 browser — Reserved for Playwright/CDP
.8 cnc — TODO: future CNC operations jail
.9 monitor — TODO: future observability jail
.10 runner — TODO: future background jobs jail
Configuration

IPs are computed from WARDEN_SUBNET_BASE in .env. The default base is 10.0.0 and all jail IPs derive automatically. See setup/network.ts for offset definitions.

First Test Install

Screenshots from the first successful test installation by pi-tui. Full deployment cycle — from credential generation to ZFS snapshots.

Password generation and .env configuration

Step 1 — Credential Generation

Three database passwords generated with Python secrets, appended to .env. Passwords are visible once in the screenshot, then read from environment variables in all subsequent steps.

PostgreSQL role and database creation

Step 2 — Database Setup

PostgreSQL roles created inside the db jail via jexec: postgres admin, clawdie_app, and strapi_user. Passwords sourced from .env — no hardcoding. Verified with \du and \l showing 3 roles and 2 databases.

ZFS snapshots for rollback safety

Step 3 — ZFS Snapshots

Pre-security ZFS snapshot taken on zroot/clawdie-runtime/jails/db before applying hardening changes. Snapshot naming follows @pre-security_YYYY-MM-DD_HH:MM:SS convention for clean rollback.

Screenshot skill path configuration

Step 4 — Screenshot Skill Setup

Screenshot paths updated for production: output goes to /usr/local/www/clawdie/screenshots/, manifest served at clawdie.si/screenshots/. Font paths adjusted for FreeBSD package locations.

nginx configuration and .env update

Step 5 — nginx + Authentication

nginx configuration validated (syntax is ok), screenshot credentials added to .env: SCREENSHOTS_USER and SCREENSHOTS_PASSWORD. The .env.example template updated with empty placeholders.

nginx htpasswd and final ZFS snapshot

Step 6 — Security Hardening

nginx basic auth location block configured for /screenshots/ with htpasswd. Final security-configured ZFS snapshot taken. Two snapshots now protect the database jail: pre-security and security-configured.

Setup Steps (Post-Wizard)

After the wizard writes .env, the remaining setup runs as individual steps. Each can be invoked independently:

# Full sequence after wizard
npm run setup --step environment     # Detect platform, check prereqs
npm run setup --step network         # Write IP configuration to .env
npm run setup --step jail            # Create Bastille jails on ZFS
npm run setup --step groups          # Sync Telegram groups to DB
npm run setup --step register        # Register agent-controlled channels
npm run setup --step mounts          # Configure nullfs mounts for jail
npm run setup --step telegram-auth   # Authenticate with Telegram bot
npm run setup --step service         # Create rc.d service file
npm run setup --step verify          # Final health check
Jail creation

The jail step runs Bastille with thin jails (-T) and ZFS backing (-B). Jails are created at /usr/local/bastille/jails/controlplane/, each with a static IP on the warden0 bridge, a hostname like controlplane.agent.local, and FreeBSD 15.0-RELEASE as the base.

After Installation

Verify Jails

bastille list should show controlplane and db running. Jails live at /usr/local/bastille/jails/. Access with bastille console controlplane.

Test Telegram

Send a message to your bot. Clawdie should respond. Check logs at /var/log/clawdie/telegram.log if not.

Glasspane Console

Attach with tmux attach -t clawdie to see the 3-pane console: gateway, shell, and btop.

Configure nginx

Set up vhosts for your domain. Let's Encrypt certificates auto-renew via acme.sh.

Troubleshooting

Common issues

Gateway missing: Jails created without default gateway. Fix with bastille cmd controlplane route add default 10.0.0.1

PostgreSQL SYSVIPC: Database jail needs allow.sysvipc=1 in /usr/local/bastille/jails/db/jail.conf. Restart jail after change.

Sharp/image errors: Astro builds fail on FreeBSD due to native deps. Install vips inside CMS jail: bastille pkg cms install graphics/vips

pf blocking: Ensure jail subnet (10.0.0.0/24) is in nat rules. Check with pfctl -s nat.

Passwords lost: All secrets live in .env (gitignored). If lost, regenerate with python3 -c "import secrets; print(secrets.token_urlsafe(32))" and update PostgreSQL roles.

Jail not starting: Check /usr/local/bastille/jails/controlplane/ exists. If not, re-run npm run setup --step jail.