> ## Documentation Index
> Fetch the complete documentation index at: https://docs.firstflow.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Production checklist

> What to verify before shipping FirstFlow to production: RLS, key hygiene, migrations, backups, and a quick smoke test.

There is no FirstFlow server to harden, so production readiness is mostly about your Supabase project and key handling. Work through this list before you go live.

## Database

<Steps>
  <Step title="All migrations applied">
    Confirm every file through `0008` has run, in order. A partially-migrated
    project fails silently for example the [intent classifier](/server/overview)
    computes a result but can't persist it if `0007` is missing. See
    [Set up Supabase](/self-hosting/supabase-cloud).
  </Step>

  <Step title="RLS enabled and scoped">
    Verify `rowsecurity` is `true` on every `firstflow_*` table and the policies
    match your auth model. The browser's `anon` key is public RLS is the only
    guard. See [Row Level Security](/self-hosting/oauth-providers).
  </Step>

  <Step title="Backups configured">
    Enable Supabase automated backups (and point-in-time recovery if your plan
    offers it). FirstFlow data lives only in your project.
  </Step>
</Steps>

## Secrets

<Steps>
  <Step title="Service-role key is server-only">
    `SUPABASE_SERVICE_ROLE_KEY` bypasses RLS. It must never carry a
    `NEXT_PUBLIC_` prefix, never appear in client code, and never be committed.
    Only `@firstflow/runtime-server` uses it.
  </Step>

  <Step title="AI keys are server-only">
    `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` / `LLM_API_KEY` live in backend
    environment only.
  </Step>

  <Step title="Rotate anything that leaked">
    If a service-role or AI key was ever pasted into a log, chat, or shared
    file, rotate it in the provider dashboard.
  </Step>
</Steps>

## Identity

<Steps>
  <Step title="Real user ids in production">
    Pass an authenticated `user.id` for signed-in users so caps, resume, and
    persistence attribute correctly. Anonymous fallback is for pre-sign-in
    visitors. See [Identity & auth](/self-hosting/auth-and-orgs).
  </Step>

  <Step title="captureContent is intentional">
    `captureContent: true` stores prompt and completion text in
    `firstflow_conversation_messages`. Enable it deliberately and make sure your
    RLS and retention match your privacy posture.
  </Step>
</Steps>

## Smoke test

After deploying, confirm the full path in production:

1. An experience fires and renders in the widget.
2. `firstflow_flow_runs` gains a row (in-progress → completed/dismissed) and survives a reload (resume works).
3. If you wrap an LLM client, `firstflow_llm_calls` gains a row with tokens and cost.
4. If you use the classifier, `firstflow_conversations.state` / `.intent` updates.

If 1–2 pass but 3–4 don't, re-check the service-role key and that migration `0007` is applied.

## Versioning

Pin the `@firstflow/*` package versions and take upgrades deliberately. When you bump `@firstflow/config-schema`, check for new migrations and apply them before deploying the new runtime.

## Next

Review [what you own](/self-hosting/responsibilities), or set up a local dev loop in [Contributing](/self-hosting/contributing).
