Turf is a standalone, multi-tenant mobile platform for teams doing coordinated work out in the field. The engineering problem I care most about is verification — given only a phone in someone's pocket, can you tell genuine on-foot activity apart from someone who drove, biked, or spoofed their GPS? I designed and built the whole system solo: the React Native field app with live GPS tracking, the PostGIS-backed Supabase backend with row-level tenant isolation, and the on-device sensor-fusion ML model (detailed in a companion entry) that scores whether activity really happened on foot.
What it's built with.
Mobile
- React Native / Expo
- expo-router (typed routes)
- Live GPS Tracking
- Map Rendering
Backend & Data
- PostgreSQL / Supabase
- PostGIS (geospatial)
- Row-Level Security
- SECURITY DEFINER RPCs
Machine Learning
- Sensor Fusion
- On-Device Inference (Core ML)
- Time-Series Classification
- Server-Enforced Plan Limits
How it works.
Live field tracking
Crew start a shift and the app streams GPS breadcrumbs into PostGIS in real time, drawing a live trail on the map with a follow-camera and a running timer / distance readout. The raw track is the substrate everything else builds on — the input to coverage rendering and to the verification model alike.
Positions are org-scoped and crew-writable under row-level security: a walker can write their own breadcrumbs but never touch another org's data. It's built to keep logging reliably from a phone in someone's pocket out in the field.
Architecture — standalone, multi-tenant, isolated
Turf is its own product with its own dedicated Supabase project, generic and multi-tenant from the start: an org owns members (crew with roles) who run projects, cut into routes, worked in shifts, and every row carries org_id for row-level-security isolation.
The RLS model is deny-by-default and keyed on two SECURITY DEFINER helper functions that resolve the caller's org memberships. Every org-scoped table gets a member-readable select policy plus an admin-only write policy; crew-writable tables like positions and shifts carry extra self-scoped policies so a walker can write their own breadcrumbs but not another org's. Any new table has to follow the same pattern or it's invisible and locked by default — the safe failure mode.
On-device verification
A GPS track alone is trivial to fake, so verification leans on the sensors that are hard to fake together. The app fuses GPS with the pedometer, accelerometer/gyroscope, and barometer into one feature stream for an on-device model that scores whether activity was genuinely performed on foot.
The model, its sensor signals, and the training-data strategy are covered in the companion Activity Verification ML entry. Keeping inference on-device keeps the raw sensor stream private to the phone.
Where it's headed.
Engineering roadmap
The core — org/crew model, plan gating, live shift tracking, and the multi-tenant geospatial backend — is built end to end. These are the forward phases.
- 01
Hands-off coverage (map-matching)
UpcomingSnap raw GPS breadcrumbs to the street network (OSRM / Valhalla over OpenStreetMap) so coverage reads as 'walked the north side of Maple between 3rd and 4th,' not a cloud of points.
- 02
Background location
UpcomingAn expo-location background task that keeps logging positions with the app in the user's pocket — the core 'it just works' field experience.
- 03
On-device model deployment
UpcomingShip the trained verification model to the phone via Core ML for private, low-latency scoring of activity as it happens.
The things I'm proudest of.
- ▹Built the field app in React Native (Expo / TypeScript): crew start a shift and the app streams live GPS breadcrumbs into PostGIS as they move, drawing a live trail on the map with a camera that follows the walker and a running timer / distance readout.
- ▹Fuses the phone's sensors — GPS, pedometer (steps + cadence), accelerometer/gyroscope (gait + heading), barometer (elevation) — into a feature stream for an on-device verification model, so the system sees far more than a GPS track that's trivial to fake.
- ▹Designed a multi-tenant PostgreSQL + PostGIS schema (orgs, members, projects, routes, shifts, positions) with row-level-security tenant isolation enforced through SECURITY DEFINER helper functions — every org-scoped table carries org_id and a member-select + admin-write policy pair.
- ▹Enforced plan limits at the API, not the client: seat and active-project caps are checked inside the accept_invite RPC at invite-accept time, with limits and feature flags data-driven in a plan_limits table — a client can't exceed a cap by calling the API directly.
- ▹Generated typed DB bindings end to end (createClient<Database>) and kept tsc --noEmit clean across the app; auth is a session-aware AuthProvider with a route guard redirecting between the auth and app stacks.