Getting started locally
Everything runs on one machine: the Payload backend, the Astro site, Postgres and Meilisearch in Docker, and Ollama for embeddings. Budget about twenty minutes, most of it downloads.
Prerequisites
Section titled “Prerequisites”| Tool | Why | Install |
|---|---|---|
Node 22 (see .nvmrc) |
backend and frontend | nvm install |
| pnpm | backend package manager | corepack enable pnpm |
| Docker Desktop | Postgres 17, Meilisearch 1.53.1, the WordPress source DB for migrations | docker.com |
Ollama with bge-m3 |
query embeddings for semantic search, and the sentence/passage index builds | brew install ollama && ollama pull bge-m3 |
| ffmpeg, uv, a Hugging Face login | only if you run the transcription worker | see worker/README.md |
New machine
Section titled “New machine”The code lives in two private GitHub repositories under mohanad-a; clone them side by side, the deploy script and the gate expect ../frontend next to backend/:
mkdir kalelmv3 && cd kalelmv3git clone https://github.com/mohanad-a/kalelm-backend.git backendgit clone https://github.com/mohanad-a/kalelm-frontend.git frontendThen you need, in this order:
- Secrets for
.env. Nothing secret is in git.PAYLOAD_SECRET,CRON_SECRET,MEILI_MASTER_KEYand the rest are in Hangar env (GET /api/v1/sites/kalelm-api/envwith the Hangar token) and in/srv/kalelm/.envon the search VPS. Local Docker gets its own Postgres password and Meilisearch key; onlyCRON_SECRETmust match production if this machine will run a worker. - ssh to the servers. Add the machine’s public key to
/root/.ssh/authorized_keyson the search VPS (49.12.78.95) and the Hangar box (188.245.169.96), from a machine that already has access. See Access inventory. - The Hangar token, from the owner’s password manager, only when deploying:
HANGAR_TOKEN=… scripts/deploy-hangar.sh backend|frontend|docs. - The Claude start-here block is
backend/CLAUDE.md; a new Claude session reads it on its own. There is no other state to carry over.
Backend
Section titled “Backend”cd backendcp .env.example .env # then fill DATABASE_URL, PAYLOAD_SECRET, CRON_SECRET, MEILI_*docker compose up -d postgres meilisearchpnpm installpnpm payload migrate # schema lives in src/migrations, push is off on purposepnpm dev # http://localhost:3001 — admin at /adminData: restore a dump into the local Postgres rather than migrating from WordPress again. The nightly production dump lands on the Hangar box (see Backups and restore); pg_restore --no-owner --no-acl -d "$DATABASE_URL" kalelm-YYYY-MM-DD.dump.
Search index: pnpm migrate:wp index builds the keyword indexes from the local database in about two minutes. sentences and passages build the vector indexes and take hours on a GPU; copy data.ms from another same-version instance instead when one exists.
Frontend
Section titled “Frontend”cd frontendnpm installPUBLIC_API_URL=http://localhost:3001 npm run dev # http://localhost:4321; the gate expects 4322, see scripts/frontend-dev.shPUBLIC_API_URL is baked in at build time by Astro, so a production build must be made with the production API URL; scripts/deploy-hangar.sh frontend does that.
The gate
Section titled “The gate”pnpm verify # all sectionspnpm verify pipeline # one sectionIt needs Postgres, the backend on 3001, the frontend on 4322, Meilisearch on 7701 and Ollama running. Each section says what it exercises. A Stop hook in the owner’s Claude setup runs it at the end of every turn and refuses to finish while it fails; if Meilisearch is stopped for maintenance, the gate fails for that reason alone.
Worker
Section titled “Worker”The transcription worker is a separate Python program with its own README in worker/. Locally it can be started from the admin panel once PIPELINE_WORKER_PYTHON in .env points at a venv that has its requirements.
cd docs && npm install && npm run dev # this site, at http://localhost:4321Pages are Markdown in src/content/docs/; screenshots in public/images/. Publish with scripts/deploy-hangar.sh docs.