Brainrush

Multiplayer quiz game: a Flutter app for iOS and Android, a Next.js website where people play in the browser, an Admin panel and the API, plus a background worker. You add your own keys; everything else is ready.

1. Welcome

Thank you for buying Brainrush. This guide is written for beginners: follow the chapters in order and you will have the server, the website and the app running under your own name and keys.

What is in the download

FolderWhat it is
brainrush_mobile_flutter/The mobile app (Flutter, iOS and Android): levels, the daily quiz, live battles, contests, leagues, exams, player-made quizzes, coin store and ads.
brainrush_web_nextjs/One Next.js app that is the website (play in the browser), the Admin panel (/admin), the API the app uses (/api/v1) and the background worker (pnpm worker: battle clock, contest payouts, weekly leagues, daily quizzes, pushes).
deploy/The Docker stack: website + worker + PostgreSQL database + MinIO file storage (+ optional automatic HTTPS), started with one command.
tools/rename.mjsRebrands the whole kit (name, app id, colour, font, icon) in one command.
docs/This documentation (HTML and PDF).

How the parts fit

The mobile app and the website both talk to the same server (the Next.js app). All data lives in your PostgreSQL database: players, questions, games, coins, leagues. Question pictures, sounds and avatars live in S3-compatible storage (MinIO in the Docker stack, or Amazon S3, Cloudflare R2…). Firebase is used only for sign-in and push notifications.

Nothing is blocked on a key. Until you add RevenueCat keys, coin purchases run in demo mode (coins are granted without payment and labelled DEMO). Ads show Google's test ads until you add your AdMob ids. AI and email each show a clear "add your key" state until set.

2. Requirements

To run the server

To build the mobile app

Accounts you will create (all have free tiers)

3. Quick start (Docker)

This gets the whole server running on your VPS in about 15 minutes. Sign-in needs the Firebase keys from chapter 4; you can do this chapter first and add them after.

  1. Copy the kit to the server, for example with scp brainrush-1.0.0.zip root@YOUR_SERVER_IP:, then on the server:
    apt install -y unzip
    unzip brainrush-1.0.0.zip
    cd brainrush/deploy
    cp .env.example .env
    nano .env
  2. In .env, fill at least these values:
    KeyWhat to put
    POSTGRES_PASSWORDA long random password, letters and digits only (run openssl rand -hex 24 to make one).
    S3_SECRET_ACCESS_KEYAnother random value (at least 8 characters) for the built-in file storage.
    APP_URLYour website address, e.g. https://your-domain.com (for a first test: http://YOUR_SERVER_IP:3000).
    NEXT_PUBLIC_FIREBASE_*, FIREBASE_*From chapter 4.
  3. Start everything:
    docker compose up -d
    The first build takes 5–10 minutes. Each start creates or updates the database tables, then loads the badges and coin packs.
  4. Open APP_URL in a browser. The first visit opens the install wizard: your admin account, the app name and the sample quizzes (chapter 7). APP_URL/api/v1/health shows {"ok":true,…}.
  5. Put it on your domain with HTTPS (chapter 5).
Port 3000 already used by something else on the server? Set WEB_PORT in .env.
Changed .env? Run docker compose up -d again. Changed code? Run docker compose up -d --build.

4. Firebase setup

