Location Data
This document outlines the final design, enforcement rules, and technical implementation of location-based features in RABS. It is intended for use in development, internal documentation, and for drafting the Terms of Use / Privacy Policy.
1. Purpose
RABS collects location data from staff and participants to ensure safety, accountability, and operational accuracy. Location collection is role-specific, shift-aware, and limited to business purposes.
Core Use Cases
- Staff Clock-in/Clock-out Validation → verify geo-fence compliance.
- Transport/Route Tracking → detect missed stops or off-route deviations.
- Excursion Safety → live visibility of staff and participant trackers.
- Admin Remote Work → accountability for offsite operations.
2. Location Collection Policy
Role Matrix
| Role | Location Collection | Enforcement |
|---|---|---|
| Support Staff | During rostered shifts only | Required for clock-in/out & transport |
| Admin | Whenever logged in | Required for full system access |
| Manager | Whenever logged in | Required for full system access |
| Participants | Not continuous | Only via dedicated trackers |
| Carers/Public | Optional (tooltip only) | May reject without blocking access |
Behavior
- Support Staff → Only collect during rostered shifts. Outside shifts = no collection.
- Admin/Managers → Collect whenever logged in. Required for access.
- Participants → Not tracked by phone; instead assigned GPS lanyards/trackers.
- Carers/Public → Can reject location requests without impact.
Enforcement
- If staff deny location → session revoked + modal instructing them to contact office.
- Persistent refusal → refresh token revoked (cannot re-login without admin intervention).
- Staff are informed via banner: “Your location is collected only during rostered work (support staff) or while signed in (admin/manager).”
3. System Design
Database Tables
CREATE TABLE location_policy (
role TEXT PRIMARY KEY, -- staff_support, admin, manager
mode TEXT NOT NULL, -- shift_only | session_active
freshness_sec INT NOT NULL, -- e.g., 300
ping_interval_sec INT NOT NULL -- e.g., 300
);
CREATE TABLE staff_last_location (
staff_id INT PRIMARY KEY,
ts TIMESTAMPTZ NOT NULL,
lat DOUBLE PRECISION NOT NULL,
lng DOUBLE PRECISION NOT NULL,
accuracy DOUBLE PRECISION,
source TEXT NOT NULL, -- 'web','app','device'
shift_id INT,
hash TEXT -- integrity field
);
CREATE TABLE geo_events (
id BIGSERIAL PRIMARY KEY,
subject_type TEXT NOT NULL, -- staff, participant, device
subject_id INT NOT NULL,
ts TIMESTAMPTZ NOT NULL,
lat DOUBLE PRECISION NOT NULL,
lng DOUBLE PRECISION NOT NULL,
accuracy DOUBLE PRECISION,
speed DOUBLE PRECISION,
event_type TEXT, -- position, clock_in, clock_out, geofence_enter, alarm
meta JSONB DEFAULT '{}'::jsonb
);
Session Policy Flow
-
Login → role check
- If staff: require location challenge.
- If admin/manager: always enforce location.
- If non-staff: optional prompt.
-
Location challenge
- Use browser Geolocation API (
enableHighAccuracy:true). - On success → session upgraded with
loc_ok_untilclaim. - On denial → revoke provisional token + show modal.
- Use browser Geolocation API (
-
During session
- Support staff: location updates only when shift active.
- Admin/manager: updates whenever session active.
- Ping interval defined in
location_policy.
-
Middleware enforcement
- Protected endpoints (clock in/out, transport, excursion ops) check for fresh geo event ≤ freshness window.
- If stale/missing →
401 LOCATION_REQUIRED→ client re-prompts.
4. Backend Examples
Express Middleware
async function requireFreshLocation(req, res, next) {
const pol = await getLocationPolicy(req.user.role);
const enforce = (pol.mode === 'session_active') ||
(pol.mode === 'shift_only' && await isShiftActive(req.user.id));
if (!enforce) return next();
const last = await getLastLocation(req.user.id);
const fresh = last && (Date.now() - new Date(last.ts)) / 1000 <= pol.freshness_sec;
if (!fresh) return res.status(401).json({ code:'LOCATION_REQUIRED' });
next();
}
app.post('/api/geo/report', auth(), async (req,res) => {
const { lat, lng, accuracy, shiftId } = req.body;
await insertGeoEvent({ subject_type:'staff', subject_id:req.user.id, ts:new Date(), lat, lng, accuracy, event_type:'position', meta:{shiftId, source:'web'} });
await upsertLastLocation({ staff_id:req.user.id, ts:new Date(), lat, lng, accuracy, source:'web', shift_id:shiftId });
res.json({ ok:true });
});
Shift-aware Enforcement
async function isShiftActive(userId, now=new Date()) {
return !!(await getActiveShift(userId, now));
}
5. Frontend Examples
Login Enforcement
async function enforceStaffLocation() {
return new Promise<void>((resolve, reject) => {
navigator.geolocation.getCurrentPosition(async pos => {
await fetch('/api/geo/report', {
method:'POST',
headers:{'Content-Type':'application/json', 'Authorization':bearer()},
body: JSON.stringify({ lat: pos.coords.latitude, lng: pos.coords.longitude, accuracy: pos.coords.accuracy })
});
resolve();
}, err => {
logoutAndShowModal('Location is required for staff. Please enable and contact the office.');
reject(err);
}, { enableHighAccuracy:true, maximumAge:0, timeout:8000 });
});
}
Policy Application
function applyPolicy(p:LocPolicy){
const shouldTrack =
p.mode === 'session_active' ? true :
p.mode === 'shift_only' ? p.shift.active : false;
shouldTrack ? startGeo(p) : stopGeo();
}
6. Map Display
- Each staff pin shows Name + Local Time.
- Local time resolved by server → IANA timezone → client renders with
Intl.DateTimeFormat.
Example: “Brett Watson • Tue 11:15pm (Asia/Singapore)”
7. Privacy & Compliance
- Consent: Written consent required for staff and participant tracking.
- Purpose limitation: Only during rostered work (support staff) or while logged in (admin/manager).
- Retention: Keep detailed trails for 30–90 days, then aggregate.
- Access control: Only excursion staff and managers may view participant/staff location.
- Audit logs: Record who accessed location data.
- Kiosk exception: Fixed terminals at sites may clock staff without GPS (IP allowlist or BLE beacon).
8. Implementation Roadmap
- Add DB schema (
location_policy,staff_last_location,geo_events). - Add
/api/geo/reportendpoint + middleware. - Add login-time location challenge.
- Add role-aware enforcement for protected ops.
- Add map tooltip with local time.
- Update Terms of Use / Privacy Policy.
9. Future Enhancements
- Participant GPS lanyards integrated via Traccar.
- Geofence alerts (leave venue, SOS, off-route transport).
- WebSocket live map updates.
- Offline caching for poor reception excursions.
Document: location_data.md