A decentralized, verified communication platform for university students — replacing fragmented WhatsApp groups with structured class communities, study groups, and academic forums.
University students today rely on scattered WhatsApp groups where anyone with a link can join, announcements are lost in spam, and there's no way to verify who belongs to which class or department.
UniConnect solves this with a decentralized, verification-first approach:
- Registration Number as Identity — Every student signs up using their university-issued registration number, which is validated against an official roster.
- Peer-to-Peer Verification — Class representatives physically verify students in person using temporary 6-digit codes (a decentralized alternative to institutional email/SMS gateways).
- Role-Based Communities — Students, class reps, lecturers, and platform admins each have distinct, permission-controlled experiences.
- Structured Communication — No more lost messages. Class communities, year-group spaces, study groups, and a Q&A forum keep everything organized.
Current status: The authentication and verification foundation is complete and tested. Community features (posts, threads, realtime) are the next milestone.
| Feature | Detail |
|---|---|
| Registration Number Parsing | Validates, normalizes, and canonicalizes COURSE/SERIAL/YEAR format with a 22-test unit suite |
| Secure Sign-Up | Gates account creation against an official valid_reg_numbers roster with duplicate prevention |
| Password-Based Sign-In | Resolves registration numbers to internal email mapping for authentication |
| Peer Verification Codes | 6-digit SHA-256 hashed codes; reps verify students in person via /verify/{code} |
| Row-Level Security | Supabase RLS policies control access across profiles, verification_codes, and valid_reg_numbers tables |
| Role System | student → rep → lecturer → platform_admin hierarchy with server-enforced authorization |
| Responsive UI | Tailwind v4 + shadcn/ui (slate theme) with dark mode support via next-themes |
| Testing | 22 Vitest unit tests + Playwright E2E signup happy path |
- Class / year-group / study communities
- Posts, threads, and realtime messaging
- Representative self-nomination & election flow
- Admin dashboard
- Push notifications + offline support (Dexie installed, not wired)
- Email/SMS-based verification delivery
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router, Turbopack) |
| Language | TypeScript (strict mode) |
| Styling | Tailwind CSS v4 + shadcn/ui (nova preset, slate palette) |
| Backend / Database | Supabase — PostgreSQL, Auth, RLS |
| Forms | react-hook-form + Zod validation |
| Toasts | sonner |
| Offline (future) | Dexie + dexie-react-hooks |
| Unit Testing | Vitest + Testing Library |
| E2E Testing | Playwright |
┌──────────┐ ┌──────────────┐ ┌────────────────┐ ┌──────────────┐
│ Student │ ────> │ /sign-up │ ────> │ Server Action │ ────> │ Supabase │
│ enters │ │ validates │ │ 1. Check │ │ - Auth │
│ reg num │ │ reg number │ │ roster │ │ - Profiles │
│ │ │ + password │ │ 2. Sign up │ │ - Codes │
│ │ │ │ │ 3. Insert │ │ - RLS │
│ │ │ │ │ profile │ └──────────────┘
│ │ │ │ │ 4. Claim reg │ │
│ │ │ │ │ 5. Generate │ │
│ │ │ │ │ 6-digit │ │
│ │ │ │ │ code │ │
│ │ │ │ └────────────────┘ │
│ │ └──────────────┘ │
│ │ ▼
│ │ ┌──────────────────────┐
│ │ │ Success Card: │
│ │◄───────────────────────────────────────────────│ "Show this 6-digit │
│ │ │ code to your class │
│ │ │ rep in person" │
│ │ └──────────────────────┘
└──────────┘
┌──────────┐ ┌──────────────────┐ ┌─────────────────────────┐
│ Class │ ────> │ /verify/{code} │ ────> │ verify_profile_with_ │
│ Rep │ │ (public route) │ │ code(p_code) RPC │
│ │ │ │ │ │
│ │ │ Server checks │ │ - Re-checks role │
│ │ │ caller role │ │ - Verifies SHA-256 │
│ │ │ from profiles │ │ - Marks code used │
│ │ │ │ │ - Sets is_verified │
│ │ │ │ │ - Returns profile │
│ │ └──────────────────┘ └─────────────────────────┘
└──────────┘
valid_reg_numbers profiles verification_codes
┌─────────────────┐ ┌──────────────────┐ ┌───────────────────┐
│ reg_number (PK) │ │ id (PK, UUID) │ │ id (PK, UUID) │
│ course_code │──>│ reg_number (UNQ) │<──│ profile_id (FK) │
│ is_claimed │ │ full_name │ │ code_hash (SHA256)│
│ claimed_by (FK) │ │ phone │ │ expires_at │
│ claimed_at │ │ role (enum) │ │ used_at │
│ created_at │ │ is_verified │ │ used_by (FK) │
└─────────────────┘ │ verified_by (FK) │ │ created_at │
│ verified_at │ └───────────────────┘
│ created_at │
│ updated_at │
└──────────────────┘- Node.js 20+
- A Supabase project (cloud or local with
supabase start)
git clone https://github.com/MeshackKangi/uniconnect.git
cd uniconnect
npm installCreate .env.local:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key # only for E2E tests
NEXT_PUBLIC_REG_EMAIL_DOMAIN=students.maseno.placeholderRun the SQL in supabase/uniconnect_schema_and_rls.sql against your Supabase database (SQL editor or CLI).
INSERT INTO public.valid_reg_numbers (reg_number, course_code)
VALUES ('CCS/000545/2022', 'CCS');npm run devOpen http://localhost:3000.
| Command | Purpose |
|---|---|
npm run dev |
Start development server with Turbopack |
npm run typecheck |
TypeScript strict-mode compilation check |
npm run lint |
ESLint (next/core-web-vitals + next/typescript) |
npm run format:check |
Prettier formatting check |
npm test |
Run 22 Vitest unit tests for registration number parser |
npm run build |
Production build verification |
npm run test:e2e:install |
Install Playwright browsers (one-time) |
npm run test:e2e |
Run Playwright E2E signup happy path |
uniconnect/
├─ app/ # Next.js App Router pages
│ ├─ (auth)/ # Auth route group
│ │ ├─ layout.tsx
│ │ ├─ sign-up/ # Registration flow
│ │ ├─ sign-in/ # Login flow
│ │ └─ verify/[code]/ # Peer verification
│ ├─ dashboard/ # Protected dashboard (placeholder)
│ ├─ globals.css # Tailwind + shadcn theme variables
│ ├─ layout.tsx # Root layout with fonts + Toaster
│ └─ page.tsx # Landing page
├─ components/ui/ # shadcn/ui primitives
├─ e2e/ # Playwright end-to-end tests
├─ hooks/ # Shared React hooks
├─ lib/
│ ├─ auth/ # Auth logic
│ │ ├─ regNumber.ts # Parser, normalizer, validator
│ │ ├─ regNumber.test.ts # 22 unit tests
│ │ ├─ email.ts # Reg number ↔ email mapping
│ │ └─ schemas.ts # Zod schemas for sign-up/sign-in
│ ├─ supabase/ # Supabase clients (browser, server, middleware)
│ ├─ types/database.ts # TypeScript types matching SQL schema
│ └─ utils.ts # Tailwind class merge utility
├─ supabase/
│ └─ uniconnect_schema_and_rls.sql # Full schema + RLS policies
├─ types/ # Shared TypeScript types
├─ middleware.ts # Session refresh + protected route redirect
├─ next.config.ts
├─ vitest.config.ts # Vitest configuration
├─ playwright.config.ts # Playwright configuration
└─ package.json
The parser accepts COURSE/SERIAL/YEAR format with these rules:
- Course code: 2–5 letters, case-insensitive on input, uppercased in canonical form
- Serial: 3–7 digits, preserved verbatim (no padding —
000545≠545) - Year: 2–4 digits; 2-digit years use a 70-pivot (
<70→20XX,>=70→19XX)
This means CCS/545/22 normalizes to CCS/545/2022 while CCS/000545/22 normalizes to CCS/000545/2022 — they are distinct students. The 22 unit tests explicitly pin this behavior.
Many universities lack institutional email for every student or have constraints on SMS budgets. UniConnect uses a decentralized verification model:
- A 6-digit code is generated at sign-up and shown once to the student
- Only the SHA-256 hash is stored server-side
- The student presents the code in person to their class representative
- The rep visits
/verify/{code}and confirms — the RPC enforces role, single-use, and expiry
This mechanism is explicitly a placeholder and will be replaced by email/SMS delivery once institutional integration is available.
This is a foundational project under active development. Contributions around communities, realtime messaging, and admin tooling are especially welcome.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Built with ❤️ for Maseno University students.