Project Info
This project did not submit a demo video on Devpost.
Inspiration
We noticed that whenever there were off-campus club activities, there was always a large number of people in need of rides to get to the venue. Event organizers would do their best to assign drivers for each rider through messy group chats, but the process was often manual, time-consuming, and inefficient. Many times, drivers had to take longer detours to pick up riders who other nearby drivers could have picked up. This led to unnecessary delays and confusion, which often resulted in frustration for both drivers and passengers. With VroomMates, we wanted to centralize and automate the planning of carpools by creating a solution that optimizes rider-driver assignments. The goal was to make the process of organizing rides easier and more efficient for both event organizers and participants. The idea of using technology to solve this common issue inspired us to create a tool that not only streamlines the ride-matching process but also minimizes detours and travel time, making it a win-win for everyone involved.
What it does
VroomMates is a carpool management system that centralizes the process of organizing carpools for events. It allows users to register as drivers or riders, input their locations and destinations, and automatically match riders with drivers based on optimal routes. VroomMates uses A* for pathfinding and linear distance using the Haversine Formula to cluster the drivers. This reduces unnecessary detours and streamlines transportation logistics.
How we built it
VroomMates was built using a full-stack approach. The front end was developed with React, leveraging the Google Maps API for location input, tracking, and map displays. The back end was powered by Flask, where Python handled the clustering algorithms to generate optimized driver routes. These routes were stored in Google Datastore, allowing users to save and resume their sessions across devices. We implemented continuous deployment with GitHub Actions and hosted the app on Vercel to streamline development and updates.
Challenges we ran into
One of the biggest challenges we encountered was creating the Post and Get functions for Flask. Since we modularized the entire project and everybody worked on different parts simultaneously, we saved the Flask implementation for the very last. Even though we were sure that all the parts worked independently, connecting the backend operations to display on the front end proved to be messier than we anticipated.
Accomplishments we're proud of
We are proud that our program works.
What we learned
The hackathon prize is the friends we made along the way.
What's next
Add functionality for live contribution between drivers and riders for the same map. Support the ability to add a desired arrival time and VroomMates will give routes that takes current traffic conditions into consideration. Oh, and IPO as a trillion-dollar company.
VroomMates
Carpool webapp — a React + Flask application that groups passengers with drivers heading to a shared destination and suggests an optimized route for each driver.
The repo contains both the frontend (React, in src/ and public/) and the
backend (Flask, in app.py and backend/) in a single project.
Prerequisites
You will need the following installed locally:
- Node.js 24.x — https://nodejs.org/
- npm (ships with Node.js) — verify with
npm --version - Python 3.10 or newer — https://www.python.org/downloads/
- pip (ships with Python) — verify with
pip --version
On macOS and Linux, use
python3to create the virtual environment. On Windows, usepy -3. After activation, usepythonandpython -m pip.
1. Clone and enter the repo
git clone https://github.com/jonnypan2325/VroomMates.git
cd VroomMates
2. Create your .env file
Copy the template and fill in your own keys:
cp .env.example .env
Then open .env and replace the placeholder values. See
Getting the Google credentials below.
3. Install Python dependencies
Create and activate a virtual environment for the backend:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txt
On Windows (PowerShell):
py -3 -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install -r requirements.txt
The .venv/ directory is already listed in .gitignore.
Activate it in each new terminal before running backend commands or npm run dev.
The npm scripts use python, which activation resolves to the virtual environment.
4. Install Node dependencies
npm install
This installs both the React app and the dev tooling (including
concurrently, which the dev
script below uses to run the frontend and backend together).
5. Run the app
Activate the virtual environment from step 3, then choose a startup option.
Saving trips and locations needs Firestore. For local development, start the
Firestore emulator in its own terminal first; the .env.example values point the
backend at it, and no Google credentials are needed. Saved data is lost when the
emulator stops. It needs the Firebase CLI
and Java.
firebase emulators:start --only firestore --project demo-vroommates
Option A — run both servers together (recommended)
npm run dev
This uses concurrently to start the Flask backend (python app.py) and the
React dev server (react-scripts start) in a single terminal. Output from each
process is prefixed with backend or frontend.
Option B — run each server in its own terminal
In one terminal, activate the virtual environment and start the backend:
source .venv/bin/activate
npm run backend
On Windows, activate with .venv\Scripts\Activate.ps1 instead.
Running python app.py directly starts the same development server.
In a second terminal, start the frontend:
npm start
Default ports
| Service | URL |
|---|---|
| Frontend | http://localhost:3000 |
| Backend | http://localhost:5000 |
The frontend talks to the backend over HTTP, so both servers must be running at the same time for the app to work end-to-end. If you start only the frontend, the UI will load but route optimization requests will fail.
Getting the Google credentials
Google OAuth Client ID (REACT_APP_GOOGLE_CLIENT_ID)
- Go to the Google Cloud Console — Credentials page.
- Create (or select) a project.
- Click Create Credentials → OAuth client ID.
- If prompted, configure the OAuth consent screen first (External, just fill in the required fields).
- Choose Web application as the application type.
- Under Authorized JavaScript origins, add
http://localhost:3000. - The sign-in popup returns a one-time code to the page, so no redirect URI is needed; do not add the API as a Google redirect URI.
- Copy the generated Client ID and paste it into
.envasREACT_APP_GOOGLE_CLIENT_IDandGOOGLE_CLIENT_ID. The frontend and server must use the same web client ID. MissingGOOGLE_CLIENT_IDfails backend startup. - Copy the client's Client secret into
.envasGOOGLE_CLIENT_SECRET(server only, neverREACT_APP_). The API'sPOST /auth/google-codeuses it to swap the popup's code for an ID token; without it, sign-in reports that it is temporarily unavailable.
Google Maps API key (REACT_APP_GOOGLE_MAPS_API_KEY)
- In the same Credentials page, click Create Credentials → API key.
- From the API Library, enable
these APIs for your project:
- Maps JavaScript API
- Places API
- Copy the API key and paste it into
.envasREACT_APP_GOOGLE_MAPS_API_KEY. - The frontend's Maps script tag in
public/index.htmlreads the key via Create React App's%REACT_APP_GOOGLE_MAPS_API_KEY%HTML substitution, which is resolved at build time — you don't need to editindex.htmlyourself. (If you change.env, restartnpm startfor the new value to be picked up.) - It is highly recommended to add HTTP referrer restrictions to the key in the Google Cloud Console so it can only be used from your domains.
Flask backend URL (REACT_APP_FLASK_API_URL)
The React frontend reads REACT_APP_FLASK_API_URL to know where the Flask
backend is running. For local development, leave the value in .env.example
(http://127.0.0.1:5000) as-is.
Security note: anything prefixed with
REACT_APP_is embedded into the built JavaScript bundle and is therefore visible to anyone who loads the site. Treat these as public keys and lock them down with origin / referrer restrictions in the Google Cloud Console.
Available npm scripts
| Script | What it does |
|---|---|
npm start | Start the React dev server on port 3000. |
npm run backend | Start the Flask backend (python app.py) on port 5000. |
npm run dev | Start the backend and frontend together via concurrently. |
npm run build | Produce a production build of the frontend in build/. |
npm test | Run the React test suite. |
Tests and production build
Run the backend suite from an activated virtual environment:
python -m pip install -r requirements-test.txt
python -m tests.run_tests
On macOS and Linux, you can also run it without activation:
.venv/bin/python -m pip install -r requirements-test.txt
.venv/bin/python -m tests.run_tests
The backend suite covers the HTTP contract, validation, last-result storage, optimizer invariants, and real Google verification using synthetic RSA tokens. tests.run_tests disables dotenv before discovery/root-app imports and injects a synthetic expected audience; it never needs real credentials or certificate network calls. It uses Python's standard-library unittest.
Run the frontend checks separately:
CI=true npm test -- --watchAll=false
npm run build
Auth lifecycle tests are pending human visual confirmation; they have not been written or run yet.
In PowerShell, set $env:CI = "true" before running the npm test command.
Deployment
Two Vercel projects deploy from this repository root. The frontend project uses the
Create React App preset; package.json selects Node.js 24.x through engines.node,
overriding the dashboard runtime setting. The API project uses the Flask preset with
app.py as its entrypoint and installs requirements.txt. .vercelignore excludes only
Python environment/cache files, so both projects see the backend sources.
Set REACT_APP_FLASK_API_URL to the API project's URL when building the frontend.
The python app.py command starts a debug development server.
The API project reaches Firestore through Vercel OIDC and Google Workload Identity
Federation. Set GCP_PROJECT_NUMBER, GCP_WORKLOAD_IDENTITY_POOL_ID,
GCP_WORKLOAD_IDENTITY_POOL_PROVIDER_ID and GCP_SERVICE_ACCOUNT_EMAIL on it, along
with GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, CORS_ALLOWED_ORIGINS and
FIRESTORE_PROJECT_ID; without the
project ID every saved-data request fails as unavailable. Vercel
sends a short-lived token in the x-vercel-oidc-token request header; the backend keeps
the latest one for credential refresh. Its audience stays https://vercel.com/<team-slug>,
so configure the pool provider's allowed audiences with that value. Disable Vercel
Deployment Protection on the API project: browser preflight cannot carry Vercel's
login cookie across origins, and protected routes already require Google proof.
Project layout
app.py exported Flask app and development server entry point
backend/
__init__.py create_app, CORS, logging, and route registration
auth.py Google ID-token verification and request-local identity guard
auth_routes.py protected POST /auth/verify identity probe
routes.py HTTP handlers, JSON serialization, last-result storage
validation.py request parsing and validation errors
models.py Driver, Passenger, and coordinate type
optimizer.py distance, assignment, stop ordering, and route building
tests/
__init__.py test package marker
test_api.py root-app HTTP contract, storage, and validation tests
test_optimizer.py domain and optimizer invariant tests
test_auth.py synthetic crypto, auth, certificate-cache and CORS checks
run_tests.py dotenv-safe synthetic-config test discovery
public/ static assets and HTML shell
src/ React frontend source
requirements.txt Python runtime dependencies
package.json Node dependencies and npm scripts
.env.example template for local environment variables
.vercelignore frontend-only deployment exclusions
.gitignore
Google account identity
The Google-rendered sign-in button supplies a signed ID token. VroomMates sends it
as explicit Authorization: Bearer proof to POST /auth/verify, with an empty body
and browser credentials omitted. Flask verifies the signature, configured audience,
Google issuer, issue time, expiry and required claims on every protected request.
Its request-local owner is (google, verified sub), exposed as google:<sub>.
Names are optional display data; emails and client-supplied owner fields grant no authority.
The browser keeps the proof only in memory. Refreshing, opening a new tab or expiry requires explicit sign-in again. Logout clears this tab and cancels pending requests; it does not sign out Google or revoke a copied valid token, which remains usable until signed expiry. Switching accounts requires logging out first. Stale callbacks and responses cannot restore the previous account. A temporary verification outage offers explicit retry without showing an authenticated greeting.
The probe accepts no body or query inputs. Controlled errors contain string error
and stable code: AUTH_REQUIRED or INVALID_CREDENTIAL (401),
INVALID_AUTH_REQUEST (400), and AUTH_UNAVAILABLE (503). Auth responses use
Cache-Control: no-store; 401 includes WWW-Authenticate: Bearer. Public Google
certificates follow HTTP cache freshness and outbound requests have a three-second
transport timeout. Verification infrastructure failure never trusts decoded claims.
Configure CORS_ALLOWED_ORIGINS with exact comma-separated frontend origins.
The development default is http://localhost:3000; add the exact local port if
changed. Wildcards, origin patterns and URL paths are rejected. Bearer requests omit
cookies; public OPTIONS preflight permits Authorization/Content-Type for allowed
origins. CORS is a browser boundary and never replaces proof verification.
Deployment still requires a separate HTTPS Flask host, Authorization forwarding, OPTIONS handling, outbound Google certificate access, exact Google Authorized JavaScript origins, compatible GIS/CSP/popup headers and safe server/proxy logging. Do not log Authorization, proof, full claims or account data. No backend host or real-account/deployed-origin validation is configured by this change.
Route planning remains public. The legacy process-global last-result GET and route coordinate logging require migration in the future private saved-trip API slice. Authentication alone does not establish saved-route durability or privacy.
Bounded geographic optimizer
The separately implemented exact geographic optimizer supports at most 12 raw
passenger entries and 8 drivers. Oversized HTTP requests return 400, including
18 co-located passenger entries; shared pickup coordinates do not reduce this limit.
Coordinates must be finite and within latitude [-90, 90] and longitude [-180, 180];
valid numeric strings remain accepted. Repeated passenger waypoints are preserved.
Every listed driver travels, including a direct origin-to-destination route when no
passengers are assigned. Each driver's origin, assigned pickup multiplicity and
shared destination remain in the result, and the success JSON schema is unchanged.
Compatibility for 18 co-located stops in the standalone order_stops helper does
not widen the HTTP request limit.
The objective is total geographic distance, with deterministic recurrence ties and ordinary floating-point accuracy. Correctness comparisons use a numerical tolerance; this does not promise bit-exact real-number costs, road travel times or other modes.
Private saved-data API (local implementation)
The saved-trip and saved-location APIs use Firestore through Flask. The target is Vercel-hosted Flask with Firestore in the existing Google project. Database/region/IAM, exact hosted origins and real two-account/device recovery need a separate hosted check.
Send the Google ID token in Authorization: Bearer ... for each protected request. Flask verifies it and derives ownership from Google subject, then selects the account's isolated Firestore document namespace. Browser clients do not connect directly to Firestore; no Firebase Authentication migration is needed. FIRESTORE_PROJECT_ID and optional FIRESTORE_DATABASE_ID are server-only settings. The SDK uses server Application Default Credentials; no database credential belongs in a REACT_APP_ variable. On Vercel, the four GCP_* settings switch the SDK to Vercel OIDC / Google Workload Identity Federation (see Deployment); hosted verification of project/environment IAM restrictions is still pending. Never place actual credential values in this repo or its CDK record.
POST /saved-trips:{id: UUIDv4, title, snapshot: {schema_version: 1, inputs: {drivers, passengers, destination}, routes}}. Each input point has a uniquestop_id, optional independent user label and{kind: "google_places", place_id}source; drivers also have positive integer capacity. A route is{driver_id, stop_ids: [driver, ...passengers, destination]}. Ordered occurrence references preserve repeated pickup places. Addresses, coordinates and provider responses are rejected. Saved assignments are preserved; reopening resolves current place details and does not automatically optimize again.GET /saved-trips: owner summaries, default50/max100 with opaquecursor;GET /saved-trips/<id>loads references;DELETEreturns204 including already missing/foreign IDs.PATCH /saved-trips/<id>:{title?, snapshot?}, validated as forPOST. The later write wins; there is no revision check.- Optional on snapshot stops:
location_id, the UUIDv4 of the owner's saved location the stop was picked from, and on drivers and passengersperson, a name of 1-80 characters. Optional oninputs:arrive_by, the wall-clock time at the destination asYYYY-MM-DDTHH:MM, a real date from 2000 to 2100. Any other field is still rejected. - Trip details and list rows carry an optional
updated_at, set at creation and stamped on everyPATCH, never earlier thancreated_ator the previous edit. Trips saved before it have none until their next edit. - List rows also carry
arrive_bywhen the trip has one. The list stays ordered bycreated_at, with no new index. POST /saved-locations:{id: UUIDv4, label, source: {kind: "google_places", place_id}};GET /saved-locationslists at most100 owner locations, detail byID,DELETEremoves one. Labels are normalized/case-insensitively unique per owner; different labels can refer to the same place. The response hasresolution_required: true, with no retained address/geometry. No manual-coordinate or edit API in v1.
Generate one random ID per save intent. A matching existing ID/content returns the original record; different content conflicts. A new ID can intentionally copy a trip. After a timeout the save outcome is unknown: load that ID to reconcile before an explicit retry. Never automatically replay writes under a new account or after deletion; there is no permanent operation ledger or post-delete replay guarantee. Store failures return503 rather than fabricated empty results or successful writes.
The former public GET /routeoptimizer/ is retired with410 LAST_RESULT_REMOVED; route POST still returns its existing success shape. No global last-result object or full coordinate logging remains.
firestore.indexes.json declares a collection-group exemption for trips.snapshot_json. This configuration is not deployed by these changes; without applying it, Firestore's default indexing still applies. Review it alongside any existing project indexes; do not overwrite unrelated configuration. The snapshot is encoded as bounded canonical JSON, with separate projected list metadata. Server IAM and Flask ownership checks protect server SDK access; client rules alone do not constrain the server SDK. Deny browser access to the dedicated vroommates_accounts namespace when configuring rules without changing unrelated project access.
Local integration tests explicitly opt into the loopback Firestore emulator with anonymous credentials and a demo project. They cannot establish hosted durability or pricing/backup retention. Run the safe backend runner, which disables dotenv before discovery:
python -B -m tests.run_tests
For the selected-adapter suite, start an official Firestore emulator bound to 127.0.0.1:8187 with project demo-vroommates, then run:
FIRESTORE_EMULATOR_HOST=127.0.0.1:8187 VROOMMATES_TEST_FIRESTORE_EMULATOR=127.0.0.1:8187 python -B -m tests.run_tests
The explicit test flag rejects cloud/nonloopback targets. The emulator service itself is ephemeral; the restart test creates a fresh backend process against the same running emulator. Hosted managed-Firestore recovery and two-device proof remain delivery work. Save/My Trips and saved-place picker interfaces are later slices; the API does not yet make those flows available in the browser.
Analysis
View
Metric
- 9
- 6
- 4
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- CSSIn code
- FlaskIn code
- HTMLIn code
- JavaScriptIn code
- PythonIn code
- ReactIn code
6 of 6 appear in the indexed code.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
38 KB
Source files
10
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
jonnypan2325/VroomMates
24 files · 929 KB · @ 88b6f27
Structure
Application logic
8 files · 33%Domain rules, services and shared utilities.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- JavaScript48%
- Python27%
- Markdown15%
- CSS8%
- HTML2%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
package.json
npm · 10- @react-oauth/google
- @testing-library/jest-dom
- @testing-library/react
- @testing-library/user-event
- axios
- react
- react-dom
- react-scripts
- web-vitals
- +1 more
requirements.txt
pypi · 3- Flask
- Flask-Cors
- python-dotenv
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
Feature verification
Driver/passenger registration with capacityVerified
Users register as drivers or riders, input locations and destinations
Claimed on Devposthigh confidencesrc/LocationInput.js:4— UI state and forms for driver capacity/address and passenger address, submitted to backendapp.py:210— Backend parses driver/passenger location and capacity fields from the request
Flask backend for route optimizationVerified
Back end powered by Flask, Python handles clustering algorithms to generate optimized driver routes
Claimed on Devposthigh confidenceapp.py:186— Flask route /routeoptimizer/ (POST) validates input and calls give_paths() to compute optimized routes, returned as JSON
Frontend/backend split with REACT_APP_FLASK_API_URLVerified
Frontend talks to backend over HTTP; REACT_APP_FLASK_API_URL configures the backend URL
Claimed on readmehigh confidencesrc/LocationInput.js:179— Reads process.env.REACT_APP_FLASK_API_URL (falling back to localhost:5000) to construct the route optimizer request URL.env.example:17— Documents REACT_APP_FLASK_API_URL default value
Haversine formula for distance/clusteringVerified
Linear distance using the Haversine Formula to cluster drivers
Claimed on Devposthigh confidenceapp.py:10— haversine_distance() implements the great-circle distance formula and is used throughout the pathing and assignment logic
Location input via Google Maps/Places APIVerified
Front end uses Google Maps API for location input, tracking, and map display
Claimed on Devposthigh confidencesrc/LocationInput.js:77— Uses window.google.maps.places.Autocomplete for driver, passenger, and destination address inputsrc/App.js:50— Initializes a window.google.maps.Map instance and DirectionsRenderer for map displaypublic/index.html:14— Loads the Google Maps JavaScript API script with Places library
npm run dev runs backend and frontend concurrentlyVerified
npm run dev uses concurrently to start Flask backend and React dev server together
Claimed on readmehigh confidencepackage.json:15— dev script uses concurrently to run "npm run backend" and "npm run start" together, with backend script running python app.py
Rider-driver matching / assignment algorithmVerified
Automatically match riders with drivers based on optimal routes, minimizing detours
Claimed on Devposthigh confidenceapp.py:112— assign_drivers() implements a global-greedy bipartite assignment of passengers to drivers using pairwise detour cost, enforcing driver capacityapp.py:94— distance_from_line() computes detour cost used to score passenger-driver pairs
A* pathfinding for driver routesCode-supported
VroomMates uses A* for pathfinding to generate optimized driver routes
Claimed on Devpostmedium confidenceapp.py:53— Driver.get_path() uses a heapq-based best-first search over remaining passengers and destination, similar to A*/Dijkstra, but there is no explicit heuristic function, so it is closer to uniform-cost/Dijkstra search than a true A*
Google sign-inCode-supported
Users register/sign in (Google Login button visible in UI)
Claimed on Devpostmedium confidencesrc/App.js:17— useGoogleLogin from @react-oauth/google is wired to a login button and fetches profile info on success, but there's no backend session/auth enforcement tying this to route optimization access
React + Flask full-stack app with combined repo layoutCode-supported
Frontend (React, src/ and public/) and backend (Flask, app.py and backend/) in a single project
Claimed on readmemedium confidenceapp.py:1— Flask backend entrypoint exists as describedsrc/App.js:1— React frontend exists in src/ as described, but the backend/ directory mentioned in the README's project layout does not exist in this repo
Vercel hosting for frontendCode-supported
Hosted the app on Vercel
Claimed on Devpostlow confidence.vercelignore:1— A .vercelignore file exists configured to deploy only the React static site and exclude the Flask backend, implying a Vercel deployment setup, but no vercel.json or actual deployment evidence is present
Continuous deployment with GitHub ActionsClaimed only
Implemented continuous deployment with GitHub Actions and hosted on Vercel
Claimed on Devposthigh confidenceSession persistence via Google DatastoreClaimed only
Routes stored in Google Datastore, allowing users to save and resume sessions across devices
Claimed on Devposthigh confidence
An AI agent derived these features from the project’s Devpost page and readme, then searched the code for each one. Verified features are backed by cited code; claimed-only features had no supporting code, which is not by itself proof a feature is missing.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.