# Staging Environment Runbook Use this runbook to create the first Vercel + Supabase + Cloudflare staging environment before any production casino traffic. ## Purpose Staging should prove the real deployment path without real-money traffic: - Vercel serves the app through `api/index.js`. - Supabase/Postgres stores operators, sessions, seeds, bets, casino transaction audit, and reconciliation rows. - Cloudflare DNS points a staging hostname at Vercel. - A Cloudflare Worker keeps browsable staging pages behind a shared client login while allowing current casino game embeds and public verifier paths. - Casino wallet callbacks remain HTTPS and use the configured operator currency. ## Vercel Environment Create a Vercel environment for previews or a dedicated staging deployment with these values: ```txt APP_ENV=staging NODE_ENV=production STORAGE_DRIVER=postgres DATABASE_URL= DATABASE_POOL_SIZE=10 OPERATOR_SECRET_STAGING_ROUGE= READINESS_OPERATOR_ID=staging-rouge CASINO_CALLBACK_TIMEOUT_MS=3000 CASINO_CALLBACK_RETRIES=1 CRON_SECRET= ``` Use `APP_ENV=staging` for staging because Vercel may set `NODE_ENV=production` for optimized runtime behavior. Real production must use `APP_ENV=production`. Do not use `DEMO_OPERATOR_API_SECRET` for staging. Use an operator-specific secret such as `OPERATOR_SECRET_STAGING_ROUGE`. ## Supabase Staging Data 1. Apply the schema from `db/schema.sql` or the committed migrations. 2. Review `docs/staging-operator-seed-template.sql`. 3. Replace callback URL and theme values if needed. 4. Run the SQL against the staging database. 5. Add the matching Vercel secret for the operator id. The seed template creates: - `staging-rouge` operator - Dice, Limbo, Plinko, Keno, Mines, and Blackjack game config rows - one operator-currency profile with USD limits by default Duplicate the seed with a separate operator id, limits, and `currency: 'uBTC'` when staging uBTC alongside USD. ## Cloudflare DNS Recommended first staging hostname: ```txt staging-games.rougereels.com ``` Add the hostname to the Vercel project first, then create the DNS record Cloudflare shows as required by Vercel. Start DNS-only until Vercel verifies TLS. ## Cloudflare Shared Login Use a Cloudflare Worker for the shared staging login instead of adding staging-only Basic Auth or password checks to the Node app. Cloudflare Access is better for named team identities, but it is not a shared static username/password product. For casino-client handoffs where one temporary login is shared with each new client, keep the password check at the Cloudflare edge and keep the app code unchanged. After TLS is verified and the DNS record is proxied through Cloudflare, route `staging-games.rougereels.com/*` through the Worker source in `infra/cloudflare/staging-shared-login-worker.js`. Cloudflare Worker script name: ```txt rouge-staging-shared-login ``` Store the shared login as Cloudflare Worker secrets: ```txt STAGING_AUTH_USERNAME= STAGING_AUTH_PASSWORD= ``` Do not commit these values to git, docs, tickets, screenshots, or client-facing examples. Use a strong temporary password and rotate it before every new casino-client handoff. Do not use weak demo passwords for client-facing access. The Worker is private-by-default and allows only these public paths: ```txt Protected browsable paths: / /index.html /docs.html /admin.html /sandbox/* /docs/* /health /ready Public verifier embed paths: /verifier.html /verifier.js /styles.css /api/verify Public current-client game integration paths: /game.html when operatorId=bspin-staging and embed=1 /game.js /dice-result-icon.svg /limbo-rocket.svg /api/sessions /api/balance /api/games /api/bets* /api/bet-feed /api/fairness/* /api/mines/* /api/blackjack/* /thumbnails/* ``` Do not protect the `bspin-staging` embedded game path unless the current casino client has confirmed that their iframe launch flow can supply the shared login without breaking browser embedding. A blanket password on all of `staging-games.rougereels.com/*` will break the current staging client because the casino iframe loads `/game.html?...&embed=1` in player browsers. The verifier exception is intentionally narrow: - `verifier.html` renders the iframe page. - `verifier.js` powers the page. - `styles.css` is the shared public stylesheet required by the verifier. - `api/verify` reproduces provably fair results without wallet, session, operator-secret, or settlement access. The current-client game runtime exception is intentionally limited to launch/session/play paths required by `bspin-staging`. The API still validates launch tokens, sessions, idempotency keys, operator config, wallet callbacks, and fairness server-side. ## Smoke Test After Vercel deploys: ```powershell npm.cmd run smoke:deployment -- https://staging-games.rougereels.com ``` Expected: - `/health` returns `status: "ok"`. - `/health` returns `environment: "staging"`. - `/health` returns `storageDriver: "postgres"`. - `/health` returns `currency: "multi"`. - `/health` returns `supportedCurrencies` including `uBTC`. - `/ready?operatorId=staging-rouge` returns `status: "ready"` without exposing secrets. - `/docs/openapi.json` is reachable. After the Cloudflare Worker shared login is enabled, run the same smoke checks with the shared credentials. Unauthenticated checks should confirm that protected browsable paths are blocked while these verifier and current-client integration paths stay public: - `/verifier.html?embed=1` - `/verifier.js` - `/styles.css` - `/api/verify` - `/game.html?operatorId=bspin-staging&game=dice&token=&embed=1` Only use `SMOKE_INCLUDE_SANDBOX=1` if the staging database intentionally includes the local demo operator and sandbox callback path. Real staging operator callbacks should be tested with the casino callback harness instead. ## Promotion Rule Do not copy staging environment variables into production blindly. Production must use its own operator ids, secrets, callback URLs, database connection string, Cloudflare hostname, and go-live approvals.