Skip to content

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.

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

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/:

Terminal window
mkdir kalelmv3 && cd kalelmv3
git clone https://github.com/mohanad-a/kalelm-backend.git backend
git clone https://github.com/mohanad-a/kalelm-frontend.git frontend

Then you need, in this order:

  1. Secrets for .env. Nothing secret is in git. PAYLOAD_SECRET, CRON_SECRET, MEILI_MASTER_KEY and the rest are in Hangar env (GET /api/v1/sites/kalelm-api/env with the Hangar token) and in /srv/kalelm/.env on the search VPS. Local Docker gets its own Postgres password and Meilisearch key; only CRON_SECRET must match production if this machine will run a worker.
  2. ssh to the servers. Add the machine’s public key to /root/.ssh/authorized_keys on 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.
  3. The Hangar token, from the owner’s password manager, only when deploying: HANGAR_TOKEN=… scripts/deploy-hangar.sh backend|frontend|docs.
  4. 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.
Terminal window
cd backend
cp .env.example .env # then fill DATABASE_URL, PAYLOAD_SECRET, CRON_SECRET, MEILI_*
docker compose up -d postgres meilisearch
pnpm install
pnpm payload migrate # schema lives in src/migrations, push is off on purpose
pnpm dev # http://localhost:3001 — admin at /admin

Data: 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.

Terminal window
cd frontend
npm install
PUBLIC_API_URL=http://localhost:3001 npm run dev # http://localhost:4321; the gate expects 4322, see scripts/frontend-dev.sh

PUBLIC_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.

Terminal window
pnpm verify # all sections
pnpm verify pipeline # one section

It 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.

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.

Terminal window
cd docs && npm install && npm run dev # this site, at http://localhost:4321

Pages are Markdown in src/content/docs/; screenshots in public/images/. Publish with scripts/deploy-hangar.sh docs.