FluxyChat

Operations

Environment setup -- local, staging, production

Step-by-step configuration before first deploy. Run code checks first (`pnpm run check:env`).

Quick start (local only, ~10 min)

pnpm install
pnpm run dev:setup          # copies .dev.vars + .env.local from examples (won't overwrite)
pnpm dev                    # worker :8787 + dashboard :3000

Open http://localhost:3000/onboarding → paste admin JWT (from Worker POST /auth/token with a project API key) → create room → message.

Worker: edit apps/worker/.dev.vars (from example). Minimum:

VarValue
ALLOWED_ORIGINShttp://localhost:3000,http://127.0.0.1:3000
REQUIRE_ADMIN_AUTHtrue
ALLOW_PLAINTEXT_WEBHOOK_SECRETStrue (local only)

Dashboard: edit apps/dashboard/.env.local:

VarValue
NEXT_PUBLIC_FLUXYCHAT_WORKER_URLhttp://127.0.0.1:8787

Leave Clerk keys empty for manual JWT onboarding.


Architecture (what talks to what)

Browser → Dashboard (Vercel) → Worker API (Cloudflare)
                ↓                    ↓
           Clerk (optional)      D1 + DO + KV + R2
Host (example)AppEnv file / config
app.fluxychat.comDashboardVercel env vars
api.fluxychat.comWorkerwrangler.toml + secrets
status.fluxychat.comDashboard /statusSame Vercel project, optional subdomain

Phase 1 -- Cloudflare Worker (staging or production)

1.1 Create resources (staging -- separate from prod)

Run from apps/worker:

# D1
pnpm exec wrangler d1 create fluxychat-staging
# → paste database_id into wrangler.staging.toml

# KV (rate limits)
pnpm exec wrangler kv namespace create RATE_LIMIT_KV
pnpm exec wrangler kv namespace create RATE_LIMIT_KV --preview
# → paste id + preview_id into wrangler.staging.toml

# R2
pnpm exec wrangler r2 bucket create fluxychat-attachments-staging

Production uses names in wrangler.toml (fluxychat, fluxychat-attachments) -- create if not already done.

1.2 Migrations

cd apps/worker
# Staging
pnpm exec wrangler d1 migrations apply fluxychat-staging --remote --config wrangler.staging.toml
# Production
pnpm exec wrangler d1 migrations apply fluxychat --remote

1.3 Worker vars (wrangler.toml / wrangler.staging.toml [vars])

VarStagingProduction
WORKFLOW_SCHEDULES_ENABLEDfalsefalse (unless using Workflows)
BROWSER_ENABLEDfalsefalse until you need SPA OG previews
ALLOWED_ORIGINShttps://staging-app.example.com,http://localhost:3000https://app.fluxychat.com,https://fluxychat.com

1.4 Worker secrets (wrangler secret put NAME)

Required for hosted multi-tenant:

SecretHow to get
PLATFORM_BOOTSTRAP_SECRETopenssl rand -base64 32 -- one-time bootstrap only
WEBHOOK_SECRET_ENCRYPTION_KEYopenssl rand -base64 32 -- 32 bytes base64

After bootstrap (see Phase 2):

SecretNotes
--Per-project jwt_secret lives in D1 project_secrets (bootstrap creates platform project)

Optional by feature:

SecretFeature
STRIPE_SECRET_KEYBilling checkout
STRIPE_WEBHOOK_SECRETStripe webhooks
AI_GATEWAY_TOKEN + gateway varsAI agents
AI_API_KEYLegacy AI fallback
TURNSTILE_SECRET_KEYPublic /demo
CLERK_WEBHOOK N/A on workerClerk is dashboard-side

Set hosted mode on Worker:

pnpm exec wrangler secret put HOSTED_MULTI_TENANT
# enter: true

pnpm exec wrangler secret put FLUXY_PLATFORM_PROJECT_ID
# enter: <uuid from provision bootstrap>

Or use [vars] for non-secret flags and secrets for keys.

1.5 Deploy

# Staging
pnpm exec wrangler deploy src/worker.js --config wrangler.staging.toml

# Production
pnpm --filter @fluxy-chat/worker deploy

1.6 Custom domain (API)

Cloudflare dashboard → Worker → Custom domains → add api.fluxychat.com (or staging subdomain).
wrangler.toml already has [[routes]] for production pattern.


Phase 2 -- Platform bootstrap (hosted tenants)

One-time per Worker deployment:

  1. Set on Worker: ALLOW_PLATFORM_BOOTSTRAP=true, PLATFORM_BOOTSTRAP_SECRET=<random>.
  2. From repo root:
PLATFORM_BOOTSTRAP_SECRET=<same> \
FLUXY_WORKER_URL=https://api.fluxychat.com \
pnpm provision:bootstrap
  1. Read scripts/.provision-secrets.env (gitignored) -- contains FLUXY_CONSOLE_API_KEY and project UUID.
  2. Copy to dashboard .env.local / Vercel:
