Skip to main content

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

RoleLocation CollectionEnforcement
Support StaffDuring rostered shifts onlyRequired for clock-in/out & transport
AdminWhenever logged inRequired for full system access
ManagerWhenever logged inRequired for full system access
ParticipantsNot continuousOnly via dedicated trackers
Carers/PublicOptional (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

  1. Login → role check

    • If staff: require location challenge.
    • If admin/manager: always enforce location.
    • If non-staff: optional prompt.
  2. Location challenge

    • Use browser Geolocation API (enableHighAccuracy:true).
    • On success → session upgraded with loc_ok_until claim.
    • On denial → revoke provisional token + show modal.
  3. During session

    • Support staff: location updates only when shift active.
    • Admin/manager: updates whenever session active.
    • Ping interval defined in location_policy.
  4. 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

  1. Add DB schema (location_policy, staff_last_location, geo_events).
  2. Add /api/geo/report endpoint + middleware.
  3. Add login-time location challenge.
  4. Add role-aware enforcement for protected ops.
  5. Add map tooltip with local time.
  6. 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