<!--
App Infrastructure Playbook

Part of the free Claude Code template library from BILT AI — https://biltai.io/claude-templates

These are the actual playbooks used to build and ship production SaaS apps with
Claude Code. They're written to be handed to an agent as-is: point Claude at one
and it has the full pattern, the schema, the gotchas, and the "don't do this"
list. Paths, project refs, and org names have been replaced with placeholders
(<your-app>, <supabase-project-ref>, <your-github-org>) — search for "<" and
fill in your own before running.

Free to use, modify, and ship. No attribution required.
-->

# App Infrastructure Playbook

**The MOTHER PLAYBOOK. The META orchestrator that defines the full tech stack and bootstrapping sequence for any new SaaS app in a multi-app SaaS portfolio.**

When you say "I want to launch a new app," this is the first thing the agent reads. It explains the full stack, the deployment topology, the bootstrapping sequence (Week 0 → Launch), and points to every other tier-S/A/B/C playbook in the order they should be executed.

Reference repo (always the source of truth for "how does BKE do this?"): `reference app`.

---

## 0. Use this spec

**Inputs (collect from the owner before starting Week 0):**
- `APP_NAME` — e.g. "Kompozy", "BILT Pulse", "ColdReach". Used for repo name, Vercel project, Supabase project, folder name.
- `DOMAIN` — root domain (e.g. `kompozy.com`). Must be DNS-resolvable; you own the registrar.
- `ONE_LINER` — single sentence pitch. Goes in `README.md`, `<meta description>`, Postmark sender display name.
- `ICP` — target buyer (e.g. "real estate wholesalers under 5 deals/mo", "B2B SaaS founders 1–10 employees"). Drives cold-email copy + onboarding language.
- `PRIMARY_KEYWORD_CLUSTER` — 3–5 head keywords for topical authority work (e.g. for Kompozy: "social media automation", "AI content scheduler", "cross-platform posting").
- `TARGET_LAUNCH_DATE` — anchors the 4-week build sequence. If <4 weeks, cut Week 3 admin scope first.
- (Optional) `BRAND_TOKENS` — primary color, accent color, font family. Defaults to BKE's Tailwind v4 token set if absent.

**Outputs (what exists at end of Week 0):**
- GitHub repo under `github.com/<your-github-org>/<app-slug>`
- Vercel project connected to repo, auto-deploying on push to master
- Supabase project (URL + anon + service-role key in `.env.local`)
- Trigger.dev project (secret key + project ref in `.env.local`)
- Postmark server + verified sender on `<DOMAIN>` with SPF/DKIM/DMARC
- Stripe account (or product under existing umbrella — see §7)
- Upstash Redis instance
- DNS configured (A/ALIAS to Vercel, MX/SPF/DKIM/DMARC to Postmark)
- Local `npm run dev -- --webpack --port 3001` works and renders a Next.js boilerplate at `localhost:3001`

---

## 1. The stack (canonical)

This is the locked stack. Don't substitute. Don't shop around. Don't introduce a "better" alternative without explicit approval — every swap multiplies the maintenance surface across the portfolio.

| Layer | Tool | Why | Notes |
|---|---|---|---|
| **Hosting** | Vercel | Zero-config Next.js, auto-deploy on `master` push, edge runtime for middleware | Next.js 16 + React 19 + App Router. **Webpack NOT Turbopack** (memory: `no_turbopack.md`). Dev port **3001**. |
| **Database** | Supabase | Postgres + Auth + Storage + Realtime + RLS in one console | Migrations live in `supabase/migrations/`. NEVER destructive sync (memory: `feedback_never_destructive_db_writes.md`). Build on Supabase directly, no new localStorage (memory: `feedback_supabase_first.md`). |
| **Background workers** | Trigger.dev v4 | Long-running tasks (>60s), retries, scheduled jobs, queues | **Every task >60s MUST be a worker.** Never browser-bound long requests. Set `NEXT_PUBLIC_APP_URL` in Trigger env (memory: `bke_trigger_app_url.md`). |
| **Version control** | GitHub | Vercel auto-deploys from master | Repo under `github.com/<your-github-org>/`. Session-scoped commits MANDATORY (memory: `feedback_session_scoped_commits_no_ask.md`). NEVER `git add -A`. |
| **Auth** | Supabase Auth via `@supabase/ssr` | Cookie-based SSR auth, OAuth + email/password | See `AUTH-ONBOARDING-PLAYBOOK`. |
| **Payments** | Stripe | Checkout, billing portal, webhooks | See `STRIPE-BILLING-PLAYBOOK`. Separate accounts per app (§7). |
| **Email** | Postmark | Transactional + inbound + broadcast streams, best deliverability for SaaS | See `EMAIL-INFRASTRUCTURE-PLAYBOOK`. |
| **Image rendering** | Satori + Resvg | Server-side OG / share-card generation in pure JS | Carved-out gotchas — see lab notes 2026-04-23 (variable-font crash), 2026-04-24 (no per-slug `opengraph-image.tsx`). |
| **Media storage** | Supabase Storage `generated-media` bucket | All provider outputs persisted before URLs expire | NEVER store provider URLs (memory: `feedback_never_store_provider_urls.md`). Run every external URL through `persistMediaToStorage` helper. |
| **Rate limiting** | Upstash Redis + `@upstash/ratelimit` | Edge-compatible, no cold-start | In-memory fallback in dev (`process.env.NODE_ENV !== 'production'`). |
| **Analytics** | First-party `/api/track` | Owns the data; no GDPR vendor; deduped via visitor_id cookie | Writes to `page_events` + `page_event_visitors`. See `CUSTOMER-ANALYTICS-PLAYBOOK`. |
| **Observability** | Vercel logs + Trigger.dev runs dashboard | Per-environment logs, no third-party agent | Lab notes appended to repo CLAUDE.md on every failed-attempt resolution. |

---

## 2. Deployment topology

```
                              Browser
                                 │
                                 ▼
                     ┌──────────────────────┐
                     │  Vercel (Next.js)    │
                     │  - /app routes       │
                     │  - /api routes       │
                     │  - proxy.ts (edge)   │
                     └──────────┬───────────┘
                                │
            ┌───────────────────┼───────────────────────┐
            │                   │                       │
            ▼                   ▼                       ▼
   ┌────────────────┐  ┌────────────────┐    ┌──────────────────┐
   │   Supabase     │  │  Trigger.dev   │    │  External APIs   │
   │  - Postgres    │  │  - workers     │    │  (per app: GHL,  │
   │  - Auth        │  │  - schedules   │    │   Anthropic,     │
   │  - Storage     │  │  - queues      │    │   ElevenLabs,    │
   │  - RLS         │  └───────┬────────┘    │   HeyGen, etc.)  │
   └────────────────┘          │             └──────────────────┘
            ▲                  │
            │                  ▼
            └──────── writes back to Supabase

   Stripe ──────────────► Vercel /api/stripe/webhook ──► Supabase
   Postmark (inbound) ──► Vercel /api/email/inbound ───► Supabase
   Postmark (bounces) ──► Vercel /api/email/webhook ───► Supabase
   Trigger.dev runs ────► Vercel /api/jobs/[runId] (status proxy for browser polling)
   GitHub push to master ─► Vercel (auto-deploy)
   GitHub push to master ─► (manual) npx trigger.dev@<version> deploy  ← when worker code/deps change
```

**Key invariants:**
- **Browser never talks to Trigger.dev directly.** Always through `/api/jobs/[runId]` status proxy. Browser polls Vercel; Vercel reads Trigger.dev runs API.
- **Webhooks land on Vercel, never directly on Supabase.** Vercel route handler verifies signature → writes to Supabase via service-role client.
- **Trigger.dev workers call back into Vercel** for shared logic (publish, persist media). That's why `NEXT_PUBLIC_APP_URL` MUST be set in Trigger env or workers default to localhost:3001.
- **Two deploy targets per push to master:** Vercel (automatic) + Trigger.dev (manual `npx trigger.dev@<version> deploy` when worker code or transitively imported files changed).

---

## 3. Bootstrapping sequence (Week 0)

Execute in order. Each step has a verification check — don't proceed until it passes.

1. **Create folder.** `<your-app>/` (uppercase slug, hyphenated). Verify with `ls` that parent exists.

2. **Next.js init.**
   ```bash
   npx create-next-app@latest <app-slug> --typescript --app --tailwind --eslint --no-src-dir
   ```
   Wait — Next 16 changed defaults. After init, **edit `package.json` dev script** to `next dev --webpack --port 3001` (no Turbopack, fixed port). Verify: `npm run dev` boots at `localhost:3001`.

