v0.1 — backend bones + ingredient sterilizer
LAN-only Flask API that consumes Mealie (source of truth for recipes / plans
/ lists) and clawdforge (centralized claude -p runner) to do AI work.
v0.1 surface:
GET /healthz liveness + clawdforge upstream
GET /api/recipes proxy Mealie recipe list
POST /api/sterilize/preview/<slug> dry-run AI parse, return proposals
POST /api/sterilize/apply/<slug> write parses back to Mealie
Why sterilizer first: Mealie's CRF parser is mediocre and Alice's hand-typed
recipes have lots of free-form ingredient strings ("about 2 cups cooked
white rice", "a pinch of salt") that don't aggregate cleanly into a
shopping list. We batch all ingredients of one recipe into a single Sonnet
call via clawdforge, get back parallel structured parses, then on apply
link each to Mealie food/unit records (creating missing by name) and PUT
the recipe back. Preview is non-destructive.
No UI in v0.1 — bearer-auth API only. Frontend + Authentik OIDC + Bob's
swamp/meadow/forest palette arrives in v0.2.
Auth: simple shared bearer in env (ADMIN_BEARER) until OIDC lands. LAN-only
deploy means the bearer is the only gate; no public exposure.
Stack: python:3.12-slim + Flask 3 + gunicorn + requests. No DB in v0.1.
This commit is contained in:
parent
b70c08e858
commit
961d15b285
12 changed files with 734 additions and 1 deletions
99
README.md
99
README.md
|
|
@ -1,3 +1,100 @@
|
|||
# cauldron
|
||||
|
||||
Mealie-backed AI meal planner + shopping list for the family
|
||||
Mealie-backed AI meal planner + shopping list for the family. LAN-only,
|
||||
internal tool. Mealie at `mealie.example.com` is the source of truth for
|
||||
recipes / meal plans / shopping lists; cauldron is the AI layer + Bob'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/<slug> dry-run AI parse, return proposals
|
||||
POST /api/sterilize/apply/<slug> write parses back to Mealie
|
||||
```
|
||||
|
||||
All routes except `/healthz` require `Authorization: Bearer <ADMIN_BEARER>`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Bob's phone (later: Kotlin app)
|
||||
│
|
||||
▼
|
||||
cauldron (Flask, port 7790, LAN-only)
|
||||
├─ Mealie API client ─── mealie.example.com (source of truth)
|
||||
├─ clawdforge client ─── clawdforge: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 Bob-specific prefs +
|
||||
chat history.
|
||||
|
||||
## Ingredient sterilizer
|
||||
|
||||
Mealie's CRF parser is mediocre. Alice'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://0.0.0.0:7790/api/sterilize/preview/spaghetti-bolognese | jq .
|
||||
|
||||
# Apply (creates missing foods/units by default)
|
||||
curl -sS -X POST -H "Authorization: Bearer $ADMIN_BEARER" \
|
||||
http://0.0.0.0:7790/api/sterilize/apply/spaghetti-bolognese | jq .
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
1. `ssh your-host`
|
||||
2. `cd /mnt/user/appdata && git clone <gitea-url> cauldron && cd cauldron/build`
|
||||
(or wherever the deploy convention lands)
|
||||
3. Drop `.env` at `/srv/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 `mealie.example.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://0.0.0.0:7790/healthz`
|
||||
|
||||
## Roadmap
|
||||
|
||||
- v0.1 ✓ — sterilizer backend + Flask shell
|
||||
- v0.2 — Authentik OIDC, Bob-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
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue