Skip to content

Deployment Guide โ€” Lantern App โ€‹

Complete guide for deploying the Lantern app to development and production environments using Cloudflare Pages and Firebase.


Table of Contents โ€‹

  1. Overview
  2. Prerequisites
  3. Environment Setup
  4. Local Development
  5. Cloudflare Pages Setup
  6. Firebase Projects Setup
  7. Deployment Workflow
  8. 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 โ€‹

ComponentEnvironmentURLPages ProjectUse Case
AppProductionourlantern.applantern-appLive users
AppDevelopmentdev.ourlantern.applantern-app-devTeam testing
DocsProductiondocs.ourlantern.applantern-docsPublic documentation
DocsDevelopmentdocs.dev.ourlantern.applantern-docs-devUnreleased docs
StorybookProductionstorybook.ourlantern.applantern-storybookComponent library
StorybookDevelopmentstorybook.dev.ourlantern.applantern-storybook-devDev 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:

  1. User clicks "Allow Location" in browser prompt
  2. App gets GPS coordinates from browser
  3. Server validates location (see Location Security in TODO.md)
  4. Location used for feature (proximity match, check-in verification)
  5. 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.jsx for location spoofing debug panel (dev only)

Prerequisites โ€‹

Required Accounts โ€‹

  • [X] Google/Firebase account
  • [X] Cloudflare account
  • [X] GitHub account (repo already exists)

Required Tools โ€‹

bash
# Node.js (v18 or v20)
node --version

# npm (comes with Node)
npm --version

# Git
git --version

# Optional: Cloudflare CLI (wrangler)
npm install -g wrangler

Environment Setup โ€‹

Step 1: Create Firebase Projects โ€‹

You need TWO separate Firebase projects:

Development Project โ€‹

  1. Go to Firebase Console
  2. Click "Add project"
  3. Name: lantern-app-dev
  4. Enable Google Analytics (optional)
  5. Click "Create project"

Production Project โ€‹

  1. Repeat steps above
  2. Name: lantern-app-prod

Step 2: Get Firebase Configuration โ€‹

For EACH Firebase project (lantern-app-dev and lantern-app-prod):

  1. Go to Project Settings (gear icon)
  2. Scroll to "Your apps" section
  3. Click "Web" icon (</>) to add a web app
  4. Register app name (e.g., "Lantern Dev" or "Lantern Prod")
  5. Copy the config object that looks like:
javascript
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:

  1. Authentication

    • Go to Authentication > Get started
    • Enable "Email/Password" provider
    • (Later: Add Google, Facebook, etc. as needed)
  2. 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)
  3. Storage

    • Go to Storage > Get started
    • Start in "test mode"
  4. Functions (optional for now)

    • Will set up when needed

Local Development โ€‹

Step 1: Clone & Install โ€‹

bash
# Clone repo (if not already)
git clone https://github.com/cattreedev/lantern_app.git
cd lantern_app

# Install dependencies
npm install

Step 2: Set Up Local Environment โ€‹

bash
# Copy the example file
cp .env.local.example .env.local

Edit .env.local with your DEV Firebase config:

bash
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 โ€‹

bash
npm run dev

Visit 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-dev

Cloudflare Pages Setup โ€‹

Architecture: Two Separate Projects โ€‹

We use two independent Cloudflare Pages projects to avoid auth conflicts and keep environments clean:

ProjectBranchDomainPurpose
lantern-appmainourlantern.appProduction (live)
lantern-app-devdevdev.ourlantern.appDevelopment/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) โ€‹

  1. Go to Cloudflare Dashboard โ†’ Pages
  2. Click "Create a project" โ†’ "Connect to Git"
  3. Select cattreedev/lantern_app
  4. Configure:
    • Project name: lantern-app
    • Production branch: main
    • Build command: npm run build
    • Build output directory: dist

Step 2: Create Development Pages Project (lantern-app-dev) โ€‹

  1. Click "Create a project" again
  2. Select same repo cattreedev/lantern_app
  3. Configure:
    • Project name: lantern-app-dev
    • Production branch: dev (not main!)
    • Build command: npm run build
    • Build output directory: dist

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 = 20

