Skip to content

Venue Age Rules Schema โ€‹

Overview โ€‹

Lantern enforces age restrictions at the venue level to ensure:

  1. Legal compliance (21+ for bars serving alcohol)
  2. Time-based restrictions (some venues change age requirements by time of day)
  3. 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 โ€‹

json
{
  "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 day
  • true: Age requirements change by time (see restrictions array)

restrictions (array, optional) โ€‹

Array of time-based age restrictions. Only used if time_restricted: true.

Each restriction object:

json
{
  "start_time": "HH:MM",
  "end_time": "HH:MM",
  "min_age": 18
}

Examples โ€‹

Example 1: Simple Cafe (18+, All Day) โ€‹

json
{
  "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) โ€‹

json
{
  "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) โ€‹

json
{
  "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+) โ€‹

json
{
  "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 โ€‹

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

json
{
  "can_light": true,
  "venue_id": "bar-rusty-anchor",
  "venue_name": "Rusty Anchor Pub"
}

If NOT eligible:

json
{
  "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:

  1. Frontend sends request to backend with:

    • user_id
    • venue_id
    • current_time
  2. Backend validates age and responds

  3. 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 Lantern

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

json
{
  "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_override is set, use that instead of venue's min_age
  • Prevents accidental marketing of alcohol to underage users

Backend Implementation TODO โ€‹

  1. Firestore Schema

    venues/{venue_id}
      - min_age: number
      - time_restricted: boolean
      - restrictions: array
    
    users/{user_id}
      - encrypted_birth_date: string
      - created_at: timestamp
  2. Cloud Function: verifyVenueAge

    • Input: userId, venueId, timestamp
    • Output:
  3. 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
    }
  4. Logging

    • Log age verification attempts (no PII)
    • Alert on repeated failures (potential abuse)

Questions to Answer โ€‹

  1. Grace period for birthdays?

    • Should we verify age daily or cache verification status?
    • Recommendation: Cache for 24 hours, reverify daily
  2. Venue-specific overrides?

    • Can merchants request custom age rules?
    • Recommendation: Yes, but requires manual approval
  3. Emergency access?

    • What if user is locked out due to age miscalculation?
    • Recommendation: Support ticket with ID verification
  4. International users?

    • Drinking age varies by country
    • Recommendation: Use venue's local age rules

Built with VitePress