# cauldron Mealie-backed AI meal planner + shopping list for the family. LAN-only, internal tool. Mealie at `recipes.sulkta.com` is the source of truth for recipes / meal plans / shopping lists; cauldron is the AI layer + Abby's branded UI on top. ## Status **v0.1 — backend bones (current).** Ingredient sterilizer endpoint working. No UI yet; bearer-auth API only. Frontend + Authentik OIDC arrives in v0.2. Native Kotlin Android in v0.5. ## Surface (v0.1) ``` GET /healthz liveness + clawdforge upstream GET /api/recipes list Mealie recipes (paginated) POST /api/sterilize/preview/ dry-run AI parse, return proposals POST /api/sterilize/apply/ write parses back to Mealie ``` All routes except `/healthz` require `Authorization: Bearer `. ## Architecture ``` Abby's phone (later: Kotlin app) │ ▼ cauldron (Flask, port 7790, LAN-only) ├─ Mealie API client ─── recipes.sulkta.com (source of truth) ├─ clawdforge client ─── 192.168.0.5:8800 (claude -p runner) └─ Authentik OIDC (v0.2) ``` cauldron does NOT hold its own database in v0.1 — all state lives in Mealie. A small Postgres/MariaDB schema lands in v0.2 for Abby-specific prefs + chat history. ## Ingredient sterilizer Mealie's CRF parser is mediocre. Cobb's hand-typed recipes have lots of free-form quantity strings ("about 2 cups cooked white rice", "1 small handful kale", "a pinch of salt") that don't aggregate cleanly into a shopping list. The sterilizer batches all ingredients of one recipe into a single Sonnet call (via clawdforge), gets back parallel structured parses, then on apply links each parse to existing Mealie food/unit records (creating any missing by name) and PUTs the recipe back. Preview is non-destructive — review proposals before apply. ```bash # Dry-run preview curl -sS -X POST -H "Authorization: Bearer $ADMIN_BEARER" \ http://192.168.0.5:7790/api/sterilize/preview/spaghetti-bolognese | jq . # Apply (creates missing foods/units by default) curl -sS -X POST -H "Authorization: Bearer $ADMIN_BEARER" \ http://192.168.0.5:7790/api/sterilize/apply/spaghetti-bolognese | jq . ``` ## Deploy 1. `ssh lucy` 2. `cd /mnt/user/appdata && git clone cauldron && cd cauldron/build` (or wherever the deploy convention lands) 3. Drop `.env` at `/mnt/cache/appdata/secrets/cauldron.env` (chmod 600 root:root) - `CLAWDFORGE_TOKEN` is already populated by the bootstrap (see `memory/2026-04-28.md`) - `MEALIE_API_TOKEN` — mint at `recipes.sulkta.com` → user → API tokens - `ADMIN_BEARER` — pick 32 bytes of entropy - `SECRET_KEY` — 32 bytes for Flask sessions 4. `docker compose up -d --build` 5. Smoke: `curl http://192.168.0.5:7790/healthz` ## Roadmap - v0.1 ✓ — sterilizer backend + Flask shell - v0.2 — Authentik OIDC, Abby-branded web UI, palette CSS, postgres for prefs - v0.3 — meal plan generator (week → Mealie meal plan write) - v0.4 — shopping list aggregator (read meal plan → consolidated grocery list) - v0.5 — native Kotlin + Compose Android app (read-only shopping list + plan view) ## Repo layout ``` cauldron/ ├─ cauldron/ │ ├─ config.py env-driven config │ ├─ forge.py clawdforge HTTP client │ ├─ mealie.py Mealie API client │ ├─ sterilizer.py ingredient parse + apply pipeline │ └─ server.py Flask app ├─ Dockerfile ├─ compose.yml ├─ requirements.txt └─ .env.example ```