Safety Mechanics Quick Start Guide โ
For developers implementing safety features in Lantern
Overview โ
This guide provides a quick reference for implementing the safety features documented in SAFETY_MECHANICS.md. Start here for implementation priorities and key integration points.
Priority Order (MVP โ Full Launch) โ
Phase 1: Block Functionality (4-6 weeks) โญ START HERE โ
Why First: Foundation for all other safety features. Users need ability to control who can interact with them.
Key Files to Create:
src/lib/blockService.js- Block/unblock logicsrc/components/BlockButton.jsx- Reusable block UI componentsrc/components/BlockedUsersList.jsx- Manage blocked users
Key Files to Modify:
src/components/Chat.jsx- Add block button to chat menusrc/components/WaveManager.jsx- Add block option to wave notificationssrc/screens/profile/ProfileSettings.jsx- Add "Blocked Users" section
Firestore Collections:
// Collection: blocks
{
id: "{userId}_{blockedUserId}",
userId: "user123",
blockedUserId: "user456",
reason: "harassment", // optional
createdAt: Timestamp,
platform: "web"
}Security Rules:
// Users can only read/write their own blocks
match /blocks/{blockId} {
allow read, write: if request.auth != null &&
blockId.matches(request.auth.uid + '_.*');
}Testing Checklist:
- [ ] User A blocks User B
- [ ] User B disappears from User A's lantern hub, waves, chats
- [ ] User B cannot see User A's lantern or send waves
- [ ] Block persists across devices (test on phone + desktop)
- [ ] User A can unblock User B from ProfileSettings
- [ ] Pattern detection: flag user receiving 5+ blocks
Phase 2: In-App Safety Education (2-3 weeks) โ
Why Second: Low development effort, high user value. Educates users on safety best practices.
Key Files to Create:
src/components/SafetyTipsPanel.jsx- Safety education contentsrc/components/SafetyPrompt.jsx- Contextual safety reminders
Key Files to Modify:
src/screens/profile/ProfileSettings.jsx- Add "Safety & Privacy" tabsrc/components/LightLanternForm.jsx- Add safety prompt before first lightsrc/components/WaveManager.jsx- Add safety reminder before accepting first wave
Content to Include:
- Meeting strangers safely (public places, tell a friend, trust instincts)
- Recognizing red flags (pushy behavior, asking for money, oversharing)
- Using safety features (block, report, SOS)
- Crisis hotline links (domestic violence, suicide prevention, trans lifeline)
Testing Checklist:
- [ ] Navigate to ProfileSettings โ Safety & Privacy โ see safety tips
- [ ] First lantern light โ see safety reminder prompt
- [ ] First wave acceptance โ see "you can block this user" message
- [ ] All crisis hotline links are correct and functional
Phase 2.5: Platform Bans & Re-Registration Prevention (6-10 weeks) โ
Why Here: Build on Phase 1 block pattern detection. Essential before scaling to prevent abuse at scale.
Key Files to Create:
src/lib/banService.js- Account ban/suspension logicsrc/lib/deviceFingerprint.js- Browser/device fingerprinting utilitiessrc/lib/behavioralFingerprint.js- Behavioral pattern detectionsrc/components/moderation/ModerationDashboard.jsx- Internal tool for reviewing flagged accountssrc/components/moderation/BanNotification.jsx- In-app ban notification UI
Key Files to Modify:
src/lib/auth.js- Add ban check during signup/loginsrc/screens/auth/SignupFlow.jsx- Check for ban evasion before account creation
Firestore Collections:
// Collection: banned_accounts
{
id: "ban123",
reason: "harassment",
severity: "permanent", // "warning" | "temporary" | "shadow" | "permanent"
bannedAt: Timestamp,
expiresAt: Timestamp, // null for permanent
appealStatus: "pending" | "upheld" | "overturned",
// Hashed identifiers (for re-registration prevention)
emailHash: "bcrypt_hash",
phoneHash: "bcrypt_hash",
deviceFingerprint: "browser_fingerprint_hash",
installationId: "uuid",
// Behavioral signatures (anonymized)
behavioralSignature: {
typingSpeed: 120, // WPM
wavingPattern: [9, 14, 22], // Hours of day
preferredVenues: ["coffee_shop", "bar"]
}
}
// Collection: moderation_queue
{
id: "flag123",
userId: "user123",
flagReason: "multiple_blocks", // "multiple_blocks" | "spam" | "reported"
flaggedAt: Timestamp,
evidence: {
blockCount: 7,
blockReasons: ["harassment", "spam", "harassment"],
reportCount: 3
},
status: "pending" | "reviewed" | "dismissed",
reviewedBy: "moderator_id",
reviewedAt: Timestamp,
action: "warning" | "suspension" | "shadow_ban" | "permanent_ban" | "no_action"
}Re-Registration Prevention Strategy:
Layer 1: Email/Phone Hashing
// src/lib/banService.js
import bcrypt from 'bcrypt'
export async function checkEmailBanned(email) {
const emailHash = await bcrypt.hash(email.toLowerCase(), 10)
const bannedRef = collection(db, 'banned_accounts')
const q = query(bannedRef, where('emailHash', '==', emailHash))
const snapshot = await getDocs(q)
return !snapshot.empty
}Layer 2: Device Fingerprinting
// src/lib/deviceFingerprint.js
export function generateDeviceFingerprint() {
const fingerprint = {
userAgent: navigator.userAgent,
language: navigator.language,
screenResolution: `${screen.width}x${screen.height}`,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
platform: navigator.platform,
// Hash components together
hash: hashComponents([...])
}
return fingerprint.hash
}Layer 3: Behavioral Fingerprinting
// src/lib/behavioralFingerprint.js
export function analyzeTypingPattern(keystrokes) {
// Measure time between keystrokes
const intervals = []
for (let i = 1; i < keystrokes.length; i++) {
intervals.push(keystrokes[i].timestamp - keystrokes[i-1].timestamp)
}
return {
avgInterval: intervals.reduce((a,b) => a+b) / intervals.length,
stdDev: calculateStdDev(intervals),
typingSpeed: calculateWPM(keystrokes)
}
}
export function comparePatterns(pattern1, pattern2) {
// Calculate similarity score (0-100)
const speedSimilarity = 1 - Math.abs(pattern1.typingSpeed - pattern2.typingSpeed) / Math.max(pattern1.typingSpeed, pattern2.typingSpeed)
// ... more comparisons
return similarityScore
}Account Creation Flow with Ban Check:
// src/screens/auth/SignupFlow.jsx
async function handleSignup(email, phone) {
// 1. Check email hash
const emailBanned = await checkEmailBanned(email)
if (emailBanned) {
throw new Error("This email cannot be used")
}
// 2. Check phone hash (if provided)
if (phone) {
const phoneBanned = await checkPhoneBanned(phone)
if (phoneBanned) {
throw new Error("This phone number cannot be used")
}
}
// 3. Get device fingerprint
const deviceFP = generateDeviceFingerprint()
const deviceBanned = await checkDeviceBanned(deviceFP)
if (deviceBanned) {
// Don't block immediately (false positives), but flag for review
await flagForReview(email, deviceFP, "banned_device_match")
}
// 4. Create account (monitor behavioral patterns for 7 days)
const user = await createAccount(email, phone, deviceFP)
// 5. Start behavioral monitoring
await startBehavioralMonitoring(user.id, deviceFP)
}Moderation Dashboard (Internal Tool):
- View flagged accounts in queue
- See evidence (block count, reasons, reports, behavioral patterns)
- Take action (warn, suspend, shadow ban, permanent ban)
- Document decision with notes
- Send notification to user
Testing Checklist:
- [ ] User receives 5 blocks โ flagged for moderation review
- [ ] User receives 10 blocks โ automatic 7-day suspension
- [ ] Moderator reviews flagged account โ can warn/suspend/ban
- [ ] Banned user receives email notification with reason
- [ ] Banned user tries signup with same email โ rejected
- [ ] Banned user tries signup with new email, same device โ flagged for review
- [ ] Banned user tries signup with new email, new device โ behavioral monitoring detects match after 3 days
- [ ] False positive: Legitimate user on shared device not auto-banned
- [ ] Appeals process: User appeals ban โ different moderator reviews โ decision communicated within 7 days
Privacy Safeguards:
- Only store hashed emails/phones (cannot reverse)
- Device fingerprints anonymized (no PII)
- Behavioral patterns aggregated (not keystroke-level detail)
- Ban data retention: 1 year, then deleted
- GDPR compliance: User can request data deletion (ban remains active)
Phase 3: Safe Spaces Partnership (8-12 weeks) โ
Why Third: Requires external partnerships and legal agreements. Start outreach early but implement after Phases 1-2.
Key Files to Create:
src/lib/safeSpacesService.js- Safe space search and alertingsrc/components/SafeSpaceMap.jsx- Map view of nearby safe spacessrc/components/SafeSpaceCard.jsx- Display safe space details
Key Files to Modify:
src/lib/venueService.js- Add safe space fields to venue data modelsrc/screens/dashboard/Dashboard.jsx- Add safe space search option
Partnership Process:
- Create partnership application form (Google Forms or Typeform)
- Vet organizations (501(c)(3) status, references, background checks)
- Legal agreements (liability waivers, privacy commitments, alert response SLAs)
- Onboarding training (how to respond to SOS alerts, user privacy expectations)
- Add to Firestore with encrypted contact details
Firestore Collections:
// Collection: safe_spaces
{
id: "safeSpace123",
name: "The San Diego LGBT Community Center",
type: "lgbtq_center",
address: "3909 Centre St, San Diego, CA 92103",
location: new GeoPoint(32.7537, -117.1628),
contactPhone: "619-692-2077", // encrypted
contactEmail: "safety@thecentersd.org",
services: ["crisis_intervention", "counseling", "safe_space"],
operatingHours: {
monday: "9am-9pm",
tuesday: "9am-9pm",
// ...
},
acceptsSOSAlerts: true,
publiclyListed: true, // false for confidential addresses (DV shelters)
fundingTier: 1, // $500/month
totalAlertsReceived: 0,
avgResponseTime: null
}San Diego Pilot Partners (Target: 5-10 organizations): These are example organizations identified for the San Diego pilot. Actual partners will be determined during implementation based on availability, interest, and compatibility with Lantern's mission.
Potential partner types:
- LGBTQ+: Local LGBTQ+ community centers and resource organizations
- Domestic Violence: Women's shelters and family violence prevention centers
- Mental Health: Crisis intervention services and walk-in counseling centers
- Community: Neighborhood safety organizations and mutual aid networks
Note: Contact information and specific organizations should be verified during implementation as these details can change.
Testing Checklist:
- [ ] Search for safe spaces โ see nearest options with distance
- [ ] Tap safe space โ see details (services, hours, contact info)
- [ ] Map view shows safe spaces within 5 miles
- [ ] Confidential addresses (DV shelters) are NOT shown on public map
Phase 4: SOS Emergency System (12-16 weeks) โ
Why Last: Most complex feature requiring native APIs, legal review, and extensive testing. Build after foundation is solid.
Key Files to Create:
src/lib/sosService.js- SOS activation/deactivation logicsrc/lib/recordingService.js- Audio/video recording via Web APIssrc/lib/emergencyContacts.js- Contact management and notificationssrc/components/SOSButton.jsx- Prominent SOS button componentsrc/components/SOSActivationModal.jsx- Two-stage confirmation screensrc/components/SOSActiveNotification.jsx- Persistent notification during active SOSsrc/components/EmergencyContactsManager.jsx- Add/edit/remove contacts
Key Files to Modify:
src/components/dashboard/DashboardNavigation.jsx- Add SOS button (left side, red, always visible)src/screens/profile/ProfileSettings.jsx- Add "Emergency Contacts" section
Web APIs Required:
navigator.mediaDevices.getUserMedia()- Camera/microphone accessMediaRecorder- Audio/video recordingnavigator.geolocation.watchPosition()- Real-time GPS trackingBlob+URL.createObjectURL()- Local recording storage- Firebase Cloud Functions - SMS/email notifications
Firestore Collections:
// Collection: sos_incidents
{
id: "sos123",
userId: "user123",
status: "active", // "active" | "resolved" | "cancelled"
activatedAt: Timestamp,
deactivatedAt: null,
location: new GeoPoint(32.7157, -117.1611),
venue: "Balboa Park",
recordingUrl: null, // encrypted blob URL (after upload)
incidentReport: null,
notifiedContacts: ["contact1", "contact2"],
notifiedSafeSpaces: ["safeSpace123"]
}
// Collection: emergency_contacts
{
id: "contact123",
userId: "user123",
name: "Jane Doe",
phone: "+1-619-555-1234",
email: "jane@example.com",
relationship: "friend", // "friend" | "family" | "partner" | "roommate"
verified: false, // true if contact confirmed via SMS/email
createdAt: Timestamp
}Emergency Actions Flow:
- User taps SOS button โ Confirmation modal appears
- User confirms โ Immediate actions triggered:
- Start audio/video recording (save to local IndexedDB)
- Begin real-time GPS tracking (update every 30 seconds)
- Send SMS/push to emergency contacts: "[Name] activated Lantern SOS at [Location]"
- Alert nearest safe space (if within 5 miles)
- Optionally call 911 (if user enabled in settings)
- Show persistent notification: "SOS ACTIVE - Recording and sharing location"
- User can deactivate by entering PIN/passphrase
- Post-deactivation: Prompt for incident report (optional)
Legal Requirements:
- Update Terms of Service to disclose recording functionality
- Check state recording laws (one-party vs two-party consent)
- GDPR/CCPA compliance for location data processing
- Liability waivers for safe space partnerships
Testing Checklist:
- [ ] Tap SOS โ see confirmation modal
- [ ] Confirm โ recording starts (verify microphone icon in browser)
- [ ] Location sharing active (verify GPS coordinates updating)
- [ ] Emergency contacts receive SMS with live location link
- [ ] Nearest safe space receives alert (if within 5 miles)
- [ ] Recording saves to IndexedDB (check DevTools โ Application โ IndexedDB)
- [ ] Deactivate SOS โ recording stops, location sharing ends
- [ ] Incident report form appears post-deactivation
- [ ] Recording uploads to Firebase Storage (encrypted)
Key Integration Points โ
Dashboard.jsx โ
// Add to imports
import { blockUser, isUserBlocked } from '../lib/blockService'
// Filter blocked users from waves and connections
const visibleWaves = incomingWaves.filter(wave =>
!isUserBlocked(currentUser.uid, wave.senderId)
)
const visibleConnections = activeConnections.filter(conn =>
!isUserBlocked(currentUser.uid, conn.userId)
)Chat.jsx โ
// Add block button to menu
<button
onClick={() => {
setShowMenu(false)
handleBlockUser(connection.userId)
}}
className="w-full px-4 py-3 text-left text-orange-400 hover:bg-orange-500/10 flex items-center gap-2"
>
<Shield size={16} />
Block User
</button>WaveManager.jsx โ
// Add block button to wave notifications
<button
onClick={() => onBlock(wave.senderId)}
className="flex-1 bg-red-500/20 hover:bg-red-500/30 border border-red-500/40 text-red-400 rounded-xl py-3 font-medium flex items-center justify-center gap-2"
>
<Shield size={18} />
Block
</button>ProfileSettings.jsx โ
// Add Blocked Users section
<div className="border-t border-neutral-800 pt-6">
<h4 className="text-white font-medium mb-2 flex items-center gap-2">
<Shield className="w-5 h-5 text-amber-400" />
Blocked Users
</h4>
<BlockedUsersList userId={currentUser.uid} />
</div>
// Add Emergency Contacts section
<div className="border-t border-neutral-800 pt-6 mt-6">
<h4 className="text-white font-medium mb-2 flex items-center gap-2">
<AlertTriangle className="w-5 h-5 text-red-400" />
Emergency Contacts
</h4>
<EmergencyContactsManager userId={currentUser.uid} />
</div>Common Patterns โ
Block Confirmation Modal โ
const handleBlockUser = async (targetUserId) => {
const confirmed = window.confirm(
"Block this user? They won't be able to see you or interact with you. This action can be undone in Settings."
)
if (!confirmed) return
try {
await blockUser(currentUser.uid, targetUserId)
// Close chat/wave notification
onClose()
// Show success notification
showNotification({ type: 'success', text: 'User blocked' })
} catch (error) {
console.error('Error blocking user:', error)
showNotification({ type: 'error', text: 'Failed to block user' })
}
}Filter Blocked Users from Queries โ
// In any service fetching users/waves/lanterns
export async function getActiveLanterns(userId) {
const blockedUsers = await getBlockedUserIds(userId)
const lanternsRef = collection(db, 'lanterns')
const q = query(
lanternsRef,
where('status', '==', 'active'),
where('userId', 'not-in', blockedUsers) // Firestore supports up to 30 items in not-in
)
// Note: If >30 blocked users, perform client-side filtering:
const allLanterns = await getDocs(q)
return allLanterns.docs
.map(doc => ({ id: doc.id, ...doc.data() }))
.filter(lantern => !blockedUsers.includes(lantern.userId))
}Resources โ
- Full Design Document: SAFETY_MECHANICS.md
- TODO List: docs/TODO.md (search for "Safety Mechanics")
- User Security Guide: USER_SECURITY_GUIDE.md
- Security Architecture: SECURITY_ARCHITECTURE.md
Questions or Issues? โ
If you encounter problems implementing safety features:
- Review SAFETY_MECHANICS.md for detailed requirements
- Check existing security patterns in
src/lib/services - Consult Firebase documentation for Firestore security rules
- For legal questions (recording consent, liability), consult legal team
Remember: Safety features are mission-critical. Take time to implement correctly, test thoroughly, and prioritize user protection over speed.