3. **Install canonical deps.**
   ```bash
   npm install @supabase/ssr @supabase/supabase-js @trigger.dev/sdk@latest @trigger.dev/build stripe @upstash/ratelimit @upstash/redis zod postmark satori @resvg/resvg-js
   npm install -D @types/node
   ```
   Pin `@trigger.dev/sdk` and `@trigger.dev/build` to identical versions — the CLI aborts on mismatch.

4. **Supabase project.** Create at supabase.com/dashboard. Region = `us-east-1` (matches Vercel default edge). Copy `URL`, `anon key`, `service_role key` into `.env.local`. Verify: `npx supabase --version` works; `supabase link --project-ref <ref>`.

5. **Trigger.dev project.** Create at trigger.dev. Get `TRIGGER_PROJECT_REF` + `TRIGGER_SECRET_KEY` (prod + dev). Set keys in **three places**: `.env.local`, Vercel env, Trigger.dev project env. Verify: `npx trigger.dev@<version> login` then `npx trigger.dev@<version> init`.

6. **Vercel project.** `vercel link` from repo root, or create via dashboard and connect to GitHub repo (next step). Set production branch = `master`. Add all env vars from §6 to Production environment. Verify: a dummy commit to master triggers a build.

7. **GitHub repo.** Create `github.com/<your-github-org>/<app-slug>` (private). Push initial commit. **Branch protection OFF on master** — we deploy direct, no PR gate. CI = none (Vercel handles).

8. **Postmark server.** Create new server (one per app, isolates reputation). Add sender signature on `<DOMAIN>`. Pull DNS records (DKIM, Return-Path) — set on registrar in next step. Get `POSTMARK_SERVER_TOKEN`. See `EMAIL-INFRASTRUCTURE-PLAYBOOK` §1.

9. **Stripe account.** Per §7 portfolio rule: **separate Stripe account per app** (clean payout, clean tax, clean dispute history). Create products + prices later in Week 2; for Week 0 just collect `STRIPE_SECRET_KEY` (test mode is fine until launch). Webhook signing secret comes after deploying the webhook route.