Step 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 = 20

Step 5: Add Custom Domains โ€‹

Production Project โ€‹

  1. In lantern-app, go to Settings โ†’ Domains & accounts
  2. Click Add domain
  3. Enter ourlantern.app
  4. Cloudflare auto-creates required DNS records

Development Project โ€‹

  1. In lantern-app-dev, go to Settings โ†’ Domains & accounts
  2. Click Add domain
  3. Enter dev.ourlantern.app
  4. 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 โ€‹

  1. Go to Cloudflare Zero Trust โ†’ Access โ†’ Applications
  2. Click Add an application โ†’ Self-hosted
  3. Configure:
    • Application name: lantern-app
    • Subdomain/Domain: ourlantern.app
    • Path: /
  4. 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)
  5. Save

2. Development Access App โ€‹

  1. Click Add an application โ†’ Self-hosted
  2. Configure:
    • Application name: lantern-app-dev
    • Subdomain/Domain: dev.ourlantern.app
    • Path: /
  3. Add same policy as production (or customize as needed)

3. Wildcard Access App (Catch-all for future subdomains) โ€‹

  1. Click Add an application โ†’ Self-hosted
  2. Configure:
    • Application name: lantern-app-wildcard
    • Subdomain/Domain: *.ourlantern.app
    • Path: /
  3. Add same PIN policy
  4. This catches any accidental subdomains (e.g., api.ourlantern.app)

Result:

  • โœ… ourlantern.app protected by prod app
  • โœ… dev.ourlantern.app protected by dev app
  • โœ… Any future *.ourlantern.app caught 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:

javascript
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:

javascript
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 โ€‹

bash
# 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-feature

Result: 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) โ€‹

bash
# Merge feature into dev
git checkout dev
git merge feature/my-feature
git push origin dev

Result: Auto-deploys to https://dev.ourlantern.app

Deploy to Production (ourlantern.app) โ€‹

bash
# Merge dev into main (use PR for code review)
git checkout main
git pull origin main
git merge dev
git push origin main

Result: Auto-deploys to https://ourlantern.app (live)


Branch Strategy โ€‹

main (production)
  โ†‘
  โ””โ”€โ”€ dev (development/staging)
        โ†‘
        โ””โ”€โ”€ feature/my-feature
        โ””โ”€โ”€ fix/bug-fix

Rules:

  • โœ… Work in feature branches
  • โœ… Merge features โ†’ dev for testing
  • โœ… Merge dev โ†’ main for production releases
  • โŒ Never commit directly to main

Troubleshooting โ€‹

Build Fails on Cloudflare โ€‹

Error: Command not found: npm

  • Fix: Set NODE_VERSION=20 in environment variables

Error: Module not found

  • Fix: Ensure package-lock.json is 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 _headers file is in public/ folder
  • Fix: Test with npm run build && npm run preview locally

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_ENV is set to production for main branch

Security Checklist โ€‹

Before going live:

  • [ ] Firebase Security Rules are set (NOT test mode)
  • [ ] Environment variables are set in Cloudflare (not hardcoded)
  • [ ] .env.local is 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 โ€‹

bash
# 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 dist

Important Files โ€‹

  • .env.local โ€” Local dev config (gitignored)
  • src/firebase.js โ€” Firebase initialization
  • public/_headers โ€” Security & PWA headers
  • public/_redirects โ€” SPA routing config
  • wrangler.toml โ€” Cloudflare config

Next Steps โ€‹

  1. โœ… Set up Firebase projects (dev + prod)
  2. โœ… Configure Cloudflare Pages
  3. โœ… Set environment variables
  4. โœ… Create dev branch
  5. โœ… Test deployment workflow
  6. ๐Ÿ”œ Add Firebase Security Rules
  7. ๐Ÿ”œ Set up Cloudflare Access
  8. ๐Ÿ”œ Add custom domain (optional)
  9. ๐Ÿ”œ Set up Firebase Functions
  10. ๐Ÿ”œ Configure CI/CD with GitHub Actions

Questions? Check docs/engineering/ for more details or open an issue!

Built with VitePress