Firebase handles sign-in (guest, Google, Apple, email) and push notifications. Your data stays in your own database.

  1. Go to the Firebase console → Add project. Give it your app name.
  2. Build → Authentication → Get started → Sign-in method. Turn on: Anonymous (guest play), Google, Email/Password (players and staff) and Apple (see chapter 15).
  3. Authentication → Settings → Authorized domains: add your-domain.com.
  4. Project settings (gear) → General → Your apps → Add app → Web (name it "Website"). Copy the values from the firebaseConfig shown into deploy/.env:
    NEXT_PUBLIC_FIREBASE_API_KEY="…apiKey…"
    NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN="your-project.firebaseapp.com"
    NEXT_PUBLIC_FIREBASE_PROJECT_ID="your-project"
    NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET="your-project.firebasestorage.app"
    NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID="…"
    NEXT_PUBLIC_FIREBASE_APP_ID="1:…:web:…"
    The website reads these when it starts, so a restart (docker compose up -d) is enough after a change.
  5. Project settings → Service accounts → Generate new private key. A JSON file downloads. Copy three values from it into deploy/.env:
    FIREBASE_PROJECT_ID="your-project"
    FIREBASE_CLIENT_EMAIL="firebase-adminsdk-xxxx@your-project.iam.gserviceaccount.com"
    FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIE…\n-----END PRIVATE KEY-----\n"
    Keep the \n as they are in the file. Keep this file secret.
  6. Add the mobile apps: Project settings → Your apps → Add app, once for Android and once for iOS, with your app id (for example com.yourcompany.brainrush; see chapter 16 to change it first). Download google-services.json and GoogleService-Info.plist: you only need to copy values out of them (the app does not use the files themselves). Or run flutterfire configure, which does the same and prints the values.
  7. Put the values in brainrush_mobile_flutter/.env:
    Key in the app's .envWhere to find it
    FIREBASE_PROJECT_ID, FIREBASE_MESSAGING_SENDER_ID, FIREBASE_STORAGE_BUCKETProject settings → General (project id, project number, storage bucket)
    FIREBASE_ANDROID_API_KEY, FIREBASE_ANDROID_APP_IDgoogle-services.json: api_key.current_key and mobilesdk_app_id
    FIREBASE_IOS_API_KEY, FIREBASE_IOS_APP_ID, FIREBASE_IOS_BUNDLE_ID, FIREBASE_IOS_CLIENT_IDGoogleService-Info.plist: API_KEY, GOOGLE_APP_ID, BUNDLE_ID, CLIENT_ID
    GOOGLE_SERVER_CLIENT_IDAuthentication → Sign-in method → Google → Web SDK configuration → Web client ID (Google sign-in on Android)
  8. iOS: copy ios/Flutter/Keys.xcconfig.example to ios/Flutter/Keys.xcconfig (the download already has one) and set GOOGLE_REVERSED_CLIENT_ID (the REVERSED_CLIENT_ID from GoogleService-Info.plist) and FIREBASE_ENCODED_APP_ID (your GOOGLE_APP_ID with the colons turned into dashes, e.g. 1:123:ios:abc → app-1-123-ios-abc).
  9. Android Google sign-in needs your signing key fingerprints: run cd brainrush_mobile_flutter/android && ./gradlew signingReport and add the SHA-1 and SHA-256 under Project settings → Your apps → Android → Add fingerprint. Add the fingerprints of your upload key and of Google Play's app signing key too when you publish.

5. Your domain and HTTPS

Easiest: the stack can get a free certificate for you with Caddy. Point your domain's DNS at the server, open ports 80 and 443, then in deploy/.env set DOMAIN="your-domain.com" and APP_URL="https://your-domain.com", and start with the https profile:

docker compose --profile https up -d

Already use Nginx, Traefik or Caddy on the server? Point your-domain.com at port 3000 (WEB_PORT), set APP_URL, run docker compose up -d, and leave the https profile off. Close port 3000 in your firewall once the proxy works (ufw allow 22,80,443/tcp && ufw enable).

Use HTTPS in production: the iOS app only talks to https servers, and Firebase sign-in on the website needs your https domain under Authorized domains.

Backups

cd brainrush/deploy
docker compose exec postgres pg_dump -U brainrush brainrush | gzip > backup-$(date +%F).sql.gz

Run it daily with cron and copy the files off the server. Uploaded pictures and sounds are in the minio-data Docker volume; back it up too, or use a cloud bucket (next chapter).

6. Other hosting

Website on Vercel (or any Node host)

  1. Create a PostgreSQL database (Neon, Supabase, Railway, or your own) and copy its connection string.
  2. Import brainrush_web_nextjs into Vercel (Root directory: brainrush_web_nextjs). Add the keys of brainrush_web_nextjs/.env.example under Settings → Environment Variables, with DATABASE_URL set to your database and the S3_* keys set to a cloud bucket.
  3. Create the tables once from your computer: in brainrush_web_nextjs, put the same DATABASE_URL in .env, then run pnpm install, pnpm prisma:migrate:deploy and pnpm prisma:seed --base. Then open your site: the install wizard does the rest.
  4. The worker (battle clock, contest payouts, leagues, daily quizzes, pushes) runs all the time, so it cannot run on Vercel. Run it on any small server: cd brainrush_web_nextjs && pnpm install && pnpm worker with the same .env (use a process manager such as pm2 or systemd), or with Docker using deploy/docker-compose.yml's worker service.

