---
name: strapi
description: Manage the Strapi CMS running inside the db jail on FreeBSD. Use when installing Strapi, defining content types, managing API access, migrating content, or troubleshooting the CMS. Triggers on "strapi", "cms", "content management", "content types".
---

# Strapi

Use this skill for the headless CMS that feeds content to the Astro static site.

## Scope

This skill covers:

- Strapi installation and configuration inside the `db` jail
- PostgreSQL database setup for Strapi
- Content type design and management
- API access configuration
- Content migration from static HTML
- Strapi admin UI access via Tailscale

This skill does not replace:

- `astro` for frontend builds and deployment
- `nginx` for web server configuration
- `postgres-memory` for the Clawdie memory database (separate from CMS)

## Architecture

```
cms jail (192.168.100.3)
  ├── Strapi (Node.js, :1337)
  └── Astro (build tool)
         ↓ fetches from Strapi API
         ↓ reads .agent/skills/*/SKILL.md via mount
         ↓
  build output ──→ /usr/local/www/clawdie/ (host) ──→ nginx

db jail (192.168.100.2)
  └── PostgreSQL 18
         ↑ Strapi connects over VNET (192.168.100.2:5432)
```

Strapi and Astro run together in a dedicated `cms` jail. Strapi connects to
PostgreSQL in the `db` jail over the warden0 bridge. This keeps the `db` jail
as a pure database server.

Strapi uses a **separate database** from the Clawdie memory database —
never mix CMS content with agent memory.

## Canonical targets

- Jail: `cms`
- Hostname: `cms.clawdie.si`
- IP: `192.168.100.3`
- Memory budget: 1 GB (see `freebsd-admin/references/memory-budget.md`)
- Strapi port: `1337`
- Strapi admin: `http://192.168.100.3:1337/admin` (Tailscale-accessible only)
- Strapi API: `http://192.168.100.3:1337/api`
- PostgreSQL host: `192.168.100.2` (db jail, over VNET)
- PostgreSQL database: `strapi_cms` (separate from `clawdie_memory`)
- Strapi install path: `/home/clawdie/strapi` (inside cms jail)
- Astro install path: `/home/clawdie/clawdie-site` (inside cms jail)
- Strapi Node.js limit: `NODE_OPTIONS="--max-old-space-size=512"`

## Content types

Design content types to match the clawdie.si site structure:

### Page

General-purpose pages (homepage, about, etc.)

| Field | Type | Notes |
| --- | --- | --- |
| title | Text | Required |
| slug | UID | From title |
| content | Rich Text | Main body |
| hero_text | Rich Text | Optional hero section |
| seo_title | Text | Optional |
| seo_description | Text | Optional |
| order | Number | Sort order |

### Guide

Technical guides (FreeBSD setup, nginx SSL, etc.)

| Field | Type | Notes |
| --- | --- | --- |
| title | Text | Required |
| slug | UID | From title |
| content | Rich Text | Markdown-friendly |
| category | Enum | setup, networking, security, integration |
| difficulty | Enum | beginner, intermediate, advanced |
| last_verified | Date | When last tested |
| order | Number | Sort order |

### Skill

Auto-generated from `.agent/skills/*/SKILL.md` files.

| Field | Type | Notes |
| --- | --- | --- |
| name | Text | Skill name |
| slug | UID | From name |
| description | Text | From YAML frontmatter |
| category | Enum | channels, infrastructure, operations, agent, utility |
| content | Rich Text | From SKILL.md body |
| skill_count | Number | Updated on sync |

### BlogPost

Development log and milestone announcements.

| Field | Type | Notes |
| --- | --- | --- |
| title | Text | Required |
| slug | UID | From title |
| content | Rich Text | Main body |
| published_at | DateTime | Publication date |
| tags | JSON | Flexible tagging |

## Workflow

### Phase 1: Installation

1. Read `references/install.md` for step-by-step Strapi setup in the db jail
2. ZFS snapshot before installing: `zfs snapshot zroot/clawdie-runtime/jails/db@pre-strapi-DD-mon-YYYY`
3. Install Node.js 24 inside db jail if not present
4. Create `strapi_cms` PostgreSQL database
5. Create Strapi project at `/home/clawdie/strapi`
6. Configure Strapi to use local PostgreSQL
7. Start Strapi and verify admin UI

### Phase 2: Content types

1. Create content types via Strapi admin UI or content-type builder
2. Follow the schema defined above
3. Set API permissions: public read for pages/guides/skills, authenticated write

### Phase 3: Content migration

1. Read `references/migration.md` for HTML-to-Strapi migration steps
2. Extract content from existing static HTML files
3. Create entries via Strapi API or admin UI
4. Verify all content is accessible via API

## Safe defaults

- Always ZFS snapshot the db jail before Strapi changes
- Keep Strapi database (`strapi_cms`) separate from Clawdie memory database
- Strapi admin is jail-internal only — access via Tailscale, never expose to public internet
- Public API is read-only — authenticated access required for writes
- Run Strapi as the `clawdie` user, not root

## Strapi service management

Inside the db jail:

```sh
# start
cd /home/clawdie/strapi && NODE_ENV=production npm run start

# development mode
cd /home/clawdie/strapi && npm run develop

# rebuild admin
cd /home/clawdie/strapi && npm run build
```

For persistent service, create an rc.d script inside the jail or use a process manager.

## Troubleshooting

### Strapi won't start

- Check Node.js version: `node --version` (must be 20+)
- Check PostgreSQL is running: `service postgresql status`
- Check database exists: `psql -U clawdie -l | grep strapi_cms`
- Check logs: `tail /home/clawdie/strapi/logs/`

### Cannot access admin UI

- Strapi binds to `0.0.0.0:1337` by default inside the jail
- Access via jail IP: `http://192.168.100.2:1337/admin`
- Must be on Tailscale network or host bridge to reach jail IP
- Check pf rules allow traffic to jail port 1337

### API returns empty

- Check content type permissions in Settings > Roles > Public
- Enable `find` and `findOne` for public role
- Check content is published (draft content not returned by default)

### Database connection fails

- Verify `strapi_cms` database exists
- Check database config in `/home/clawdie/strapi/config/database.js`
- PostgreSQL must allow local connections (check `pg_hba.conf`)
