← Back to Portfolio

NomadCrew

Group travel coordination, rebuilt from the ground up.

692+

Source Files

65+

API Endpoints

22

Database Tables

604

Test Functions

40+

Event Types

10

Integrations

Five Apps, Three Time Zones, Zero Shared Context

Every group trip starts the same way. Someone creates a WhatsApp group. Someone else shares a Google Sheet. A third person downloads Splitwise. Within 48 hours, the planning context is scattered across five apps, three time zones, and zero shared understanding.

The failure mode is not a lack of tools — it is a surplus of disconnected ones. Conversations happen in one app. Budgets live in another. Location sharing requires a third. No single surface holds the full picture, and decisions stall because context is always somewhere else.

Key challenges:

  • Fragmented tooling — Chat in WhatsApp, budgets in Splitwise, itineraries in Google Sheets, maps via shared links. Every tool holds a fragment; none holds the truth.

  • Coordination across time zones — Asynchronous planning with no shared state means someone is always out of the loop.

  • Expense opacity — Who paid for what, who owes whom, and how to split unevenly across subgroups is a recurring source of friction.

  • No trip lifecycle — Existing tools treat a trip as a static document. In reality, a trip moves through phases: creation, invites, active planning, live coordination, and post-trip settlement.

  • Permission complexity — Group dynamics are not flat. Admins, members, and guests need different levels of access — and those permissions shift as the trip evolves.

A Single Source of Truth for Every Trip

Not a booking engine. Not a social feed. Not another travel planner. A structured, real-time workspace where every participant sees the same trip state, at all times, from any time zone.

The platform needed to:

  • Manage the full trip lifecycle from creation through post-trip settlement
  • Support real-time chat with typing indicators and read receipts
  • Enable live location sharing with database-enforced privacy levels
  • Track and split expenses with penny-perfect accuracy — no rounding errors
  • Handle multi-party permissions across 9 resource types and 8 action types
  • Ship cross-platform on iOS and Android from a single codebase
  • Deliver sub-second updates for chat, location, and trip state changes

Built for the Messy Reality of Group Travel

A mobile-first experience across 12 feature modules — from trip creation through settlement.

NomadCrew Trip List screen

Trip List

Every active, upcoming, and completed trip in one view. Status badges show trip phase at a glance.

NomadCrew Live Map screen

Live Map

Real-time location sharing across all trip members on a single Google Maps integration.

NomadCrew Invite Member screen

Invite Member

Streamlined invite flow with role-based permissions assigned automatically on acceptance.

NomadCrew Welcome screen

Welcome

Google and Apple Sign-In with secure token storage and preemptive refresh.

NomadCrew Onboarding screen

Onboarding

First-run experience that sets context before the first trip is created.

NomadCrew Setup screen

Setup

Identity setup that feeds into the permission and notification systems.

Every Technology Choice Serves a Constraint

Two pillars: a Go backend optimized for concurrent, real-time workloads and a React Native + Expo client that ships to both platforms from one codebase. The architecture is deliberately opinionated.

React NativeMobile Client
Go API ServerREST + WS
PostgreSQLRelational Data
RedisCache + Pub/Sub
WebSocketsReal-time Sync
AWS S3Object Storage
BackendGo 1.24 + Gin

Goroutines and channels handle thousands of simultaneous WebSocket connections with minimal overhead

MobileReact Native + Expo

Single codebase for iOS and Android with native performance where it matters

DatabasePostgreSQL

Enforces referential integrity across trips, members, expenses, and permissions at the schema level

CacheRedis (Upstash)

Hot-path caching and real-time event distribution between service boundaries

Real-TimeWebSockets

Persistent connections for instant chat, location updates, and trip state synchronization

StorageCloudflare R2

Object storage for trip media with HMAC-signed download URLs and magic-byte MIME detection

AuthSupabase + CASL

Matrix-based permission model mapping users, roles, trip phases, and resource types

Hard Problems, Solved

Group coordination is a fundamentally concurrent problem. These are the engineering decisions that made it work.

Penny-Perfect Expense Splitting

Money stored in minor units (cents as BIGINT). The go-money library handles distribution, and a greedy debt simplification algorithm minimizes the number of settlement transactions. Three split types — EQUAL, EXACT, and PERCENTAGE — all resolve without rounding errors.

Privacy at the Database Level

A PostgreSQL function round_coordinates() enforces three privacy levels — hidden, approximate (~1.1km), and precise. The application layer cannot bypass it. Privacy is not a feature flag; it is a schema constraint.

WebSocket Hub with Fine-Grained Concurrency

Each connection runs three goroutines (read, write, ping) coordinated via context cancellation. A sync.RWMutex guards the connection map. Buffered channels (256) with graceful overflow — drops events rather than blocking the hub.

iOS SecureStore Token Chunking

iOS limits SecureStore entries to 2,048 bytes. JWT tokens exceed this. The frontend implements a chunking adapter that splits tokens across multiple entries and reassembles transparently — combined with preemptive refresh and a 401 queue for concurrent requests.

Type-Safe Data Access via SQLC

15 generated query files covering all domain entities. SQL injection and type mismatch bugs are caught at compile time, not runtime. The database is the authority, not the code.

Zero-Budget Production Infrastructure

Oracle Cloud free tier (4 OCPU, 24GB ARM), Coolify for orchestration, Supabase free tier for auth, Cloudflare R2 for storage. Production-grade deployment at near-zero operational cost.

What Makes This Different

Matrix-based authorization, not flat RBAC

The permission model evaluates access across four dimensions — user identity, trip phase, resource type, and relationship context. An admin’s permissions during planning differ from their permissions during an active trip, automatically.

Real-time everything

Chat, location, expenses, and trip state all flow through the same WebSocket infrastructure. No polling. No stale reads. Every participant sees the same truth, at the same time.

The planning phase is the product

Most travel apps focus on booking or during-trip features. NomadCrew treats the weeks of group coordination before departure as the primary use case — because that is where trips succeed or fail.

Go for concurrency, not convention

The backend was built in Go because group coordination is a fundamentally concurrent problem. Goroutines and channels handle this naturally, without thread-management overhead.

Not a chat app. Not a shared spreadsheet. Not another travel planner.

The best group trip is the one where nobody gets left behind — in the planning or on the ground.