10. **DNS.** On the domain registrar (the operator's at Namecheap / Cloudflare depending on age of domain):
    - `A` or `ALIAS @` → Vercel (`76.76.21.21` or Vercel-provided ALIAS target)
    - `CNAME www` → `cname.vercel-dns.com`
    - `MX`, DKIM `CNAME`, Return-Path `CNAME`, SPF `TXT`, DMARC `TXT` → Postmark values (from step 8)
    Verify: `dig <domain>` resolves, `dig MX <domain>` shows Postmark, Postmark "Sender Signatures" all green.

11. **Upstash Redis.** Create global database. Copy `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN` to `.env.local` + Vercel.

12. **Copy BKE config files** as starting points (don't reinvent):
    - `eslint.config.mjs`
    - `tsconfig.json`
    - `next.config.ts` (keep `serverExternalPackages` block, prune redirects)
    - `postcss.config.mjs`
    - `trigger.config.ts` (prune `additionalPackages` / `additionalFiles` to what this app actually needs — don't ship ffmpeg if no video)

13. **Seed `.env.example`** with every key from §6 (placeholder values, no secrets). Commit it. **Never commit `.env.local`** — verify it's in `.gitignore`.

14. **Smoke test.** Push a "hello world" homepage to master. Vercel deploys. Visit `https://<domain>` — verify it returns 200 with no console errors. **Week 0 done.**

---

## 4. Reference repo structure (mirror BKE)

```
<APP-NAME>/
├── .env.local                    # NEVER commit — secrets live here
├── .env.example                  # commit with placeholder values
├── .gitignore                    # must include .env.local, .next, node_modules, .trigger
├── next.config.ts                # serverExternalPackages for native modules, redirects
├── trigger.config.ts             # Trigger.dev config (additionalFiles, additionalPackages, ffmpeg if needed)
├── package.json                  # dev script: "next dev --webpack --port 3001"
├── tsconfig.json
├── eslint.config.mjs
├── postcss.config.mjs
├── CLAUDE.md                     # Per-repo agent rules. Start from BKE's, prune to fit.
├── AGENTS.md                     # "This is NOT the Next.js you know" — Next 16 + React 19 + Tailwind v4 gotchas
├── README.md                     # One-liner + local dev instructions
├── <APP_NAME>_SPEC.md            # Source-of-truth spec doc (rename KONTENT_ENGINE_SPEC.md pattern)
├── lab-notes.md                  # Or inlined section in CLAUDE.md. Append every failed-attempt resolution.
├── supabase/
│   ├── config.toml
│   └── migrations/               # NNNNN_descriptive_name.sql, numeric ordering
├── src/
│   ├── proxy.ts                  # NEXT 16 renamed middleware.ts → proxy.ts. Edge auth gateway.
│   ├── app/                      # Routes. See AUTH-ONBOARDING-PLAYBOOK §1.
│   │   ├── (marketing)/          # Public marketing pages
│   │   ├── signin/
│   │   ├── signup/
│   │   ├── onboarding/
│   │   ├── dashboard/
│   │   ├── admin/
│   │   ├── api/
│   │   │   ├── stripe/webhook/
│   │   │   ├── email/webhook/
│   │   │   ├── email/inbound/
│   │   │   ├── jobs/[runId]/     # Trigger.dev status proxy
│   │   │   ├── track/            # First-party analytics ingest
│   │   │   └── admin/
│   │   └── layout.tsx
│   ├── lib/
│   │   ├── supabase.ts           # Browser client
│   │   ├── supabase-server.ts    # Server + admin (service-role) client
│   │   ├── user-auth.ts          # requireUser() helper
│   │   ├── admin.ts              # Admin role + account status helpers
│   │   ├── stripe.ts             # Stripe client + helpers
│   │   ├── postmark.ts           # Postmark client
│   │   ├── rate-limit.ts         # Upstash ratelimit wrapper
│   │   ├── media-storage.ts      # persistMediaToStorage helper
│   │   └── trigger-status.ts     # Run status proxy helper
│   ├── trigger/                  # Trigger.dev tasks. See TRIGGER-DEV-WORKER-PLAYBOOK.
│   │   └── <task-name>.ts
│   ├── components/
│   └── types/
├── public/
│   ├── favicon.ico
│   └── og-default.png
└── docs/
    ├── outreach/                 # See TOPICAL-AUTHORITY-PLAYBOOK
    └── audits/                   # CWV reports, Lighthouse runs
```

**Variants from BKE worth noting:**
- BKE has `src/proxy.ts` (Next 16 rename). Older BKE branches may still have `middleware.ts`. New app: always `proxy.ts`.
- BKE's `trigger.config.ts` includes ffmpeg. Strip that if the new app has no video/audio.
- BKE has `KONTENT_ENGINE_SPEC.md`. Rename for new app — pattern is `<APP_NAME>_SPEC.md` uppercase.

---

## 5. The build sequence (order of operations)

After Week 0 bootstrapping completes, execute the playbooks below in this order. Don't skip — each phase depends on the previous one's foundation.

### Week 1 — Foundation (public site + auth)

- **`Topical Authority Playbook`** — Build the homepage, cluster hubs, `sitemap.xml`, `robots.txt`, `llms.txt`, Organization JSON-LD schema. Anchored on `PRIMARY_KEYWORD_CLUSTER`. Ship public marketing surface FIRST so GSC indexing has a 3-week head start before launch.
- **`Auth + Onboarding Playbook`** — `proxy.ts`, signup/signin/reset routes, `auth/callback`, onboarding wizard, profile auto-create trigger, account-status enforcement. By end of Week 1: a user can sign up, complete onboarding, and reach `/dashboard`.

### Week 2 — Payments + comms

- **`Stripe Billing Playbook`** — Define plan tiers + Stripe products, build checkout + portal flows, webhook handler at `/api/stripe/webhook`, sync subscriptions → `profiles.account_status`.
- **`Email Infrastructure Playbook`** — Postmark transactional templates, broadcast stream, drip campaigns, unsubscribe HMAC, DMARC enforcement, bounce/complaint webhook. Wire trial-signup → enrollment trigger from Auth playbook.

### Week 3 — Workers + admin

- **`Trigger.dev Worker Playbook`** — Three-file pattern (`src/trigger/<task>.ts` + `/api/jobs/[runId]/route.ts` + dispatch route) for every long-running task. Wire the app's first real worker (the highest-value async job — e.g. content generation, scraping, video render).
- **`Credit Metering Playbook`** — Only if the app charges per-output (Kompozy: yes; a flat-monthly tool: skip). Define credit table, deduct-on-success pattern, top-up Stripe products.
- **`Admin Console Playbook`** — Customer list, impersonation (`POST /api/admin/impersonate`), audit log, account-status toggles, credit grants. You need this on day 1 for support.

### Week 4 — Polish + launch prep

- App-specific publishing/integrations. Cross-ref `Publishing Pipeline Playbook` if the app posts to social/CRM/email-marketing platforms.
- **`Customer Analytics Playbook`** — `/api/track` ingest, `page_events` + `page_event_visitors` schema, admin dashboard funnel views.
- **`Beta Tester Playbook`** — `beta_testers` table, invite flow, feedback capture, founding-tier (BKE pattern: <founding-price>/mo lifetime BYO-key, see `bke_founding_tier.md`).
- **`Pre-Production Hardening Checklist`** — RLS audit, env var audit, rate-limit audit, error-boundary coverage, 404/500 pages, Lighthouse > 90.

### Launch week

- **`Cold Email Campaign Playbook`** — Start the cold-email launch to the warm house list. Daily metrics tracked in `Active/<APP-NAME>/docs/outreach/`.
- GSC + Bing Webmaster Tools — submit sitemap, request indexing on top 10 URLs.
- Wikidata + Crunchbase entity setup (GEO foundation, covered in Topical Authority playbook §7).
- Switch Stripe live mode, verify webhook signing secret matches.
- Verify Trigger.dev prod deployment matches latest master commit (`npx trigger.dev@<version> deploy`).

---

## 6. Environment variables checklist

Every app needs these in **three places**: `.env.local` (local dev), Vercel project env (prod), Trigger.dev project env (workers). Variables marked **server-only** must NEVER be prefixed `NEXT_PUBLIC_` or referenced from a client component.

```bash
# --- Supabase ---
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=                # server-only — bypasses RLS

# --- Trigger.dev ---
TRIGGER_SECRET_KEY=                       # server-only
TRIGGER_PROJECT_REF=

# --- Stripe ---
STRIPE_SECRET_KEY=                        # server-only
STRIPE_WEBHOOK_SECRET=                    # server-only — from Stripe dashboard after webhook URL set
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=

# --- Postmark ---
POSTMARK_SERVER_TOKEN=                    # server-only
POSTMARK_INBOUND_USER=                    # basic-auth user for /api/email/inbound
POSTMARK_INBOUND_PASS=                    # basic-auth pass
EMAIL_UNSUBSCRIBE_SECRET=                 # server-only — HMAC key for unsubscribe tokens

# --- Upstash Redis ---
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=

# --- App ---
NEXT_PUBLIC_APP_URL=https://<domain>      # MUST be set in Trigger.dev env or workers fetch localhost:3001
                                          # (memory: bke_trigger_app_url.md)

# --- Optional: publishing workers ---
PUBLISH_WORKER_SECRET=                    # server-only — if app has scheduled publish jobs
                                          # MUST be set in BOTH Vercel and Trigger.dev
                                          # (memory: bke_publish_worker_secret.md)

# --- Per-app external API keys (examples — depends on app) ---
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
ELEVENLABS_API_KEY=
HEYGEN_API_KEY=
# ... etc
```

**Audit rule:** before launch, grep `process.env\.` across the repo and confirm every referenced var is set in all three locations. Vercel build will succeed with missing vars — runtime is where you find out.

---

## 7. Hard architectural rules (locked-in decisions across all apps)

From the operator's portfolio strategy (`moe_10_app_portfolio_strategy.md`):

1. **Every app works fully standalone.** Own Supabase project. Own auth. Own Stripe account + customer + products. Own brand. Own root domain. A customer of App A has zero footprint in App B's database.

2. **NO shared SDK.** No `@moe/shared-lib` npm package. No `@bilt/auth`. Each app re-implements the boilerplate from this playbook + the BKE reference. The cost of a shared lib (version pinning, breaking changes cascading across 8 apps) >> the cost of duplication.

3. **NO federated auth.** No SSO across apps. No "log in with BILT". A user signs up fresh in each app. (Trade-off accepted: friction in cross-sell, but isolation in security + churn.)

4. **NO cross-app DB reads.** App A never queries App B's Supabase. If data needs to flow, it's via a public REST API the user authorizes — same as any third-party integration.

5. **NO mandatory bundle.** Apps are never priced as a suite. Each has its own pricing page, its own checkout, its own value prop.

6. **Cross-promotion via MARKETING ONLY:**
   - Cross-sell drip emails ("Hey, you use BKE — try ColdReach")
   - Affiliate codes
   - Optional "Built by <you>" landing or footer link
   - Shared blog SEO interlinking
   - Founder-led video content mentioning multiple apps

7. **Optional: public REST API per app** so power users / Zapier / Make.com can wire apps together themselves. The app builds APIs for ITS users — not for the other apps in the portfolio.

8. **Separate Stripe accounts** (not Connect, not multi-product on one account). Clean payouts. Clean tax. Clean dispute history per brand.

---

## 8. Common pitfalls (from BKE lab notes)

The cumulative scar tissue from BKE. Read before starting. Each of these cost hours / dollars / customer trust.

- ❌ **Don't `git add -A` / `git commit -a` / `git add .`** — session-scoped commits only. Stage by exact path. Other agents may be editing the same repo in parallel.
- ❌ **Don't use Turbopack.** Next 16 enables it by default. Add `--webpack` flag explicitly to dev + build. Turbopack causes 1.6GB+ memory pile-up from a root-inference bug. (Memory: `no_turbopack.md`.)
- ❌ **Don't store provider URLs** (DALL-E, HeyGen, Kie, ElevenLabs, etc.). They expire in 1h. Always run output through `persistMediaToStorage` → Supabase Storage `generated-media` bucket.
- ❌ **Don't write destructive sync routines.** Bulk-replace (DELETE non-matching + UPSERT) wiped `creator_profiles` on 2026-04-23. Upsert-only. Deletes only from explicit user actions.
- ❌ **Don't parallelize `supabase.auth.getUser()` at boot.** Gotrue Web Lock cascade. Hoist a single call to top of dashboard hydrate path.
- ❌ **Don't use per-slug `opengraph-image.tsx`.** Satori/Resvg variable-font crash. Generate share cards via a static `/api/og` route with fixed-axis fonts.
- ❌ **Don't deploy from feature branches.** Vercel deploys only from `master`. Merge → push master → auto-deploy. (Memory: `feedback_deploy_master_only.md`.)
- ❌ **Don't skip Trigger.dev redeploy** when worker code OR its transitively-imported dependencies change. Vercel deploy ≠ Trigger.dev deploy. Workers keep running yesterday's code until `npx trigger.dev@<version> deploy` runs.
- ❌ **Don't trust the in-conversation context** — always verify against current code before claiming a rule. Injected CLAUDE.md snapshots can truncate. (Memory: `feedback_claudemd_full_read.md`.)
- ❌ **Don't tighten dispatch-route Zod schemas on recovery-context fields.** Required-ing a field like `workspaceId` turns silent-recovery into 400 crashes. Keep recovery fields `.optional()`.
- ❌ **Don't skip the local build before pushing.** Vercel logs are first stop, not last. (Memory: `feedback_verify_before_push.md`.)
- ❌ **Don't pass JS floats to PostgREST integer columns.** `Math.ceil()` / `Math.floor()` before any `.gte/.lte/.eq` on int columns. Floats crash the whole query AND its fallbacks. (Memory: `feedback_postgrest_int_columns.md`.)
- ❌ **Don't add new `/api/*` routes without updating `PUBLIC_API_PREFIXES`** in `proxy.ts`. Default-deny means custom auth in the route handler is unreachable until proxy lets the request through. (Memory: `bke_proxy_default_deny.md`.)
- ❌ **Don't show secrets / `.env` files / API keys on screen during livestreams.** All BKE dev streams to YouTube + Rumble. (Memory: `bke_livestream.md`.)

---

## 9. Agent kickoff prompt (paste into Claude when launching a new app)

> I want to launch a new app called **[APP_NAME]** in the portfolio. Read the App Infrastructure Playbook at `<workspace>/templates/s-tier/APP-INFRASTRUCTURE-PLAYBOOK.md` and execute Week 0 bootstrapping in order. Reference the BKE codebase at `<reference-app>/` for every pattern — when in doubt about a file, read the BKE version rather than re-deriving.
>
> **Inputs:**
> - APP_NAME: [name]
> - DOMAIN: [domain]
> - ONE_LINER: [single sentence pitch]
> - ICP: [target buyer]
> - PRIMARY_KEYWORD_CLUSTER: [3–5 head keywords]
> - TARGET_LAUNCH_DATE: [date]
>
> Stop after Week 0 bootstrapping completes (folder created + Next.js init + Supabase + Trigger.dev + Vercel + Postmark + Stripe + Upstash + DNS verified + `localhost:3001` boots + `https://<domain>` returns 200). I'll review and then we'll proceed to Week 1.
>
> Use session-scoped commits. Never `git add -A`. Append every failed-attempt resolution to the new repo's `CLAUDE.md` lab-notes section.

---

## 10. Reference files in BKE (every key pattern)

When mirroring BKE, these are the load-bearing files. Read them before re-implementing in the new app.

**Auth + middleware:**
- `src/proxy.ts` — edge auth gateway
- `src/lib/supabase.ts` — browser client
- `src/lib/supabase-server.ts` — server + admin client
- `src/lib/user-auth.ts` — `requireUser()` helper
- `src/lib/admin.ts` — account status + admin role

**Workers:**
- `trigger.config.ts` — Trigger.dev config
- `src/trigger/` — all worker tasks
- [src/app/api/jobs/[runId]/route.ts](file:///<reference-app>/src/app/api/jobs/) — status proxy

**Stripe:**
- `src/lib/stripe.ts` — client + helpers
- `src/app/api/stripe/webhook/route.ts` — webhook handler

**Email:**
- `src/lib/postmark.ts` — Postmark client
- `src/app/api/email/` — webhook + inbound

**Storage:**
- `src/lib/media-storage.ts` — `persistMediaToStorage`

**Onboarding + dashboard:**
- `src/app/onboarding/` — wizard
- `src/app/dashboard/layout.tsx` — hydration + gating
- `src/app/admin/` — admin console

**Config:**
- `next.config.ts`
- `tsconfig.json`
- `eslint.config.mjs`
- `postcss.config.mjs`
- `CLAUDE.md` — per-repo agent rules + lab notes
- `AGENTS.md` — Next 16 / React 19 / Tailwind v4 warnings

---

## 11. Cross-reference index

The full playbook library. Invoke each when its phase begins.

| Playbook | Tier | When to invoke | One-liner |
|---|---|---|---|
| `APP-INFRASTRUCTURE-PLAYBOOK` | S | First — Week 0 | This file. Master orchestrator. |
| `TOPICAL-AUTHORITY-PLAYBOOK` | S | Week 1 | Homepage, cluster hubs, sitemap, robots, llms.txt, Org schema. SEO foundation. |
| `AUTH-ONBOARDING-PLAYBOOK` | S | Week 1 | Supabase auth, proxy.ts, signup/signin, onboarding wizard, account-status gating. |
| `STRIPE-BILLING-PLAYBOOK` | S | Week 2 | Stripe products, checkout, billing portal, webhook → account_status sync. |
| `EMAIL-INFRASTRUCTURE-PLAYBOOK` | S | Week 2 | Postmark transactional + broadcast, drip campaigns, unsubscribe, DMARC. |
| `TRIGGER-DEV-WORKER-PLAYBOOK` | A | Week 3 | Three-file pattern for every long-running task. |
| `CREDIT-METERING-PLAYBOOK` | S | Week 3 (if per-output pricing) | Credit table, deduct-on-success, top-up Stripe products. |
| `ADMIN-CONSOLE-PLAYBOOK` | S | Week 3 | Customer list, impersonation, audit log, account-status toggles. |
| `PUBLISHING-PIPELINE-PLAYBOOK` | A | Week 4 (if app posts externally) | Scheduled post worker, platform connectors, failure surfacing. |
| `CUSTOMER-ANALYTICS-PLAYBOOK` | A | Week 4 | First-party `/api/track`, page_events schema, funnel dashboard. |
| `BETA-TESTER-PLAYBOOK` | A | Week 4 | beta_testers table, invite flow, founding-tier pattern. |
| `COLD-EMAIL-CAMPAIGN-PLAYBOOK` | A | Launch week | Cold-email launch to warm list, daily metrics + iteration. |
| `PRE-PRODUCTION-HARDENING-CHECKLIST` | C | Week 4 → Launch | RLS audit, env audit, rate-limit audit, Lighthouse > 90, 404/500 pages. |

**Tier legend:**
- **S-tier:** core systems every app needs. Skip = launch failure.
- **A-tier:** high-leverage extensions. Skip only if the app's model genuinely doesn't need them.
- **B-tier:** specialized (per-app integrations, niche flows). Invoke as the app's domain requires.
- **C-tier:** pre-launch checklists + hardening. Invoke late, but always invoke.

---

**End of master playbook.** From here, follow §5's week-by-week sequence and invoke the linked playbooks in order. When in doubt: read the BKE file, don't re-derive.
