๐Ÿ›ก Safe2Go โ€” Safety Logic

โ† back to app

Admin Login

What this page is. Every safety feature in Safe2Go: what happens and when, what's sent to whom, and what is and isn't live yet. Written from the code in server/services/safetyEngine.js, server/routes/safety.js and the Safety Mode screens in public/index.html. Last updated 2026-09-26 (commits ad335ab, 2892a10).
Status labels: LIVE working now ยท NEEDS SETUP built, waiting on an account/config ยท NOT YET not built

1. The whole flow

flowchart TD
  A["User sets up Safety Profile:
up to 5 emergency contacts + 4-digit safety code"] --> B["Starts a journey and turns on SAFETY MODE"] B --> C["Server opens a safety session
(route, legs, live-location link, local police number)"] C --> D{"Off the planned route?
more than 500 m for 60 s
(phone screen OR background GPS)"} D -- no --> D D -- yes --> P1["ARE YOU SAFE? โ€” check 1 of 3
30 seconds"] P1 --> Q1{"Code entered?"} Q1 -- "correct code" --> SAFE["You're Safe โœ…
back to monitoring"] Q1 -- "reverse (duress) code" --> DUR["Screen shows 'You're Safe' โœ…
BUT everyone is alerted immediately"] Q1 -- "no code in 30 s" --> P2["Check 2 of 3 โ€” 30 seconds"] P2 --> Q2{"Code entered?"} Q2 -- correct --> SAFE Q2 -- duress --> DUR Q2 -- "no code in 30 s" --> P3["Check 3 of 3 โ€” FINAL โ€” 30 seconds"] P3 --> Q3{"Code entered?"} Q3 -- correct --> SAFE Q3 -- duress --> DUR Q3 -- "no code in 30 s" --> ESC["๐Ÿ†˜ ESCALATE (after 90 s)"] DUR --> ESCD["๐Ÿ†˜ ESCALATE silently"] ESC --> OUT["Every contact: email + SMS + automated phone call
with last location, live-tracking link,
and 'call the police (local number)'"] ESCD --> OUT ESC --> SCR["User's screen: EMERGENCY ALERT SENT
๐Ÿ“ž Call police button + ๐Ÿ“ your location"] SCR --> AC{"User enters correct code later?"} AC -- yes --> ALL["All contacts told:
'they have confirmed they are safe'"]
The timer runs on the server, not on the phone. The checks and the escalation still happen if the phone is locked, the app is closed, the battery dies or the phone is taken away.

2. Feature list

#FeatureHow it worksStatus
1Safety ProfileHome, office and 2 safe places, up to 5 emergency contacts (name + phone with country code and/or email), and a 4-digit safety code. Palindrome codes (e.g. 1221) are refused, because their reverse would be the same code.LIVE
2Safety Mode (opt-in)Turned on by the user during a journey. Needs a profile with at least one contact. Turning it off, or finishing the journey, closes the session.LIVE
3Off-route detectionMore than 500 m from the planned route for 60 s. Checked on the phone screen, and on the server from the app's background GPS reports (every 30 s). Flight legs are ignored.LIVE
4"Are you safe?" โ€” 3 checks ร— 30 sCheck 1 โ†’ 30 s โ†’ check 2 โ†’ 30 s โ†’ check 3 (FINAL) โ†’ 30 s โ†’ escalation. Each check shows its number and a countdown, with a beep and vibration (Android). Repeated off-route signals never restart or delay a running cycle.LIVE
5Correct code"You're Safe!" and back to monitoring. After an escalation, all contacts get an all-clear message.LIVE
6Wrong code"Wrong safety code. Try again." It does not pause the 30-second windows.LIVE
7Duress (reverse) codeE.g. 1234 โ†’ 4321. See section 3.LIVE
8Server-side timerRuns every 5 s on the server, so escalation never depends on the phone being on or in hand.LIVE
9Escalation to all contactsEmail + SMS + automated voice call to every contact. See section 4.Email LIVE SMS/calls NEED TWILIO
10Police numbers by locationLocal police number (and general emergency number where different), from the user's latest location. See section 5.LIVE
11Location in every alertAddress + GPS + how long ago + Google Maps link + live-tracking link. See section 6.LIVE
12"Alert Emergency Contacts Now"Manual button on the alert screen, which escalates immediately.LIVE
13๐Ÿ“ž Call police buttonAlways on the alert screen, one tap to the local police number. It shows a second button for ambulance/fire when that number differs.LIVE
14"Your location (tell the police)" boxThe alert screen shows the user's current address and GPS so they can read it to the operator.LIVE
15Text/Call contact buttonsShown on the emergency screen only while SMS/calls aren't set up, so the user can reach phone contacts by hand.LIVE
16Event logEvery escalation and all-clear is recorded (safety_events): reason, location, and delivery result per contact and channel.LIVE

3. Duress code

flowchart LR
  A["Someone forces the user
to say they're safe"] --> B["User enters the code
REVERSED, e.g. 4321"] B --> C["Screen: 'You're Safe!' โœ…
identical to the real code"] B --> D["Session looks normal
(nothing visible changes)"] B --> E["Server immediately alerts ALL contacts:
email + SMS + call
'URGENT โ€” may be in danger, forced to appear safe'
+ location + call the police"]
Nothing on the screen gives it away. The person watching sees exactly what the real code shows.

4. Escalation โ€” who gets what

TriggerMessage opening
No answer to 3 checks"SAFETY ALERT: name went off their planned route on Safe2Go and did not confirm they are safe after 3 checks."
Duress code"URGENT: name entered their DURESS code on Safe2Go. They may be in danger and forced to appear safe."
Manual button"SAFETY ALERT: name pressed the emergency button on Safe2Go."
Correct code after an alert"UPDATE: name has now confirmed with their safety code that they are safe." (email + SMS, no call)
ChannelSent toContainsStatus
๐Ÿ“ง EmailEvery contact with an emailAlert, action ("call them now; if you can't reach them, call the police โ€ฆ"), time, route, last address + GPS + how long ago, Google Maps button, live-location buttonLIVE (via the server's Gmail account)
๐Ÿ’ฌ SMSEvery contact with a phone numberSame content as one text, with the Maps and live-tracking linksNEEDS TWILIO
๐Ÿ“ž Automated voice callEvery contact with a phone numberAlert read out twice, including the address and the GPS coordinates read digit by digit, plus the police numberNEEDS TWILIO
๐Ÿš“ Policeโ€”Automated calls to 112/911/100 are not permitted in most countries. Instead: every contact is told to call the police with the location, the user has a one-tap police button, and the location is shown on screen to read out.LIVE

5. Police numbers โ€” from the user's latest location

flowchart TD
  G["User's latest GPS position"] --> C{"Country lookup
(mobility_graph DB)"} C --> B["Country boundary polygons
(authoritative where available)"] C --> P["Nearest place within 15 km
(covers small countries: SG, MT, MC, BH, HKโ€ฆ)"] B --> ISO["Country code, e.g. DE"] P --> ISO ISO --> T["Police table โ†’ police 110 / emergency 112"] T --> W["Refreshed when moved more than 10 km or every 10 min,
and looked up FRESH at the moment of any alert"] C -- "no country (e.g. at sea)" --> F["Fallback 112
(works from any mobile phone in most countries)"]
Examples (verified 2026-09-26):
LocationCountryPoliceGeneral emergency
MannheimDE110112
BaselCH117112
StrasbourgFR17112
SalzburgAT133112
DelhiIN112112
KathmanduNP100100
SingaporeSG999999
DubaiAE999999
LondonGB999999
New YorkUS911911
SydneyAU000000
Sรฃo PauloBR190190
Mid-Atlanticโ€”112112
Moving-user test: journey started in Mannheim (110) โ†’ phone update from Basel (117) โ†’ background update from Strasbourg (17) โ†’ alert sent from Salzburg used 133.
Border limitation: within a few km of a border, the low-resolution boundary data can pick the neighbouring country (seen: Weil am Rhein, Germany, read as Switzerland). Both numbers are shown where they differ, and 112 is always a working fallback on mobile phones.

6. Location sharing

WhereWhat location is shared
SMS"Last location (N min ago): address โ€” GPS lat, lng" + Google Maps link + live-tracking link
EmailAddress, GPS, how long ago, Google Maps button, "Follow live location" button
Voice callAddress read out, GPS read digit by digit (e.g. "4 9 point 4 8 7 3 3"), and a note that the links were sent by text and email
Live tracking/live-track.html?token=โ€ฆ, a private link per session, kept current from every GPS update
User's own screen"๐Ÿ“ Your location (tell the police)": address + GPS, refreshed as they move
"Last location" is the latest GPS position the server received: from the phone screen, or from the app's background reports every 30 s.

7. Phone apps (Android / iOS)

The store apps are a wrapper around the live website (mobile_native/index.js โ†’ AppWebView). Everything on the safety screens is therefore the same in both apps, with no store update needed.
ItemAndroidiOS
All safety screens (checks, countdown, police button, location box, contacts)LIVELIVE
๐Ÿ“ž / ๐Ÿ’ฌ buttons open the dialer / SMS appLIVELIVE
Background GPS reports โ†’ server off-route check + escalationLIVEnot yet confirmed on a real iPhone
Vibration on each checkshould worknot possible from the web page
"Are you safe?" pop-up while the app is closed / phone lockedNOT YETNOT YET
No lock-screen pop-up yet: the apps have no notifications, so a user with the app in the background sees the check only when they open it. The server still escalates after 90 s. Fixing this needs a native update (local/push notification with an alarm sound) and a store release on both platforms.

8. Setup & open items

#ItemWhat's needed
1SMS + automated callsA Twilio account and one phone number. In Railway set TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_FROM_NUMBER. No code change needed; it switches on automatically.
2Confirm email alerts in productionOne real test: a profile with your own email, Safety Mode on, press "Alert Emergency Contacts Now".
3Lock-screen pop-upNative notification in the Android/iOS wrapper + store releases.
4Road-accurate off-route checkToday off-route is measured against straight lines between stops, so winding roads can cause false prompts. Needs road shapes on routes (part of the TO-BE plan).
5Real-device testSafety flow on a physical Android phone and iPhone.

9. How it was tested (2026-09-26)

10. Technical reference

PieceWhere
Engine (sessions, timer, escalation, police lookup, messages)server/services/safetyEngine.js
APIserver/routes/safety.js: /profile, /verify-code, /emergency, /session/start, /session/stop, /session, /session/ping, /session/checkin, /config
Background GPS from the appsPOST /api/journey/gps-ping (server/routes/journey.js)
Country lookupgetCountryIsoForCoordinate() in server/services/mobilityGraphQuery.js (geo.country_boundary + geo.place)
Live trackingserver/routes/track.js + public/live-track.html
DataMongoDB: safety_profiles (contacts, bcrypt-hashed code), safety_sessions, safety_events
Screenspublic/index.html: Safety Profile modal, Safety Mode button, alert overlay
External servicesGmail SMTP (email), Twilio (SMS + calls, when configured), reverse geocoding for the address
Written from the source code and test runs on 2026-09-26. Update this page by hand when the safety logic changes.