A mentorship platform that matches first-year international students in Melbourne with verified senior mentors. I built it end to end as the sole developer: schema, auth, matching, the community features and the background jobs.
CTO and sole developer, Mar 2026 to now. Mentors publish articles, host live Q&As and share their story. Students are matched on shared background and goals, browse the content library and ask questions in the forums.
Built through the Study Melbourne Leadership 4Impact program with co-founders Aarav Verma and Tanvir Kaur Sohi. We placed 2nd at the final pitch in May 2026.
Walkthrough of hoddle.org.
Timeline
Mar 2026
Foundation and auth
Joined as CTO and sole developer. One Supabase project for auth, Postgres and row-level security. Magic-link sign-in and a 5-step student onboarding wizard, Zod-validated on client and server.
Apr 2026
Matching and mentor invites
Weighted mentor matching, recomputed nightly and after onboarding changes. Mentors join through a token-gated invite with a 14-day expiry and a full audit trail.
Apr 2026
Content and forums
Mentors publish articles, videos and downloads. Forums have two-level replies, reactions and anonymous posting for students. Mentors always post under their name.
Apr 2026
Live Q&As, DMs, notifications
Capacity-limited live sessions, rate-limited direct messages, and one notify() helper behind a realtime in-app bell and per-type email preferences.
Apr 2026
Google sign-in and feedback
Google OAuth alongside magic links, and a feedback widget on every browse page that files bugs and suggestions.
Recommended mentors, each with a one-line reason for the match.
Key technical decisions
supabase rls over application-layer guards
Row-level security enforces access in the database, so a bug in middleware cannot leak another user’s data. Every table has explicit policies. Only system jobs, like notify() and the recommendation recompute, use the admin client.
three supabase clients over one shared client
A cookie-bound server client for server components and actions, a browser client for the realtime bell, and an admin client that is never imported from app/ or components/. The service-role key has no NEXT_PUBLIC_ prefix and never reaches the browser.
pure scoring function over scoring mixed with queries
scoreMentor() takes two plain objects and returns a score and a reason, with no database calls. A separate function does the Supabase work, so the scoring is tested without mocks.
nightly precompute over scoring on every request
Scores are read far more often than profiles change. Precomputing at 02:00 AEST keeps the dashboard to one ORDER BY score DESC query, and a profile update recomputes that student straight away.
23 reasoning: reasonParts.join(" · ") || "Verified mentor on Hoddle",
24 }
25}
Country carries the most weight because arriving from the same place matters more than studying the same field. The reason string is stored with the score and shown on the dashboard. A score above 10 counts as a real match; below that, top-ranked verified mentors fill the list so every student sees five.