Subdomains & Access Management
Guide for creating new subdomains, managing Cloudflare Access policies, and maintaining security.
Table of Contents
- Domain Structure
- Protection Strategy
- Creating New Subdomains
- Access Policies
- Testing Workflows
- Troubleshooting
Domain Structure
Naming Convention
Every subdomain must have a dev. counterpart following this pattern:
| Production | Development |
|---|---|
ourlantern.app | dev.ourlantern.app |
{subdomain}.ourlantern.app | {subdomain}.dev.ourlantern.app |
Full Domain Tree
ourlantern.app (production root)
├── dev.ourlantern.app (development staging)
│
├── admin.ourlantern.app (admin portal - prod)
│ ├── /docs/ (internal documentation)
│ └── /storybook/ (component library)
│
├── admin.dev.ourlantern.app (admin portal - dev)
│ ├── /docs/ (internal documentation)
│ └── /storybook/ (component library)
│
├── docs.ourlantern.app (public documentation - planned)
│
├── api.ourlantern.app (future: backend/API - prod)
├── api.dev.ourlantern.app (future: backend/API - dev)
│
├── *.ourlantern.app (wildcard catch-all - prod)
└── *.dev.ourlantern.app (wildcard catch-all - dev)Deprecated subdomains:
docs.dev.ourlantern.app,storybook.dev.ourlantern.app,storybook.ourlantern.appThese are replaced by paths within the admin portal.
Current Domains
| Domain | Environment | Purpose | Protection |
|---|---|---|---|
dev.ourlantern.app | Development | Main app staging | In-app Firebase Auth |
admin.dev.ourlantern.app | Development | Admin portal (includes docs & storybook) | In-app Firebase Auth + Admin role |
*.dev.ourlantern.app | Development | Wildcard catch-all | Cloudflare Access PIN |
ourlantern.app | Production | Main app (live) | In-app Firebase Auth (planned) |
admin.ourlantern.app | Production | Admin portal (includes docs & storybook) | In-app Firebase Auth + Admin role (planned) |
docs.ourlantern.app | Production | Public documentation | Public (planned - curated subset) |
*.ourlantern.app | Production | Wildcard catch-all | Cloudflare Access PIN |
Note: Docs and Storybook are bundled into the admin portal at
/docs/and/storybook/paths. Separatedocs.dev.ourlantern.appandstorybook.dev.ourlantern.appsubdomains are deprecated. See Issue #255 for implementation details.
Protection Strategy
We use a hybrid protection model to avoid Cloudflare Access cache issues on the main app while keeping static sites protected.
In-App Firebase Auth (No Cloudflare Access)
These domains use Firebase Authentication at the app level:
| Domain | Auth Requirement |
|---|---|
dev.ourlantern.app | Firebase login + admin role (pilot phase) |
admin.dev.ourlantern.app | Firebase login + admin role |
ourlantern.app (planned) | Firebase login + admin role (pilot phase) |
admin.ourlantern.app (planned) | Firebase login + admin role |
Why? Cloudflare Access redirects cause persistent cache issues, blocking real-time app updates.
Pilot → Public transition: When ready to open to the public, set PILOT_MODE = false in src/App.jsx.
Implementation details:
src/App.jsx- ContainsPILOT_MODEflag and access gate logicsrc/components/AccessGate.jsx- UI components for loading, login prompt, and access deniedsrc/lib/auth.js-checkAdminRole()function checks Custom Claims then falls back to Firestore
See Issue #254 for implementation details.
Docs & Storybook (Bundled in Admin Portal)
Docs and Storybook are served as static assets within the admin portal:
| URL | Content |
|---|---|
admin.dev.ourlantern.app/docs/ | VitePress documentation (all internal docs) |
admin.dev.ourlantern.app/storybook/ | Storybook component library |
admin.ourlantern.app/docs/ (planned) | VitePress documentation (all internal docs) |
admin.ourlantern.app/storybook/ (planned) | Storybook component library |
Why bundled? Single auth system (Firebase), no Cloudflare Access cache issues, simpler infrastructure.
Future public docs: When ready, curated user-facing docs will be deployed to docs.ourlantern.app (public, no auth).
See Issue #255 for implementation details.
Cloudflare Access PIN (Wildcard Safety Net)
The *.ourlantern.app and *.dev.ourlantern.app wildcards ensure:
- Any new/forgotten subdomain is protected by default
- Preview deployments are automatically locked down
- Zero-effort security for future subdomains
Creating New Subdomains
Scenario: Adding a New Subdomain (e.g., api.ourlantern.app)
Step 1: Code Your Feature
Create a feature branch and develop your subdomain code:
git checkout -b feature/api-endpoint dev
# ... implement API functionality ...
git push origin feature/api-endpointStep 2: Test on Preview URL
Cloudflare auto-generates a preview URL:
https://feature-api-endpoint.lantern-app-dev.pages.dev- Already protected by dev Access app
- Test thoroughly before proceeding
Step 3: Merge to Dev
git checkout dev
git merge feature/api-endpoint
git push origin devResult: Deploys to dev.ourlantern.app for staging.
Step 4: Add DNS Record in Cloudflare
- Go to Cloudflare Dashboard → DNS
- Click Add record
- Configure:
- Type: CNAME
- Name:
api - Content:
lantern-app.pages.dev(or your prod project) - Proxy status: Proxied (orange cloud)
- TTL: Auto
- Save
Step 5: Create/Update Access App
Option A: Use Wildcard App (Recommended)
- The existing
lantern-app-wildcardapp already protects*.ourlantern.app - No new Access app needed
- New subdomain automatically inherits PIN policy
Option B: Create Specific Access App
- Go to Cloudflare Zero Trust → Access → Applications
- Click Add an application → Self-hosted
- Configure:
- Application name:
lantern-app-api - Domain:
api.ourlantern.app
- Application name:
- Add same PIN policy as other apps
- Save
Step 6: Deploy to Production
Once tested in dev:
git checkout main
git merge dev
git push origin mainResult: Lives at https://api.ourlantern.app (protected by Access)
Access Policies
Current Access Setup
Cloudflare Access applications protect static sites and act as wildcard safety nets:
lantern-docs-dev(Development Docs)- Domain:
docs.dev.ourlantern.app - Policy: One-time PIN
- Purpose: Dev documentation site
- Domain:
lantern-storybook-dev(Development Storybook)- Domain:
storybook.dev.ourlantern.app - Policy: One-time PIN
- Purpose: Dev component library
- Domain:
lantern-wildcard-dev(Development Catch-all)- Domain:
*.dev.ourlantern.app - Policy: One-time PIN
- Purpose: Protect any unknown dev subdomains
- Domain:
lantern-docs(Production Docs)- Domain:
docs.ourlantern.app - Policy: One-time PIN
- Purpose: Production documentation site
- Domain:
lantern-storybook(Production Storybook)- Domain:
storybook.ourlantern.app - Policy: One-time PIN
- Purpose: Production component library
- Domain:
lantern-wildcard(Production Catch-all)- Domain:
*.ourlantern.app - Policy: One-time PIN
- Purpose: Protect any unknown prod subdomains
- Domain:
Domains WITHOUT Cloudflare Access
These domains rely on in-app Firebase Auth instead:
| Domain | Why No Cloudflare Access |
|---|---|
dev.ourlantern.app | Main app - Firebase auth avoids cache issues |
admin.dev.ourlantern.app | Admin portal - Firebase auth + admin role |
ourlantern.app (planned) | Main app - Firebase auth avoids cache issues |
admin.ourlantern.app (planned) | Admin portal - Firebase auth + admin role |
Modifying Access Policies
Change Authentication Method
For example, switch from "One-time PIN" to "Email Domain":
- Go to Cloudflare Zero Trust → Access → Applications
- Click the app name (e.g.,
lantern-app-dev) - Click Edit
- Go to Policies tab
- Click your existing policy
- Change Require from "One-time PIN" to "Email addresses":
- Add allowed email addresses (e.g., team members)
- Save policy
Add a New Policy
Example: Allow GitHub organization members:
- Go to Applications → Click app → Edit → Policies
- Click Add a policy
- Configure:
- Action: Allow
- Include rule:
- Selector: GitHub Organization
- Value: Your org name
- Session duration: 24 hours
- Save
Remove/Disable a Policy
- Go to Applications → Click app → Edit → Policies
- Click the X button on the policy to delete
- Save
Session Management
Adjust how long users stay logged in:
- Click app → Edit → Click policy
- Under Session duration, choose:
- 15 minutes (most secure)
- 1 hour
- 24 hours (current default)
- Custom duration
- Save
Testing Workflows
Testing New Subdomains Locally
Since new subdomains require Cloudflare DNS, test locally first:
# In your dev server, you can test by mocking the subdomain
# For example, test API logic without hitting Cloudflare DNS
npm run dev
# Visit http://localhost:5173 and test your codeTesting on Preview URL
Feature branches auto-deploy to preview URLs:
https://feature-api-endpoint.lantern-app-dev.pages.devAccess PIN: Same as dev environment (request from team)
Testing in Dev Environment
After merging to dev:
https://dev.ourlantern.appFull staging environment with dev Firebase, staging data, etc.
Testing in Production
After merging to main:
https://ourlantern.appLive environment — use real Firebase, real data, and careful QA!
Common Tasks
Add a New Team Member to Access
- Go to Access → Applications
- Click the app (e.g.,
lantern-app-dev) - Click Edit → Policies
- Edit the PIN or email policy
- Add their email address
- Save
Disable Access Temporarily
⚠️ Warning: This makes your site public. Use with caution.
- Go to Access → Applications
- Click app → Edit
- Click the Delete button at the bottom
- Confirm deletion
- The domain becomes public (no more Access protection)
To re-enable: Create a new Access app with the same domain and policy.
Monitor Access Logs
- Go to Access → Logs
- View recent authentication attempts
- Filter by domain, date range, or user
- Useful for debugging access issues
Set Different PIN for Different Environments
Currently all use the same PIN. To differentiate:
- Edit dev Access app → Click policy
- Change PIN code
- Save
Now dev has a different PIN than prod (useful for separating access).
Security Best Practices
✅ Do
- ✅ Always create both prod and dev subdomains (e.g.,
api.ourlantern.appANDapi.dev.ourlantern.app) - ✅ Keep Cloudflare Access enabled on static sites (docs, storybook)
- ✅ Use in-app Firebase auth for main app and admin portal
- ✅ Use strong, random PIN codes for Cloudflare Access
- ✅ Limit who has access to production
- ✅ Review Access logs regularly
- ✅ Test new subdomains on preview before production
- ✅ Rely on wildcard apps as a safety net for unknown subdomains
❌ Don't
- ❌ Create a subdomain without its
dev.counterpart - ❌ Use Cloudflare Access on main app (causes cache issues)
- ❌ Share PIN codes publicly or in code
- ❌ Use weak PIN codes (like "1234")
- ❌ Forget to add DNS records for new subdomains
- ❌ Commit environment secrets to Git
Troubleshooting
New Subdomain Not Working
Error: 404 or "Not Found"
Checks:
- Is the DNS record created? Go to DNS tab and verify CNAME exists
- Is it proxied (orange cloud)? If not, enable proxying
- Did you wait for DNS propagation? (Usually instant, but can take 5 mins)
- Is the code actually deployed to the right Pages project?
Fix:
# Verify DNS record
nslookup api.ourlantern.app
# Should resolve to lantern-app.pages.dev or lantern-app-dev.pages.devAccess Redirecting to Wrong Domain
Error: dev.ourlantern.app redirects to ourlantern.app
Cause: Multiple Access apps covering the same domain
Fix:
- Go to Access → Applications
- Check for duplicate entries
- Ensure each domain has only ONE app
- Delete duplicates if found
PIN Not Working
Error: "Invalid PIN" even though you entered it correctly
Cause: PIN sent to your email may have expired (usually 15 mins)
Fix:
- Go back and request a new PIN
- Check spam/junk folder
- If still issues, contact admin to reset
Related Documentation
- DEPLOYMENT.md — Main deployment guide
- ENVIRONMENT_SETUP.md — Setting up environments
- PWA.md — Progressive Web App setup