Macro-Max finds the least-cost weekly grocery basket that meets a person's macro targets. It solves a mixed-integer program over whole packages and current prices from one supported catalog:
Harris Teeter · University Place
2110 S. Estes Drive, Chapel Hill, NC · Kroger location 09700117
The model enforces protein, carbohydrate, and fat targets; an optional calorie ceiling; a weekly budget; dietary tags; category coverage; and food variety. Results include the basket cost, achieved macros, and cost per gram of protein.
flowchart LR
UI[Next.js web app] -->|JSON| API[Go API]
API --> PG[(Postgres)]
API --> Redis[(Redis cache)]
API -->|gRPC| Solver[Python OR-Tools MILP]
Ingest[Go price importer] -->|product.compact| Kroger[Kroger API]
Ingest --> PG
API -->|authorization code| Kroger
FDC[USDA FoodData Central] --> Foods[Go nutrition importer]
Foods --> PG
The optimizer, rather than a language model, determines the basket and proves optimality. Optional Claude recipe generation only turns an already-solved basket into meal suggestions.
Requirements: Go, Docker, golang-migrate, Node.js, npm, and Kroger developer credentials.
cp .env.example .envSet KROGER_CLIENT_ID and KROGER_CLIENT_SECRET, then start the infrastructure and load the schema:
make up
make migrate-up
make seed
make kroger-dry
make kroger-ingestRun the API and frontend in separate terminals:
make runmake webOpen http://localhost:3000. The UI always solves against the University Place catalog; there is no store picker or synthetic product catalog.
WEB_APP_URL is intentionally required whenever Kroger credentials are set. The API has no localhost fallback, preventing a production OAuth flow from silently redirecting to a developer machine.
Register this exact local callback in the Kroger developer portal:
http://localhost:3000/api/kroger/callback
Set NEXT_PUBLIC_KROGER_CART=true for a local cart demo. After solving, Add to my Kroger cart sends the browser to Kroger. The user signs in and grants cart.basic:write; the callback fills that account's cart and returns to Macro-Max. OAuth tokens are used only during that callback request and are never stored. Users authorize each cart fill.
Cart writes are additive. Test with a small basket and do not submit the same basket repeatedly.
FDC_API_KEYenables USDA nutrition enrichment withmake fdc-dryandmake fdc-import.ANTHROPIC_API_KEYenables the backend recipe route.NEXT_PUBLIC_ANTHROPIC_RECIPES=trueexposes the recipe control in the frontend build. Set both recipe variables or neither.
| Method | Route | Purpose |
|---|---|---|
GET |
/v1/healthcheck |
Database-backed health check |
GET |
/v1/foods |
Nutrition catalog |
GET |
/v1/products |
University Place product catalog |
POST |
/v1/targets |
Save nutrition and budget targets |
GET |
/v1/targets/{id} |
Read a target |
POST |
/v1/solve |
Optimize a target |
POST |
/v1/kroger/authorize |
Start a cart authorization for a solved target |
GET |
/v1/kroger/callback |
Fill the authorized cart and return to the web app |
POST |
/v1/recipes |
Generate optional meal suggestions |
Clients do not choose a store_id; the API assigns 09700117.
make lint
make test
make test-int
make solver-test
make web-build
cd web && npm run test:e2eTo rebuild local data from scratch:
make down-v
make up
make migrate-up
make seed
make kroger-ingestThe repository includes Fly configuration for the API and solver, plus a Next.js frontend suitable for Vercel. See docs/DEPLOY.md for the required secrets, callback URL, migration order, and production checks.
Price refreshes run from .github/workflows/kroger-ingest.yml against the fixed University Place location. The importer records price history only when a price changes and marks products unavailable when they disappear from the catalog.