Venue Age Rules Schema
Overview
Lantern enforces age restrictions at the venue level to ensure:
- Legal compliance (21+ for bars serving alcohol)
- Time-based restrictions (some venues change age requirements by time of day)
- User safety and appropriate venue matching
All age verification happens server-side using encrypted birth dates. Users never see ages displayed publicly.
Venue Configuration Schema
Basic Structure
{
"venue_id": "unique-venue-id",
"name": "Venue Name",
"type": "bar|cafe|restaurant|club|activities",
"min_age": 18,
"time_restricted": false,
"restrictions": []
}Fields
venue_id (string, required)
Unique identifier for the venue.
name (string, required)
Display name of the venue.
type (string, required)
Venue category. One of: bar, cafe, restaurant, club, activities, shop, public-space
min_age (integer, required)
Minimum age to light a lantern at this venue (default: 18)
- Most venues:
18 - Bars/clubs:
21 - Can be higher for special venues
time_restricted (boolean, required)
Whether venue has different age rules at different times.
false: Same age rule all daytrue: Age requirements change by time (seerestrictionsarray)
restrictions (array, optional)
Array of time-based age restrictions. Only used if time_restricted: true.
Each restriction object:
{
"start_time": "HH:MM",
"end_time": "HH:MM",
"min_age": 18
}Examples
Example 1: Simple Cafe (18+, All Day)
{
"venue_id": "cafe-midnight-bean",
"name": "The Midnight Bean",
"type": "cafe",
"min_age": 18,
"time_restricted": false,
"restrictions": []
}Behavior: All users 18+ can light lanterns anytime.
Example 2: Bar/Restaurant (Age Changes by Time)
{
"venue_id": "bar-rusty-anchor",
"name": "Rusty Anchor Pub",
"type": "bar",
"min_age": 18,
"time_restricted": true,
"restrictions": [
{
"start_time": "06:00",
"end_time": "22:00",
"min_age": 18,
"description": "Restaurant hours: 18+"
},
{
"start_time": "22:00",
"end_time": "06:00",
"min_age": 21,
"description": "Bar hours: 21+"
}
]
}Behavior:
- 6am-10pm: 18+ can enter (restaurant/dining)
- 10pm-6am: 21+ only (bar mode)
Example 3: Nightclub (21+ Always)
{
"venue_id": "club-electric-avenue",
"name": "Electric Avenue",
"type": "club",
"min_age": 21,
"time_restricted": false,
"restrictions": []
}Behavior: Only 21+ users can light lanterns.
Example 4: Public Space (18+)
{
"venue_id": "library-city-main",
"name": "City Library",
"type": "public-space",
"min_age": 18,
"time_restricted": false,
"restrictions": []
}Behavior: All 18+ users can light lanterns.
Server-Side Age Verification Logic
Pseudocode
function canUserLightLanternAtVenue(user, venue, currentTime) {
// 1. Decrypt user's birth date (server-side only)
const userAge = calculateAge(decryptBirthDate(user.encrypted_birth_date))
// 2. Get applicable age requirement
let requiredAge = venue.min_age
if (venue.time_restricted) {
// Find matching time restriction
const restriction = venue.restrictions.find(r =>
isTimeBetween(currentTime, r.start_time, r.end_time)
)
if (restriction) {
requiredAge = restriction.min_age
}
}
// 3. Check if user meets requirement
return userAge >= requiredAge
}Response to User
If eligible:
{
"can_light": true,
"venue_id": "bar-rusty-anchor",
"venue_name": "Rusty Anchor Pub"
}If NOT eligible:
{
"can_light": false,
"venue_id": "bar-rusty-anchor",
"venue_name": "Rusty Anchor Pub",
"reason": "This venue requires users to be 21+ after 10pm"
}Frontend Behavior
Venue Selection
When user selects a venue to light their lantern:
Frontend sends request to backend with:
user_idvenue_idcurrent_time
Backend validates age and responds
Frontend shows:
- Success: Open LightLanternForm
- Failure: Display error message with reason
Error Message Examples
❌ This venue is 21+ after 10pm
❌ This venue requires users to be 21+
❌ You must be 18+ to use LanternPrivacy Guarantees
What's Encrypted
- User's birth date (encrypted with user-specific key)
- Only decrypted server-side for age verification
- Never transmitted or displayed to other users
What's Logged
✅ "User X requested access to Venue Y at Time Z" ✅ "Age verification passed/failed" ❌ User's actual age ❌ User's birth date ❌ Calculation details
Data Retention
- Birth dates: Deleted 30 days after account deletion
- Verification logs: 90 days (no PII, just pass/fail)
- Location check-ins: 48 hours max
Integration with Merchant Offers
Merchants can only create offers for age-appropriate users.
Example: Bar Offer
{
"offer_id": "bogo-pints",
"venue_id": "bar-rusty-anchor",
"min_age_override": 21,
"headline": "BOGO pints after 8pm"
}Backend filters:
- Only shows offer to users who pass age check for venue
- If
min_age_overrideis set, use that instead of venue's min_age - Prevents accidental marketing of alcohol to underage users
Backend Implementation TODO
Firestore Schema
venues/{venue_id} - min_age: number - time_restricted: boolean - restrictions: array users/{user_id} - encrypted_birth_date: string - created_at: timestampCloud Function:
verifyVenueAge- Input: userId, venueId, timestamp
- Output:
Security Rules
javascript// Users can't read other users' birth dates match /users/{userId} { allow read: if request.auth.uid == userId; allow write: if false; // Only backend can write }Logging
- Log age verification attempts (no PII)
- Alert on repeated failures (potential abuse)
Questions to Answer
Grace period for birthdays?
- Should we verify age daily or cache verification status?
- Recommendation: Cache for 24 hours, reverify daily
Venue-specific overrides?
- Can merchants request custom age rules?
- Recommendation: Yes, but requires manual approval
Emergency access?
- What if user is locked out due to age miscalculation?
- Recommendation: Support ticket with ID verification
International users?
- Drinking age varies by country
- Recommendation: Use venue's local age rules