Live at cofly-gray.vercel.app
CoFly connects verified travelers on the same flight so they can share rides, coordinate travel plans, and travel smarter — safely and on their own terms.
CoFly is a connection platform. It does not broker, manage, or guarantee any offline arrangement between users.
Note: This is a personal project and not a product. It is an open-source prototype built for learning and experimentation. You are welcome to test the live deployment and open an issue if you notice anything not working.
- Flight-matched discovery — Search by flight number and date to find co-travelers
- Verified identities — Optional phone OTP and government ID verification; unverified users can still post but their posts are clearly marked
- Multiple intents — Open to connecting, carpooling to/from airport
- Safety-first chat — Messaging opens only after mutual approval, with pinned safety reminders
- Reporting & moderation — In-app reporting with reason categories and admin review pipeline
- Privacy by default — Poster identity is hidden until a connection request is accepted
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, Turbopack) |
| Language | TypeScript |
| Auth & DB | Supabase (Postgres, Auth, Realtime, Storage) |
| UI | Tailwind CSS v4, Base UI (shadcn-style components) |
| Validation | Zod 4 |
| Notifications | Sonner |
- Node.js 20+
- A Supabase project (free tier works)
git clone https://github.com/sufiyan-ahmed/cofly.git
cd cofly
npm installcp .env.local.example .env.localFill in your Supabase project URL and anon key in .env.local:
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Only needed for cron endpoint
# CRON_SECRET=your-secure-random-token
Run the combined setup script in the Supabase SQL Editor:
supabase/setup.sql
This creates all tables, enums, triggers, indexes, RLS policies, and enables Realtime on the messages table.
Storage: Create a private bucket named
id-documentsin Supabase Storage for ID verification uploads.
In your Supabase dashboard under Authentication → Providers:
- Email — Enabled by default
- Google OAuth — Add your Google Cloud OAuth client ID and secret
- Phone (OTP) — Enable and configure an SMS provider (optional — users can post without phone verification, but their posts will be marked as unverified)
Set the site URL and redirect URLs:
Site URL: http://localhost:3000
Redirect URLs: http://localhost:3000/auth/callback
npm run devOpen http://localhost:3000.
src/
├── app/
│ ├── (auth)/ # Login, register, phone/ID verification
│ ├── (dashboard)/ # Authenticated app — dashboard, posts, connections, profile
│ ├── (marketing)/ # Public pages — landing, how it works, terms, trust & safety
│ ├── api/cron/ # Cron endpoint to expire stale posts
│ └── auth/callback/ # OAuth & email verification callback
├── components/
│ ├── profile/ # Verification badge, email resend action
│ ├── shared/ # Navbar, footer, logo
│ └── ui/ # Base UI component primitives (shadcn-style)
├── lib/
│ ├── constants.ts # Intents, airports, airlines, disclaimers
│ ├── validators.ts # Zod schemas for posts, declarations, reports
│ ├── utils.ts # cn() helper
│ └── supabase/ # Client, server, middleware helpers, types
supabase/
├── setup.sql # Combined one-shot DB setup
└── migrations/ # Individual migration files (001–009)
The supabase/migrations/ directory contains numbered migration files for reference and incremental upgrades:
| Migration | Purpose |
|---|---|
| 001 | Profiles table with auto-create trigger |
| 002 | Traveler posts with intent types and expiry |
| 003 | Baggage declarations |
| 004 | Connection requests with duplicate prevention |
| 005 | Messages with Realtime enabled |
| 006 | Acknowledgments (immutable consent records) |
| 007 | Reports and moderation actions |
| 008 | Row Level Security policies for all tables |
| 009 | Schema updates — departure time, optional baggage context, carpool intents |
| 010 | Bug fixes — RLS hardening, connection expiry, blocked user enforcement |
| 011 | Relax phone verification — allow unverified users to post and connect |
The /api/cron/expire-posts endpoint marks posts as expired after their flight date passes. Call it via Vercel Cron or any external scheduler:
GET /api/cron/expire-posts
Authorization: Bearer <CRON_SECRET>
Set CRON_SECRET and SUPABASE_SERVICE_ROLE_KEY in your environment for this to work.
MIT — see LICENSE for details.