Deployment Guide โ Lantern App โ
Complete guide for deploying the Lantern app to development and production environments using Cloudflare Pages and Firebase.
Table of Contents โ
- Overview
- Prerequisites
- Environment Setup
- Local Development
- Cloudflare Pages Setup
- Firebase Projects Setup
- Deployment Workflow
- Troubleshooting
Overview โ
Architecture โ
- Frontend: Vite + React PWA hosted on Cloudflare Pages
- Backend: Firebase (Authentication, Firestore, Storage, Functions)
- Hosting: Cloudflare Pages with automatic deployments
- Documentation: VitePress site deployed to separate subdomain
Note: The documentation site is deployed separately. See DOCS_DEPLOYMENT.md for docs-specific deployment guide.
Environments โ
| Component | Environment | URL | Pages Project | Use Case |
|---|---|---|---|---|
| App | Production | ourlantern.app | lantern-app | Live users |
| App | Development | dev.ourlantern.app | lantern-app-dev | Team testing |
| Docs | Production | docs.ourlantern.app | lantern-docs | Public documentation |
| Docs | Development | docs.dev.ourlantern.app | lantern-docs-dev | Unreleased docs |
| Storybook | Production | storybook.ourlantern.app | lantern-storybook | Component library |
| Storybook | Development | storybook.dev.ourlantern.app | lantern-storybook-dev | Dev component library |
Security Headers Configuration โ
Permissions Policy for Geolocation โ
IMPORTANT: The public/_headers file includes a Permissions Policy that controls browser feature access.
Permissions-Policy: camera=(), microphone=(), geolocation=(self)What this does:
camera=()- Blocks camera access (Lantern has no photo features)microphone=()- Blocks microphone access (not needed)geolocation=(self)- Allows geolocation for same-origin requests (required for Waves, check-ins, proximity features)
Why geolocation is enabled:
- Core features (Waves, Lantern Hub, venue check-ins) require location access
- Location is used ephemerally (proximity checks) and NOT retained as history
(self)scope prevents third-party scripts/iframes from accessing location- User must explicitly grant browser permission for each session
Security model:
- User clicks "Allow Location" in browser prompt
- App gets GPS coordinates from browser
- Server validates location (see Location Security in TODO.md)
- Location used for feature (proximity match, check-in verification)
- Location discarded - NOT stored in database
Testing:
- Without
geolocation=(self): Browser blocks with "Permissions policy violation" - With
geolocation=(self): Browser shows permission prompt, feature works
References:
- See
docs/TODO.mdโ "Location Security & Anti-Fraud" for server-side validation requirements - See
src/screens/profile/ProfileSettings.jsxfor location spoofing debug panel (dev only)
Prerequisites โ
Required Accounts โ
- [X] Google/Firebase account
- [X] Cloudflare account
- [X] GitHub account (repo already exists)
Required Tools โ
# Node.js (v18 or v20)
node --version
# npm (comes with Node)
npm --version
# Git
git --version
# Optional: Cloudflare CLI (wrangler)
npm install -g wranglerEnvironment Setup โ
Step 1: Create Firebase Projects โ
You need TWO separate Firebase projects:
Development Project โ
- Go to Firebase Console
- Click "Add project"
- Name:
lantern-app-dev - Enable Google Analytics (optional)
- Click "Create project"
Production Project โ
- Repeat steps above
- Name:
lantern-app-prod
Step 2: Get Firebase Configuration โ
For EACH Firebase project (lantern-app-dev and lantern-app-prod):
- Go to Project Settings (gear icon)
- Scroll to "Your apps" section
- Click "Web" icon (</>) to add a web app
- Register app name (e.g., "Lantern Dev" or "Lantern Prod")
- Copy the config object that looks like:
const firebaseConfig = {
apiKey: "AIzaSy...",
authDomain: "lantern-app-dev.firebaseapp.com",
projectId: "lantern-app-dev",
storageBucket: "lantern-app-dev.appspot.com",
messagingSenderId: "123456789",
appId: "1:123456789:web:abcdef"
};โ ๏ธ IMPORTANT: Keep these configs safe! You'll need them in the next steps.
Step 3: Enable Firebase Services โ
In EACH Firebase project, enable:
Authentication
- Go to Authentication > Get started
- Enable "Email/Password" provider
- (Later: Add Google, Facebook, etc. as needed)
Firestore Database
- Go to Firestore Database > Create database
- Start in "test mode" (we'll add security rules later)
- Choose a location (e.g.,
us-central1)
Storage
- Go to Storage > Get started
- Start in "test mode"
Functions (optional for now)
- Will set up when needed
Local Development โ
Step 1: Clone & Install โ
# Clone repo (if not already)
git clone https://github.com/cattreedev/lantern_app.git
cd lantern_app
# Install dependencies
npm installStep 2: Set Up Local Environment โ
# Copy the example file
cp .env.local.example .env.localEdit .env.local with your DEV Firebase config:
VITE_FIREBASE_API_KEY=AIzaSy... # from lantern-app-dev
VITE_FIREBASE_AUTH_DOMAIN=lantern-app-dev.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=lantern-app-dev
VITE_FIREBASE_STORAGE_BUCKET=lantern-app-dev.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=123456789
VITE_FIREBASE_APP_ID=1:123456789:web:abcdef
VITE_APP_ENV=localโ ๏ธ NEVER commit .env.local โ it's already in .gitignore
Step 3: Run Development Server โ
npm run devVisit http://localhost:5173 โ you should see the app running with dev Firebase!
Step 4: Verify Firebase Connection โ
Open browser console and look for:
๐ฅ Firebase initialized (local environment)
Project: lantern-app-devCloudflare Pages Setup โ
Architecture: Two Separate Projects โ
We use two independent Cloudflare Pages projects to avoid auth conflicts and keep environments clean:
| Project | Branch | Domain | Purpose |
|---|---|---|---|
lantern-app | main | ourlantern.app | Production (live) |
lantern-app-dev | dev | dev.ourlantern.app | Development/staging |
Benefits:
- No proxy conflicts
- Clean separation of environments
- Independent environment variables and settings
- Easy to manage access policies
Step 1: Create Production Pages Project (lantern-app) โ
- Go to Cloudflare Dashboard โ Pages
- Click "Create a project" โ "Connect to Git"
- Select
cattreedev/lantern_app - Configure:
- Project name:
lantern-app - Production branch:
main - Build command:
npm run build - Build output directory:
dist
- Project name:
Step 2: Create Development Pages Project (lantern-app-dev) โ
- Click "Create a project" again
- Select same repo
cattreedev/lantern_app - Configure:
- Project name:
lantern-app-dev - Production branch:
dev(not main!) - Build command:
npm run build - Build output directory:
dist
- Project name:
Step 3: Set Environment Variables (Production Project) โ
In lantern-app project, go to Settings โ Environment variables:
Production (main branch):
VITE_FIREBASE_API_KEY = <your-prod-api-key>
VITE_FIREBASE_AUTH_DOMAIN = lantern-app-prod.firebaseapp.com
VITE_FIREBASE_PROJECT_ID = lantern-app-prod
VITE_FIREBASE_STORAGE_BUCKET = lantern-app-prod.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID = <your-prod-sender-id>
VITE_FIREBASE_APP_ID = <your-prod-app-id>
VITE_APP_ENV = production
NODE_VERSION = 20Step 4: Set Environment Variables (Development Project) โ
In lantern-app-dev project, go to Settings โ Environment variables:
Production (dev branch):
VITE_FIREBASE_API_KEY = <your-dev-api-key>
VITE_FIREBASE_AUTH_DOMAIN = lantern-app-dev.firebaseapp.com
VITE_FIREBASE_PROJECT_ID = lantern-app-dev
VITE_FIREBASE_STORAGE_BUCKET = lantern-app-dev.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID = <your-dev-sender-id>
VITE_FIREBASE_APP_ID = <your-dev-app-id>
VITE_APP_ENV = development
NODE_VERSION = 20Step 5: Add Custom Domains โ
Production Project โ
- In
lantern-app, go to Settings โ Domains & accounts - Click Add domain
- Enter
ourlantern.app - Cloudflare auto-creates required DNS records
Development Project โ
- In
lantern-app-dev, go to Settings โ Domains & accounts - Click Add domain
- Enter
dev.ourlantern.app - Cloudflare auto-creates required DNS records
Step 6: Verify DNS Configuration โ
In Cloudflare dashboard, DNS tab should show:
CNAME ourlantern.app โ lantern-app.pages.dev (Proxied)
CNAME dev โ lantern-app.pages.dev (Proxied)Clean up old records (delete if present):
- Namecheap parking A record (162.255.119.80)
- Parking CNAME (parkingpage.namecheap.com)
- Old nameservers (pdns1/pdns2.registrar-servers.com)
- Keep or remove MX/SPF based on your email setup
Step 7: Configure Cloudflare Access (Security) โ
We use three Access applications for defense-in-depth:
1. Production Access App โ
- Go to Cloudflare Zero Trust โ Access โ Applications
- Click Add an application โ Self-hosted
- Configure:
- Application name:
lantern-app - Subdomain/Domain:
ourlantern.app - Path:
/
- Application name:
- Click Next and add policy:
- Action: Allow
- Rule name: PIN Authentication
- Session duration: 24 hours
- Include rule: Everyone (or restrict to emails)
- Require: One-time PIN (or email authentication)
- Save
2. Development Access App โ
- Click Add an application โ Self-hosted
- Configure:
- Application name:
lantern-app-dev - Subdomain/Domain:
dev.ourlantern.app - Path:
/
- Application name:
- Add same policy as production (or customize as needed)
3. Wildcard Access App (Catch-all for future subdomains) โ
- Click Add an application โ Self-hosted
- Configure:
- Application name:
lantern-app-wildcard - Subdomain/Domain:
*.ourlantern.app - Path:
/
- Application name:
- Add same PIN policy
- This catches any accidental subdomains (e.g.,
api.ourlantern.app)
Result:
- โ
ourlantern.appprotected by prod app - โ
dev.ourlantern.appprotected by dev app - โ
Any future
*.ourlantern.appcaught by wildcard app - โ No conflicts (Cloudflare matches most-specific first)
Firebase Projects Setup โ
Security Rules (IMPORTANT!) โ
By default, Firebase is in "test mode" โ anyone can read/write. Lock it down:
Firestore Rules โ
In Firebase Console > Firestore Database > Rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Default deny
match /{document=**} {
allow read, write: if false;
}
// Users can read/write their own data
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
// Add more rules as needed
}
}Storage Rules โ
In Firebase Console > Storage > Rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}Publish these rules in BOTH projects.
Deployment Workflow โ
Feature Branch Workflow (Recommended) โ
# 1. Create feature branch from dev
git checkout -b feature/my-feature dev
# 2. Make changes and test locally
npm run dev
# ... edit code ...
# 3. Commit and push
git add .
git commit -m "Add my feature"
git push origin feature/my-featureResult: Cloudflare auto-deploys to preview URL:
https://feature-my-feature.lantern-app-dev.pages.dev- Automatically protected by dev Access app (same PIN)
- Great for QA and testing
Deploy to Development (dev.ourlantern.app) โ
# Merge feature into dev
git checkout dev
git merge feature/my-feature
git push origin devResult: Auto-deploys to https://dev.ourlantern.app
Deploy to Production (ourlantern.app) โ
# Merge dev into main (use PR for code review)
git checkout main
git pull origin main
git merge dev
git push origin mainResult: Auto-deploys to https://ourlantern.app (live)
Branch Strategy โ
main (production)
โ
โโโ dev (development/staging)
โ
โโโ feature/my-feature
โโโ fix/bug-fixRules:
- โ Work in feature branches
- โ
Merge features โ
devfor testing - โ
Merge
devโmainfor production releases - โ Never commit directly to
main
Troubleshooting โ
Build Fails on Cloudflare โ
Error: Command not found: npm
- Fix: Set
NODE_VERSION=20in environment variables
Error: Module not found
- Fix: Ensure
package-lock.jsonis committed
Error: Build exceeded time limit
- Fix: Check build logs; may need to optimize dependencies
Firebase Not Connecting โ
Error: Missing Firebase config
- Fix: Check environment variables are set in Cloudflare dashboard
- Fix: Verify variable names start with
VITE_
Error: "Firebase: Error (auth/api-key-not-valid)"
- Fix: Double-check API key in Cloudflare environment variables
PWA Not Working โ
Error: Service worker not registering
- Fix: Ensure
_headersfile is inpublic/folder - Fix: Test with
npm run build && npm run previewlocally
Error: App not installable
- Fix: Check manifest at
/manifest.webmanifest - Fix: Verify HTTPS (Cloudflare provides this automatically)
Wrong Firebase Project โ
Error: Seeing dev data in production
- Fix: Check Cloudflare environment variables are set correctly for each environment
- Fix: Verify
VITE_APP_ENVis set toproductionfor main branch
Security Checklist โ
Before going live:
- [ ] Firebase Security Rules are set (NOT test mode)
- [ ] Environment variables are set in Cloudflare (not hardcoded)
- [ ]
.env.localis gitignored (never committed) - [ ] Cloudflare Access is configured (password/PIN protection)
- [ ] Firebase Authentication is enabled
- [ ] CSP headers are configured in
_headers - [ ] HTTPS is enforced (automatic on Cloudflare)
Quick Reference โ
Useful Commands โ
# Local development
npm run dev
# Build for production
npm run build
# Preview production build locally
npm run preview
# Run tests
npm test
# Deploy manually with Wrangler (alternative to Git-based)
npx wrangler pages deploy distImportant Files โ
.env.localโ Local dev config (gitignored)src/firebase.jsโ Firebase initializationpublic/_headersโ Security & PWA headerspublic/_redirectsโ SPA routing configwrangler.tomlโ Cloudflare config
Helpful Links โ
Next Steps โ
- โ Set up Firebase projects (dev + prod)
- โ Configure Cloudflare Pages
- โ Set environment variables
- โ
Create
devbranch - โ Test deployment workflow
- ๐ Add Firebase Security Rules
- ๐ Set up Cloudflare Access
- ๐ Add custom domain (optional)
- ๐ Set up Firebase Functions
- ๐ Configure CI/CD with GitHub Actions
Questions? Check docs/engineering/ for more details or open an issue!