Hoddle Melbourne
● live · CTOInternational mentorship platform connecting first-year students at Melbourne universities with verified senior mentors. Built end-to-end as sole developer — schema, auth, matching algorithm, community layer, and background infrastructure.
Context
CTO and sole developer, Mar 2026–present. Hoddle is a one-to-many mentorship platform — mentors publish articles, host live Q&As, and tell their story; students get matched based on shared background and goals, browse a content library, and ask questions in forums. Eventually students graduate into mentors themselves.
I owned the full stack from day one: database schema and RLS policies, auth flows, matching algorithm, community features, background jobs, and admin tooling. The codebase sits at 26,000+ lines across 51 routes, 38 components, 19 server action files, and 17 migrations.
Built as part of the Study Melbourne Leadership 4Impact Program alongside co-founders Aarav Verma and Tanvir Kaur Sohi. The team placed 2nd at the final pitch day.
Mar 2026
Foundation
Joined as CTO and sole developer. Rejected every third-party auth provider — one Supabase service for auth, Postgres, and RLS. Set up full project scaffold: design tokens, Tailwind v4 theme, route groups, Supabase clients (server, browser, admin), and TypeScript strict mode.
Mar 2026
Auth & onboarding
Email magic link auth via admin.auth.admin.generateLink() and nodemailer, bypassing Supabase SMTP entirely. 5-step student wizard — university, country of origin, goals, challenges, fields of interest — Zod-validated on both client and server. Partial saves persist between steps.
Apr 2026
Matching algorithm
scoreMentor(): country +30, field overlap +15 each, challenges and goals +10 each. IO-separated — zero database calls in the scoring function. Scores stored in mentor_recommendations, recomputed nightly at 02:00 AEST via Vercel cron and on-demand after onboarding updates. "Why this mentor" reasoning line rendered on the dashboard.
Apr 2026
Mentor invite system
Token-gated invite flow: admin sends an email generating a row in mentor_invites with a hashed token, 14-day expiry, and audit columns. Mentor clicks through to /mentor-signup/[token], validated server-side before rendering. After magic-link auth, acceptMentorInvite() sets role=mentor and routes to a 4-step onboarding wizard.
Apr 2026
Content library
Mentors author articles with a Tiptap rich-text editor, embed YouTube/Vimeo via URL, and attach downloadable resources stored in Supabase Storage with signed URLs (25 MB limit). Students browse with type filter chips and offset pagination (18 per page). View counts increment atomically via server action on each load.
Apr 2026
Community forums
5 seeded categories: First Semester Struggles, Career & Internships, Living in Melbourne, Academic Writing, Visa & Admin. Threads with two-level nested replies, 4 reaction types (Helpful, Thanks, Hugs, Insightful), view counter via SECURITY DEFINER RPC, anonymous posting for students, quote-reply and @mention shortcuts. Mentors always post as identified.
Apr 2026
Live Q&A and sessions
Mentors schedule sessions with title, description, datetime, duration, capacity, and a meeting URL. Students register (capacity-enforced server-side), submit questions with an anonymous toggle, and receive recordings after. Three Vercel cron jobs handle the full session lifecycle.
Apr 2026
Notification system
Single notify(recipientId, type, payload) helper writes to the notifications table and conditionally dispatches email based on per-type, per-channel preference toggles. In-app bell uses a Supabase Realtime subscription — unread count stays live without polling. 6 notification types.
Apr 2026
Direct messaging
Private conversations between students and mentors: conversations, messages, and conversation_read_cursors tables. Rate-limited to 30 messages per 10 minutes. new_chat_message notification fires with a 5-minute per-conversation debounce to avoid notification flooding.
Apr 2026
Google OAuth + feedback pipeline
Added Google sign-in alongside magic link. Floating feedback widget on all browse pages (Bug / Suggestion / Confusion / Other) forwarded to Airtable via a server action. Auth flows through /auth/confirm client component that handles the implicit-flow hash fragment the server callback route cannot reach.
Country is weighted highest because shared migration context — knowing what it's like to arrive from the same country — matters more than overlapping in the same field. The reasoning string is stored alongside the score and rendered on the student dashboard as a "why this mentor" line. Scores above a threshold of 10 count as real matches; below that, the algorithm backfills from top-ranked verified mentors to always surface five recommendations.
Database
17 migrations · ~1,500 lines of SQL
profilesonboarding_responsesmentorsmentor_invitescontent_itemscontent_resourcescontent_tagsforum_categoriesforum_threadsforum_postsforum_reactionssuccess_storieslive_sessionssession_registrationssession_questionsnotificationsnotification_preferencesmentor_recommendationsmentor_followsconversationsmessagesconversation_read_cursorsreviews
storage buckets
content-media (public)content-resources (signed)session-recordings (private)story-images (public)avatars (public)