- **Message limit** — enforced in `app/(chat)/api/chat/route.ts` via `checkAndConsume(userId, "chat_message")` before `streamText`.
- **Tool limit** — `getWeather` is wrapped with `withLimit(tool, "tool_call", userId)` (see `lib/ai/tools/with-limit.ts`). Every tool call deducts 1 unit; if quota is hit the tool returns `{ error: "limit_exceeded", ... }` which the model sees and explains to the user.
- **Quota badge** — `<UsageBadge />` in the chat header polls `/api/usage` every 15s.
- **Upgrade flow** — `/pricing` → POST `/api/billing/checkout` → redirect to Stripe → webhook at `/api/billing/webhook` flips the user's tier on `checkout.session.completed`.
To add a new tool with metering, just wrap it the same way:
`app-deploy.sh` auto-injects: `DATABASE_URL`, `EGBE_AI_API_*`, `NEXT_PUBLIC_BASE_URL`, `NEXT_PUBLIC_UMAMI_*`, `EGBE_PAYMENT_GATEWAY_*`, `EGBE_GATEWAYS_TOKEN`. You pass `AUTH_SECRET` and `PRO_STRIPE_PRICE_ID` (if billing is desired) via `APP_ENV_JSON`.