Cloud storage instead of MinIO

Any S3-compatible bucket works. Set S3_ENDPOINT (empty for Amazon S3; for Cloudflare R2 https://<account>.r2.cloudflarestorage.com), S3_REGION, S3_BUCKET, S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY. Keep the bucket private: files are served through your API. Without any S3_* key (outside Docker), uploads go to a local uploads/ folder.

7. First admin and sample quizzes

The first visit to your site opens the install wizard (/install). It asks for:

Then sign in at https://your-domain.com/admin. Invite more staff from Admin → Roles (roles: super admin, content editor, moderator, finance). The wizard only runs while no super admin exists.

Need a fresh start? docker compose down -v deletes the database and files (everything), then docker compose up -d and open the site again for the wizard.

8. Setup check

Open Admin → Setup check: it lists every key, says which are missing, and tests each connection (database, storage, Firebase sign-in, push, purchases, email, AI, worker) with a clear message. The same check runs in a terminal:

docker compose run --rm migrate pnpm run doctor     # Docker
cd brainrush_web_nextjs && pnpm run doctor          # without Docker

9. Run the mobile app

  1. cd brainrush_mobile_flutter. The download has a .env with empty values (a copy of .env.example). Set:
    API_BASE_URL=https://your-domain.com
    APP_NAME=Brainrush
    FIREBASE_…   (from chapter 4)
  2. Get the packages and run on a phone or emulator:
    flutter pub get
    flutter run
  3. Build for testing: flutter build apk --release (Android) or open ios/Runner.xcworkspace in Xcode (iOS).
Running the server on your own computer? The Android emulator reaches it at http://10.0.2.2:3000 and the iOS simulator at http://localhost:3000. Release builds need an https address.

The app reads every setting from .env: no keys live in Dart code. After changing .env, stop the app and run it again (hot reload does not reload .env). Without the Firebase values the app shows a setup screen that says what is missing.

10. In-app purchases (RevenueCat)

The app sells coin packs (consumables) and "Remove ads" (non-consumable) through the App Store and Google Play, with RevenueCat in between. The server is told about every purchase and adds the coins; the app never decides a balance itself.

  1. Create the products in App Store Connect and Google Play Console with these ids (or your own; the id of each pack is set in Admin → Coin Packs & IAP):
    • Coins (consumable): br.coins.500, br.coins.1200, br.coins.3000, br.coins.6500
    • Remove ads (non-consumable): br.removeads
  2. In RevenueCat: create a project, add your iOS and Android apps and import the products. The app buys products by id, so no offering or entitlement is needed.
  3. App keys: RevenueCat → Project → API keys → the public SDK keys. In brainrush_mobile_flutter/.env: REVENUECAT_APPLE_KEY and REVENUECAT_GOOGLE_KEY.
  4. Server keys in deploy/.env: REVENUECAT_SECRET_KEY (a secret API key) and REVENUECAT_WEBHOOK_AUTH (any long random value).
  5. RevenueCat → Integrations → Webhooks → add https://your-domain.com/api/v1/webhooks/revenuecat with the Authorization header value equal to REVENUECAT_WEBHOOK_AUTH.
Demo mode. Until REVENUECAT_SECRET_KEY is set, purchases are granted without payment and labelled DEMO, so you can try every flow. Setting the key switches to the stores; DEMO_PURCHASES=false forces store mode.

The website does not sell coins: players buy them in the app, and the same balance shows on the website.

11. Push notifications

  1. Android works once Firebase is set up (chapter 4).
  2. iOS: in the Apple Developer site create an APNs key (Keys → +, Apple Push Notifications service), then upload it in Firebase → Project settings → Cloud Messaging → Apple app configuration.
  3. Every app joins the topic brainrush_all, and the server also sends to each player's own devices (battle invites, contest results, streak reminders, league results). Send your own messages from Admin → Notifications, now or scheduled.

12. Ads (Google AdMob)

The app has three ad types: rewarded (watch an ad to earn coins), interstitial (between games) and banner. Players who bought "Remove ads" see no interstitials or banners.

  1. In AdMob, add your Android and iOS apps and create one ad unit of each type you want.
  2. In brainrush_mobile_flutter/.env set ADMOB_ANDROID_APP_ID and the unit ids ADMOB_ANDROID_REWARDED_ID, ADMOB_ANDROID_INTERSTITIAL_ID, ADMOB_ANDROID_BANNER_ID, ADMOB_IOS_REWARDED_ID, ADMOB_IOS_INTERSTITIAL_ID, ADMOB_IOS_BANNER_ID.
  3. The iOS app id goes in ios/Flutter/Keys.xcconfig: ADMOB_IOS_APP_ID=ca-app-pub-…~….
  4. Control ads in Admin → Ads: on or off, coins per rewarded ad, rewarded ads per day, an interstitial every N games, banners on or off.

Without your own ids the app shows Google's test ads, so the flows work while you build. Other networks join through AdMob mediation, set up in your AdMob account.

13. AI question generator

Admin → AI Generator writes new questions for any topic, level and question type; you review them before they go live. Set one of ANTHROPIC_API_KEY, OPENAI_API_KEY or GEMINI_API_KEY in deploy/.env, or paste a key in Admin → Settings → AI. Without a key the page says "add your key", and you can still write questions by hand or import a CSV.

14. Email

Staff invites and password resets are sent by email. Set SMTP_HOST, SMTP_PORT (587, or 465 with SMTP_SECURE=true), SMTP_USER, SMTP_PASS from any provider (Amazon SES, Postmark, Mailgun, Brevo, your host). Without SMTP_HOST, emails are written to the server log instead (docker compose logs web), which is handy while testing. Player password resets are sent by Firebase.

15. Sign-in methods

Leave DEMO_MODE empty on your real site. DEMO_MODE=true is for a public demo only: it adds one-click demo sign-in buttons and resets the data every night.

16. Rebrand: name, app id, colour

One command changes the app name everywhere (app, website, admin, emails, push topic), the Android application id and iOS bundle id, the brand colour (with its lighter and darker shades), the website font and the icons:

cd brainrush
node tools/rename.mjs --name "QuizNova" --id com.mycompany.quiznova --color "#2563EB" --font "DM Sans" --icon my-icon-1024.png

Every option is optional, so you can run it again later for one change. Add --dry to see what would change. Afterwards:

cd brainrush_mobile_flutter
flutter pub get
dart run flutter_launcher_icons
dart run flutter_native_splash:create

then add the Firebase apps for the new app id (chapter 4) and rebuild the website (docker compose up -d --build). The folder names (brainrush_mobile_flutter and so on) stay as they are; people never see them. The app name shown inside the running app and website comes from Admin → Settings → App name too.

By hand: the app's colours are in brainrush_mobile_flutter/lib/theme/br_colors.dart (a light and a dark set) and the website's in brainrush_web_nextjs/src/app/globals.css (--br-primary and friends).

18. Basic edits

I want to change…Where
Coins per right answer, level rewards, lifeline prices, streak freeze price, welcome and referral coins, questions per level, seconds per question, stars rulesAdmin → Coin Economy
Coin packs, prices shown, store product idsAdmin → Coin Packs & IAP
Categories, subcategories, premium (coin-locked) categoriesAdmin → Categories
QuestionsAdmin → Questions (editor, CSV import), Admin → AI Generator
Today's and upcoming daily quizzesAdmin → Daily Quiz (the worker fills the next 7 days on its own)
Contests, entry fees and prize splitAdmin → Contests
Exams for a class or a companyAdmin → Exams
Battle rules (entry fees, bot wait, room size)Admin → Battles
League tiers, players per league, how many move up and down, board rewardsAdmin → Leagues
BadgesAdmin → Badges
Fun & Learn articles and their quizzesAdmin → Fun & Learn
App name, support email, store links, FAQ, guest play, minimum app version (force update), maintenance modeAdmin → Settings
Terms and privacy pagesAdmin → Settings (or point the links at your own pages)
App textsThe page files in brainrush_mobile_flutter/lib/pages/
Website textsThe page files in brainrush_web_nextjs/src/app/(site)/ and src/components/site/

19. Adding questions

Each question belongs to a subcategory and a level. A level is played with questions per level picked at random from its pool, so put more questions in a level than are played (for example 15–20 for 10 played) for variety.

20. Every key, explained

"Where" says which .env file it goes in: Docker = deploy/.env (website and worker in the Docker stack), website = brainrush_web_nextjs/.env (without Docker), app = brainrush_mobile_flutter/.env. Admin → Setup check shows the same list with the live status of each key.

KeyRequiredWhereWhat it does
Database
DATABASE_URLYeswebsitePostgreSQL connection string
POSTGRES_DBYesDockerDatabase the Docker stack creates
POSTGRES_USERYesDockerDatabase user for the Docker stack
POSTGRES_PASSWORDYesDockerDatabase password for the Docker stack
App
APP_URLYeswebsite, DockerPublic URL of the website/API, e.g. https://quiz.example.com
NEXT_PUBLIC_APP_NAME—website, DockerName shown before the admin sets one
WEB_PORT—DockerHost port the web container listens on
DOMAIN—DockerYour domain for automatic HTTPS (docker compose --profile https)
CORS_ORIGINS—website, DockerBrowser apps allowed to call /api/v1 (Flutter web builds)
DEMO_MODE—website, Docker"true" only for a public demo: one-click demo sign-in
Firebase sign-in
NEXT_PUBLIC_FIREBASE_API_KEYYeswebsite, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_AUTH_DOMAINYeswebsite, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_PROJECT_IDYeswebsite, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET—website, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID—website, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_APP_IDYeswebsite, DockerFirebase web app config
Firebase Admin + push
FIREBASE_PROJECT_IDYeswebsite, DockerFirebase project id
FIREBASE_CLIENT_EMAILYeswebsite, DockerService account e-mail
FIREBASE_PRIVATE_KEYYeswebsite, DockerService account private key
In-app purchases
REVENUECAT_SECRET_KEY—website, DockerRevenueCat secret API key — verifies store purchases (empty = demo purchases)
REVENUECAT_WEBHOOK_AUTH—website, DockerAuthorization header RevenueCat sends to /api/v1/webhooks/revenuecat
DEMO_PURCHASES—website, Docker"false" hides demo purchases when RevenueCat is not set
AI question generator
ANTHROPIC_API_KEY—website, DockerClaude API key (or set it in Admin → Settings → AI)
OPENAI_API_KEY—website, DockerOpenAI API key
GEMINI_API_KEY—website, DockerGoogle Gemini API key
Storage
S3_ENDPOINT—website, DockerS3-compatible endpoint (MinIO, R2); empty for AWS
S3_REGION—website, DockerBucket region
S3_BUCKET—website, DockerBucket for photos and documents (empty = local uploads folder)
S3_ACCESS_KEY_ID—website, DockerStorage access key
S3_SECRET_ACCESS_KEY—website, DockerStorage secret key
Email
SMTP_HOST—website, DockerSMTP server; empty = emails are printed to the log
SMTP_PORT—website, Docker587 (STARTTLS) or 465 (TLS)
SMTP_USER—website, DockerSMTP user
SMTP_PASS—website, DockerSMTP password
SMTP_SECURE—website, Docker"true" for port 465 (TLS); empty for 587
Storage
UPLOAD_DIR—websiteFolder for uploads when no bucket is set (default ./uploads)
Mobile apps
API_BASE_URLYesappYour server; the app calls <url>/api/v1
APP_NAME—appApp name in the UI
FIREBASE_PROJECT_IDYesappFirebase project id
FIREBASE_MESSAGING_SENDER_IDYesappFirebase config
FIREBASE_STORAGE_BUCKET—appFirebase config
FIREBASE_ANDROID_API_KEYYesappFirebase Android config
FIREBASE_ANDROID_APP_IDYesappFirebase Android config
FIREBASE_IOS_API_KEYYesappFirebase iOS config
FIREBASE_IOS_APP_IDYesappFirebase iOS config
FIREBASE_IOS_CLIENT_ID—appGoogle sign-in on iOS
FIREBASE_IOS_BUNDLE_ID—appiOS bundle id
GOOGLE_SERVER_CLIENT_ID—appWeb OAuth client id — Google sign-in on Android
SUPPORT_EMAIL—appFallback support e-mail until the server config loads
REVENUECAT_APPLE_KEY—appRevenueCat public iOS key (empty = demo purchases)
REVENUECAT_GOOGLE_KEY—appRevenueCat public Android key (empty = demo purchases)
ADMOB_ANDROID_APP_ID—appAdMob Android app id (empty = Google test ads)
ADMOB_ANDROID_REWARDED_ID—appAdMob Android rewarded ad unit id (empty = Google test ads)
ADMOB_IOS_REWARDED_ID—appAdMob iOS rewarded ad unit id (empty = Google test ads)
ADMOB_ANDROID_INTERSTITIAL_ID—appAdMob Android interstitial ad unit id (empty = Google test ads)
ADMOB_IOS_INTERSTITIAL_ID—appAdMob iOS interstitial ad unit id (empty = Google test ads)
ADMOB_ANDROID_BANNER_ID—appAdMob Android banner ad unit id (empty = Google test ads)
ADMOB_IOS_BANNER_ID—appAdMob iOS banner ad unit id (empty = Google test ads)
DEMO_SIGN_IN—app"true" shows one-tap demo sign-in (your public demo only)

21. File structure

Mobile app (brainrush_mobile_flutter/lib)

main.dart            loads .env, starts Firebase, runs the app (short on purpose)
app.dart             theme, router and app-wide listeners
router.dart          every route (go_router) and the sign-in / setup gates
config/              .env values (app_env.dart) and Firebase options
api/                 the typed client for your server (/api/v1)
models/              data models parsed from the API
providers/           app-wide state (Riverpod): session, config, wallet, home…
services/            push, purchases (RevenueCat or demo), ads (AdMob), sounds, sign-in
theme/               colours (br_colors.dart), fonts and text styles
components/          shared widgets, names start with cc_ (buttons, cards, sheets, avatars…)
pages/               one folder per area: launch, auth, home, play, quiz, results, battle, compete,
                     leagues, exam, learn, create, store, profile, settings…
utils/               formatting and small helpers

Lists and grids use the lazy .builder constructors, and state flows through Riverpod providers rather than long chains of widget parameters.

Server (brainrush_web_nextjs/src)

app/(site)/               the public website (landing, play, leaderboard, contests, shared quizzes…)
app/(game)/quiz/          the full-screen quiz player
app/admin/                Admin panel pages
app/install/              the first-run wizard
app/api/v1/[...path]/     the single API entry point; routes live in lib/server/handlers/
components/site|play|panel|ui   website, quiz player, admin and base UI (shadcn) components
lib/server/handlers/      every API route by area: play, compete, social, me, admin…
lib/server/               games and scoring, battles, contests, leagues, exams, economy, purchases,
                          storage, email, AI, push, settings, setup check
database/prisma_client/   database schema (schema.prisma) and migrations
database/seed/            the sample quizzes, badges and coin packs
worker/                   background jobs (pnpm worker)
scripts/doctor.ts         the setup check in a terminal
tests/                    API tests (Vitest); e2e/ has browser smoke tests (Playwright)

22. Publish to the stores

You publish the app under your own developer accounts. Before you start: set your app id (chapter 16), your Firebase values (chapter 4) and an https API_BASE_URL. The stores ask for a privacy policy link: https://your-domain.com/privacy; terms are at /terms. Players delete their account in the app (Profile → Settings → Account → Delete account).

Google Play

  1. Create an upload key inside android/app: cd brainrush_mobile_flutter/android/app && keytool -genkey -v -keystore upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload. Back this file up and never share it.
  2. Create android/key.properties:
    storePassword=…
    keyPassword=…
    keyAlias=upload
    storeFile=upload-keystore.jks
    Without this file, release builds are signed with the debug key (fine for testing, refused by Play).
  3. Set the version in pubspec.yaml (version: 1.0.0+1; raise the number after + for every upload), then flutter build appbundle.
  4. In Play Console: create the app, fill the store listing, content rating, data safety (the app collects account info, game progress, purchase history, device ids for ads and push) and upload build/app/outputs/bundle/release/app-release.aab to a testing track first.
  5. Add the SHA-1/SHA-256 of Play's app signing key (Play Console → Test and release → App integrity) to Firebase.

App Store

  1. In the Apple Developer site, register your bundle id with Push Notifications, Sign in with Apple and In-App Purchase.
  2. Open ios/Runner.xcworkspace in Xcode → Runner → Signing & Capabilities → choose your team.
  3. In App Store Connect create the app, fill the listing and App Privacy, and create the in-app purchases (chapter 10).
  4. flutter build ipa, then upload build/ios/ipa/*.ipa with the Transporter app, and send it to TestFlight first.
Store rules to keep in mind: coins sold inside the app must use in-app purchases, which the app does. Apps with sign-in must offer account deletion (included) and, on iOS, Sign in with Apple when Google sign-in is offered (included). Coins have no cash value: keep contest prizes in coins.

23. Updating

When a new version comes out, read the changelog, back up your database (chapter 5), then copy the new files over your copy, keeping your .env files and any changes you made. Run docker compose up -d --build: database changes are applied automatically on start. Using git for your copy makes this much easier: commit before you copy the update in, and review the differences.

24. FAQ and troubleshooting

The site opens the install wizard again

The wizard shows only while no super admin exists. If you see it on a site you already set up, the site is connected to an empty database: check DATABASE_URL / the POSTGRES_* values.

Sign-in fails on the website

Add your domain under Firebase → Authentication → Settings → Authorized domains, check the NEXT_PUBLIC_FIREBASE_* values, and restart (docker compose up -d).

Google sign-in fails on Android

Add the SHA-1 and SHA-256 of the key that signed the build to Firebase, and check GOOGLE_SERVER_CLIENT_ID is the Web client id.

Battles never find an opponent

Matchmaking and the battle clock run in the worker. Check it runs: docker compose logs worker; Admin → Setup check shows when it last answered. With few players, a bot joins after the wait set in Admin → Battles.

Purchases say DEMO

No RevenueCat key is set yet. That is on purpose: add your keys (chapter 10).

Push notifications do not arrive

iOS needs the APNs key in Firebase, and the phone must allow notifications. Admin → Setup check tests the Firebase push connection.

The app shows "Can't reach the server"

API_BASE_URL in the app's .env must be your site's address (https, no /api/v1 at the end). Open https://your-domain.com/api/v1/health in the phone's browser.

Images or sounds in questions do not load

Check the S3_* keys and Admin → Setup check → File storage. With a cloud bucket, the server needs read and write access to it.

I changed .env and nothing happened

Server: docker compose up -d. App: stop it and run it again.

Where are the logs?

docker compose logs -f web and docker compose logs -f worker.

25. Credits

Brainrush is built on these open-source projects. Each keeps its own licence (mostly MIT, BSD-3 or Apache 2.0).

Mobile app

Flutter, flutter_riverpod, go_router, FlutterFire (firebase_core, firebase_auth, firebase_messaging), google_sign_in, sign_in_with_apple, purchases_flutter (RevenueCat), google_mobile_ads, audioplayers, flutter_dotenv, shared_preferences, http, intl, lucide_icons_flutter, share_plus, url_launcher, package_info_plus, image_picker, qr_flutter, connectivity_plus, flutter_launcher_icons, flutter_native_splash.

Website, Admin, API and worker

Next.js, React, Prisma, PostgreSQL, pg, Zod, Tailwind CSS, shadcn/ui, Base UI, Lucide icons, Sonner, next-themes, qrcode, Nodemailer, Firebase JS SDK, Firebase Admin SDK, AWS SDK for JavaScript, MinIO, Caddy, Docker.

Fonts

Fredoka and Nunito, SIL Open Font License 1.1.

Content

The sample questions, explanations, articles, flag pictures, audio clips, sound effects, category names and player names are original and part of your licence.

26. Changelog

1.0.0 — 2026-10-07

First release. See CHANGELOG.md in the download for the full list.

27. Support

Questions or a problem? Use the Support tab on the item page on CodeCanyon, with your purchase code, what you did, and what you saw (a screenshot and the output of pnpm run doctor help a lot). Support covers questions about the item, help with bugs, and help with the third-party services it uses as far as this documentation goes. It does not cover changes or new features you want to build; for that, or to have it installed for you, ask about our installation service on the item page.