# AI Starter — full documentation export Generated: 2026-06-11T18:52:22.078Z --- ## Custom billing provider URL: https://docs.oequ.io/billing/custom-provider # Custom billing provider Use this when Stripe is not available (e.g. YooKassa, CloudPayments, invoice billing). See [Credits overview](/billing) and the monorepo guide `docs/BILLING_CUSTOM_PROVIDER.md`. --- ## Credits & billing URL: https://docs.oequ.io/billing # Credits & billing ## Credit ledger Balances live in `credit_balances`; every movement is recorded in `credit_transactions`. | Type | Meaning | |------|---------| | `grant` / `topup` | Add credits | | `debit` | Job completed | | `refund` | Job failed / cancelled | | `monthly_reset` | Allowance top-up | **Reserve** moves credits from `available` → `reserved` at submit. **Finalize** debits or refunds. ## Plans | Plan | Generation credits / month | |------|----------------------------| | free | 500 | | pro | 3,000 | | team | 10,000 | Plan changes sync allowance via `sync_org_generation_credits_for_plan` (service role / internal RPC only). ## Billing providers | `billingProvider` | Checkout | |-------------------|----------| | `mock` | In-memory + `update_organization_plan` (local only, gated) | | `stripe` | Stripe Checkout + webhooks | | `custom` | Your Edge webhook → `apply_billing_subscription` | UI uses `BillingPort` — no payment SDK in features. ## Web app adapter `WebBillingAdapter` merges Postgres billing snapshot with mock plan metadata for display. Stripe paths invoke Edge functions; mock paths sync plan to Postgres after checkout. See [Custom provider](/billing/custom-provider) and [Monthly reset](/billing/monthly-reset). --- ## Monthly reset URL: https://docs.oequ.io/billing/monthly-reset # Monthly reset ## Behavior `reset_monthly_generation_credits()` runs on a schedule (pg_cron on hosted Supabase, or `credits-monthly-reset` Edge function). For each org due for reset: 1. Compute allowance from plan (`free` / `pro` / `team`) 2. Set `available = max(current_available, allowance)` — **never reduces** carryover or purchased top-ups 3. Log positive delta as `monthly_reset` transaction 4. Advance `reset_at` to next UTC month boundary Orgs with active `reserved` credits are **still reset** (allowance metadata); reserved amounts are unchanged. ## Stale jobs `reap_stale_generation_jobs()` fails jobs stuck in `queued`/`processing` past a timeout (default 30 minutes) and refunds reserves via `finalize_generation_job`. Schedule via pg_cron every 5 minutes or call `generation-reap-stale` Edge function with cron secret / service role. ## Operations ```bash # Edge (self-hosted) curl -X POST https://your-api/functions/v1/credits-monthly-reset \ -H "Authorization: Bearer $SERVICE_ROLE_KEY" curl -X POST https://your-api/functions/v1/generation-reap-stale \ -H "X-Cron-Secret: $CREDITS_CRON_SECRET" ``` See ADR `docs/adr/0007-monthly-credit-reset.md`. --- ## Architecture URL: https://docs.oequ.io/concepts/architecture # Architecture ## Hexagonal layout ```text apps (demo, web) provideDemoAdapters() / provideWebAdapters() ↓ libs/features-* → inject *PORT, resource() ↓ libs/ports → interfaces only ↓ adapters-mock | data-access-supabase ``` ## Hard rules 1. **Never** import adapters from `libs/features-*` or `libs/ui`. 2. Features inject port tokens: `GENERATION_PORT`, `CREDIT_PORT`, `ORG_PORT`, … 3. `libs/ports` must not import `@angular/*` or `rxjs` in port logic. 4. Adapter swap **only** in `apps/*/src/app/app.config.ts`. ## Angular conventions | Use | Avoid | |-----|--------| | Standalone components | New NgModules | | `input()`, `computed()`, `resource()` | `@Input`, `subscribe()` in templates | | `@if`, `@for` | `*ngIf`, `*ngFor` | | Functional guards | Class guards | ### Critical: `inject()` in guards Call `inject()` **before** any `await` in guards. After the first await there is no injection context → **NG0203**. ### `resource()` stability Pass a **stable string key** in `params`, not a new object each computed run — avoids gallery flicker. ## Port results Adapters return `PortResult` (`ok` + `data` or `error`). Features branch on `result.ok`; templates use `resource()` loaders. ## Apps | App | Backend | Use | |-----|---------|-----| | `apps/demo` | Mock only | Static hosting, PWA, fast E2E | | `apps/web` | Supabase | Studio — full stack local + production | | `apps/api-console` | Supabase | Public API developer portal | --- ## Concepts URL: https://docs.oequ.io/concepts # Concepts AI Starter is organized around a few core ideas. Read these before changing code. ## Diátaxis map | Section | Purpose | |---------|---------| | [Getting started](/getting-started) | Tutorials — run the stack | | **Concepts** (here) | Explanations — why things are shaped this way | | [Generation](/generation) | Domain + pipeline | | [Billing](/billing) | Credits & payments | | [Security](/security/production-hardening) | Production hardening | | [Operations](/operations/stack) | Stack & workflow | ## Core principles 1. **Contracts over implementations** — ports in `libs/ports`, adapters in `libs/data-access-supabase` / `libs/adapters-mock`. 2. **Backend is authority** — RLS + Edge service role; Angular guards are UX only. 3. **Provider ignorance in UI** — logical model ids; fal/Stripe/YooKassa live at the edge or in config. 4. **Progressive productization** — mock path works without API keys; real providers behind env flags. See [Philosophy & layers](/concepts/philosophy) and [Architecture](/concepts/architecture). --- ## Philosophy & layers URL: https://docs.oequ.io/concepts/philosophy # Philosophy & layers ## Mission AI Starter is a **product-agnostic foundation** for SaaS apps that need organizations, **credits**, **async AI runs**, and a **creative studio** UX (boards, batch variants, gallery). The platform investment stays reusable if a single vertical product fails. ## Three repository layers ```text Product fork (e.g. ai-media-hub) fal catalog, regional payments, GTM ↓ fork from ai-starter AI Starter (this repo) credits, generation, boards, chat shell ↓ merge starter/main SaaS Starter auth, org, base billing, shell, RLS ``` ## Decision checklist | Question | If yes → | |----------|----------| | Auth, org shell, generic billing UI? | Upstream **saas-starter**, merge here | | Credits, generation, boards, Edge pipeline? | **This repo** | | fal-only GTM, video-first UX, one market? | **Product fork** | ## What does not belong here - Product-only GTM docs - Replacing Stripe adapters when `billingProvider: 'custom'` is enough - Provider SDKs inside `libs/features-*` Canonical detail: `docs/adr/0004-ai-starter-layer.md` in the monorepo. --- ## Security model URL: https://docs.oequ.io/concepts/security # Security model ## Trust boundaries | Layer | Role | |-------|------| | Browser | Untrusted — anon key only | | Angular guards | UX — redirect guests, hide billing | | Postgres RLS | **Authorization** for reads | | Edge Functions (service role) | **Authorization** for writes (credits, jobs, billing apply) | ## Generation - `generate` / `generate-batch`: JWT + org membership + project editor - `generation-webhook`: shared secret or provider signature; SSRF allow-list on output URLs - `generation-process`: project editor (aligned with cancel) - Credit RPCs: `service_role` only ## Billing - Plan changes in production: `apply_billing_subscription` via verified webhooks - `update_organization_plan`: gated by `private.platform_settings.self_serve_billing` (off by default; seed enables for local mock) ## Tables `generation_jobs`, `credit_balances`, `credit_transactions` — SELECT for org members; no authenticated INSERT on jobs. See [Production hardening](/security/production-hardening) for the full backlog and P0/P1 checklist. --- ## Generation domain URL: https://docs.oequ.io/generation # Generation domain ## Concepts | Concept | Storage / port | |---------|----------------| | Board | `organization_projects` · `ProjectPort` | | Generation job | `generation_jobs` · `GenerationPort` | | Credits | ledger RPCs · `CreditPort` | | Conversation | `conversations` · `ConversationPort` | | Model | catalog in `libs/generation-core` | ## User flows **Boards home** — prompt + model → create board → `submitJobBatch` → navigate to studio. **Board studio** (`/boards/:slug`) — chat + gallery; batch rows with 4 variants; video uses `