FLUXY_CONSOLE_API_KEY=fc_...
FLUXY_CONSOLE_PROJECT_ID=<uuid>
FLUXY_PLATFORM_PROJECT_ID=<uuid>
NEXT_PUBLIC_FLUXYCHAT_CLOUD_URL=https://api.fluxychat.com
  1. Copy to Worker secret: FLUXY_PLATFORM_PROJECT_ID=<uuid>, set HOSTED_MULTI_TENANT=true.
  2. Disable bootstrap: remove ALLOW_PLATFORM_BOOTSTRAP or set false.

Phase 3 -- Dashboard (Vercel or self-host)

3.1 Environment variables

See apps/dashboard/.env.example, .env.staging.example, .env.production.example.

VarRequiredPurpose
NEXT_PUBLIC_FLUXYCHAT_CLOUD_URLHostedWorker API URL (wins over WORKER_URL)
NEXT_PUBLIC_SITE_URLProdCanonical / OG (https://fluxychat.com)
FLUXY_CONSOLE_API_KEYHostedServer-only; provisions tenants
FLUXY_PLATFORM_PROJECT_IDHostedMust match Worker
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYOptionalHosted sign-in
CLERK_SECRET_KEYOptionalClerk server
CLERK_WEBHOOK_SIGNING_SECRETOptionalAuto-provision on sign-up
DASHBOARD_PUBLIC_URLOptionalClerk webhook URL base

Do not expose FLUXY_CONSOLE_API_KEY to the browser -- only server routes use it.

3.2 Vercel project

  1. Import repo → root apps/dashboard or monorepo with filter.
  2. Build: pnpm --filter @fluxy-chat/dashboard build (or Turbo from root).
  3. Env vars: paste from .env.production.example checklist.
  4. Domains: app.fluxychat.com, optional status.fluxychat.com → same project (/status route exists).

3.3 Clerk (optional)

Clerk Dashboard → Application → API Keys → paste into Vercel.
Paths: Sign-in /sign-in, Sign-up /sign-up, after sign-up redirect /onboarding.


Phase 4 -- Stripe (optional, billing)

  1. Stripe Dashboard → Developers → API keys → STRIPE_SECRET_KEY on Worker.
  2. Webhook endpoint: https://api.fluxychat.com/webhooks/stripeSTRIPE_WEBHOOK_SECRET.
  3. Without Stripe: /health shows paymentsEnabled: false; users stay on Free tier.

Phase 5 -- GitHub Actions (CI deploy)

Repository → Settings → Secrets and variables → Actions:

SecretPurpose
CLOUDFLARE_API_TOKENWrangler deploy (Workers + D1)
CLOUDFLARE_ACCOUNT_IDAccount ID
E2E_ADMIN_JWTOptional integrated Playwright

Create environments staging and production in GitHub → wire approval on production.

Manual deploy: Actions → Deploy → choose environment.


Phase 6 -- Smoke verification

# Worker
pnpm --filter @fluxy-chat/worker smoke:remote -- \
  --base-url https://api.fluxychat.com \
  --admin-jwt "<JWT>"

# Dashboard CI smoke (no worker)
cd apps/dashboard && CI=true pnpm test:e2e:smoke

# Full integrated (local worker + dashboard)
E2E_ADMIN_JWT=<jwt> pnpm --filter @fluxy-chat/dashboard test:e2e:integrated

Browser checks:

  • https://<dashboard>/status — public health
  • https://<dashboard>/onboarding — wizard
  • https://<dashboard>/billing — load plan

Configuration matrix (minimum vs full)

CapabilityLocal devStagingProduction
Chat / roomsWorker + .dev.varsWorker staging + D1Worker prod
Dashboard.env.local localhostVercel preview envVercel prod
Multi-tenantOptional bootstrapBootstrap + HOSTED_MULTI_TENANTSame
Clerk sign-inSkip (manual JWT)Test Clerk appLive Clerk
StripeOffTest mode keysLive keys
AI agentsAI_API_KEY optionalGateway or API keyGateway recommended
Browser OGBROWSER_ENABLED=true locallyOffOff until needed
Status page/status/status+ DNS status.*

Troubleshooting

SymptomCheck
CORS errorsALLOWED_ORIGINS includes dashboard origin
Connect forbiddenFLUXY_PLATFORM_PROJECT_ID matches API key project
401 on hosted routesHOSTED_MULTI_TENANT=true + valid API key/JWT
Billing 501STRIPE_SECRET_KEY not set (expected in beta)
/status unreachableNEXT_PUBLIC_FLUXYCHAT_CLOUD_URL or WORKER_URL
Staging deploy failsReplace REPLACE_WITH_* in wrangler.staging.toml

See also RUNBOOK_DEPLOY_ROLLBACK.md, open-beta-deploy-guide.

On this page