← Back to projects
Summary

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.

Stack

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
Details

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.

Roadmap

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.

  1. 01

    Hands-off coverage (map-matching)

    Upcoming

    Snap 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.

  2. 02

    Background location

    Upcoming

    An expo-location background task that keeps logging positions with the app in the user's pocket — the core 'it just works' field experience.

  3. 03

    On-device model deployment

    Upcoming

    Ship the trained verification model to the phone via Core ML for private, low-latency scoring of activity as it happens.

Highlights

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.