Clawdie AI

Most AI tooling assumes Linux. Clawdie runs on FreeBSD — using native jails instead of Docker, PF instead of iptables, and rc.d instead of systemd. This guide documents the complete setup from bare metal to running agent.

Tested on

FreeBSD 15.0-RELEASE-p4 on DigitalOcean VPS. Should work on any FreeBSD 14+ system.

Step 1

Base system preparation

1.1 Update the system

freebsd-update fetch install
pkg update && pkg upgrade -y

1.2 Install required packages

pkg install -y \
    node24 npm-node24 \
    git \
    nginx \
    python311 \
    sudo \
    tmux \
    pftop \
    htop

1.3 Create the clawdie user

# Create user with home directory
pw useradd clawdie -m -s /bin/sh -G wheel,webmaster
passwd clawdie

# Set up SSH key authentication
mkdir -p /home/clawdie/.ssh
chmod 700 /home/clawdie/.ssh

1.4 Directory structure

/home/clawdie/
├── clawdie-cp/              # Control plane (AI runtime)
│   ├── src/                 # TypeScript source
│   ├── container/           # Jail isolation layer
│   ├── .agent/skills/       # Applied customizations
│   ├── workspace/           # Identity + memory files
│   ├── logs/                # Runtime logs
│   ├── tmp/                 # Project-local temp files
│   └── credentials/         # 600 permission files
│
├── clawdie-robot/           # Robot OS (planned)
│
└── vm/                      # VM configs (planned)
    └── bhyve/               # FreeBSD bhyve definitions
Step 2

FreeBSD jail configuration

Clawdie uses FreeBSD jails to isolate agent execution — the same technology that inspired Docker, but native to FreeBSD with zero emulation overhead.

2.1 Create the jail base

# Create jail directory
mkdir -p /jails/clawdie-cp

# Install a minimal base system into the jail
fetch https://download.freebsd.org/releases/amd64/15.0-RELEASE/base.txz
tar -xf base.txz -C /jails/clawdie-cp

# Copy DNS resolution
cp /etc/resolv.conf /jails/clawdie-cp/etc/

2.2 Jail configuration

Add to /etc/jail.conf:

clawdie {
    host.hostname = "clawdie.clawdie.si";
    ip4.addr = "lo1|192.168.1.100/32";
    path = "/jails/clawdie-cp";
    mount.devfs;
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";
    allow.raw_sockets;
    enforce_statfs = 2;
    children.max = 0;

    # Resource limits
    # Memory: 2G, CPU: 2 cores, Processes: 1000
}

2.3 Mount points

Add to /etc/fstab.clawdie:

# Project source (read-only)
/home/clawdie/clawdie-cp  /jails/clawdie-cp/workspace/project  nullfs  ro  0  0

# Agent workspace (read-write)
/home/clawdie/clawdie-cp/groups  /jails/clawdie-cp/workspace/groups  nullfs  rw  0  0

# Device filesystem
devfs  /jails/clawdie-cp/dev  devfs  rulenum=4  0  0

# Temp (tmpfs — not on disk)
tmpfs  /jails/clawdie-cp/tmp  tmpfs  rw,size=256m  0  0

2.4 Start the jail

# Create the loopback interface for jail networking
ifconfig lo1 create
ifconfig lo1 inet 192.168.1.100/32

# Start the jail
service jail start clawdie

# Verify
jls
jexec clawdie hostname
Why jails, not Docker?

Docker on FreeBSD requires Linux emulation (linuxulator), adding overhead and compatibility issues. Native jails provide the same isolation with zero emulation penalty, direct kernel support, and simpler configuration.

Step 3

PF firewall

FreeBSD's PF (Packet Filter) provides stateful packet filtering with clean syntax. Our configuration supports both IPv4 and IPv6.

3.1 Configuration

Edit /etc/pf.conf:

ext_if="vtnet0"
tailscale_if="tailscale0"

set skip on lo0
block all
pass out all keep state

# SSH via Tailscale only (no public SSH)
pass in quick on $tailscale_if proto tcp to port 22 keep state

# Web traffic — Let's Encrypt + HTTPS
pass in quick on $ext_if inet proto tcp to port {80,443} keep state
pass in quick on $ext_if inet6 proto tcp to port {80,443} keep state

# Tailscale WireGuard UDP
pass in quick on $ext_if inet proto udp to port 41641 keep state
pass in quick on $ext_if inet6 proto udp to port 41641 keep state

# Jail NAT (allow jail to reach the internet)
nat on $ext_if from 192.168.1.0/24 to any -> ($ext_if)

3.2 Enable and load

# Enable PF in rc.conf
sysrc pf_enable="YES"
sysrc pflog_enable="YES"

# Load the rules
pfctl -f /etc/pf.conf

# Verify
pfctl -sr    # show rules
pftop        # live traffic monitor
Important

Default deny policy. The block all rule means everything is blocked unless explicitly allowed. Test your rules carefully before applying — a mistake can lock you out of SSH.

Step 4

Install Clawdie

4.1 Clone and install

cd /home/clawdie
git clone https://codeberg.org/Clawdie/clawdie-ai.git clawdie-cp
cd clawdie-cp
npm install
npm run build

4.2 Environment configuration

Create /home/clawdie/clawdie-cp/.env:

ASSISTANT_NAME=Clawdie
ASSISTANT_HAS_OWN_NUMBER=false
CONTAINER_RUNTIME=jail
JAIL_PATH=/jails/clawdie-cp
LOG_LEVEL=info
TZ=Europe/Ljubljana

# Telegram
TELEGRAM_BOT_TOKEN=your_bot_token

# Supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_KEY=your_anon_key

# LLM Providers (configure one or more)
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
OPENROUTER_API_KEY=sk-or-...
ZAI_API_KEY=...

4.3 Identity files

# Create workspace directory
mkdir -p /home/clawdie/clawdie-cp/workspace

# Identity system — these persist across redeployment
cat > workspace/SOUL.md    # Personality definition
cat > workspace/IDENTITY.md # Self-concept
cat > workspace/USER.md     # Information about operator

4.4 rc.d service

Create /usr/local/etc/rc.d/clawdie:

#!/bin/sh
#
# PROVIDE: clawdie
# REQUIRE: NETWORKING
# KEYWORD: shutdown

. /etc/rc.subr

name="clawdie"
rcvar="clawdie_enable"
pidfile="/var/run/${name}.pid"
command="/usr/local/bin/node"
command_args="/home/clawdie/clawdie-cp/dist/index.js"
clawdie_user="clawdie"

load_rc_config $name
run_rc_command "$1"
# Enable and start
chmod +x /usr/local/etc/rc.d/clawdie
sysrc clawdie_enable="YES"
service clawdie start
Step 5

tmux glass-pane console

Clawdie's signature feature — a live terminal window into agent reasoning. Three panes: gateway output, interactive shell, system monitoring.

#!/bin/sh
# start-clawdie.sh — tmux glass-pane initialization

SESSION="clawdie"

tmux new-session -d -s $SESSION -n main

# Pane 0: Gateway (agent runtime)
tmux send-keys -t $SESSION "cd /home/clawdie/clawdie-cp && npm start" Enter

# Pane 1: Shell (interactive)
tmux split-window -h -t $SESSION
tmux send-keys -t $SESSION "cd /home/clawdie/clawdie-cp" Enter

# Pane 2: System monitor
tmux split-window -v -t $SESSION
tmux send-keys -t $SESSION "btop" Enter

tmux select-pane -t 0
tmux attach -t $SESSION

Attach from anywhere via SSH:

ssh clawdie@100.x.x.x -t "tmux attach -t clawdie"