β¨ Features Β β’Β π§ Lifecycle Β β’Β ποΈ Architecture Β β’Β π§° Tech Stack Β β’Β π Getting Started Β β’Β π Deployment
HackSphere is a full-stack, role-based hackathon management platform that covers the whole event, from the first announcement to the published leaderboard.
| π§βπ» Participants | discover events, form teams, pick a challenge and submit their build |
| ποΈ Organizers | configure and run the entire hackathon: stages, challenges, rubric, registrations, judges, results |
| βοΈ Judges | score projects against a weighted rubric and leave feedback |
| π‘οΈ Administrators | verify organizers, moderate accounts and events, and review an audit trail |
The interface is light-first: a bright canvas, bento-grid dashboards, glass navigation, clay tiles and avatars, and neumorphic toggles. Dark mode is available as an optional, reduced theme (profile menu or Profile β Appearance).
| Feature | Details | |
|---|---|---|
| π | Role-based access | One identity, several contexts. Every permission is enforced on the server; the UI only hides what a user can't use |
| π₯ | Team formation | Create or join one team per hackathon, invite members, request to join, transfer leadership, with team-size limits enforced |
| π§© | Challenges | Problem statements with optional team limits; the choice locks after a final submission |
| π¦ | Submissions | Needs an approved registration and a live hackathon; locks at the deadline unless the organizer grants an extension |
| βοΈ | Weighted judging | Per-criterion 0 to 10 scores, weighted average, tie-breaks, conflict-of-interest declarations |
| π | Results and awards | Rankings stay private until published; top three plus best project per challenge are generated |
| π | Real-time updates | Socket.IO rooms per user and per event for live notifications |
| π | Secure uploads | 15 MB limit, type allow-list, access-controlled downloads |
| π§Ύ | Audit log | Admins can review sensitive actions: role changes, suspensions, verifications |
| π¨ | Light-first design system | Bento, glass, clay and neumorphic surfaces, accessible by default |
| βΏ | Accessibility | Focus rings, skip links, linked error messages, keyboard-operable switches, prefers-reduced-motion respected |
Every hackathon moves through a clear set of stages. The stage colours are identical everywhere (badges, stepper and charts).
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#EDE9FF','primaryBorderColor':'#7058F5','primaryTextColor':'#202747','lineColor':'#5A41E6','tertiaryColor':'#F8FAFF'}}}%%
stateDiagram-v2
direction LR
[*] --> Draft : organizer creates event
Draft --> Announced : publish
Announced --> RegistrationOpen : open registration
RegistrationOpen --> Live : start hackathon
Live --> Judging : submissions lock
Judging --> Completed : publish results
Completed --> [*]
| Role | How you get it | Can do |
|---|---|---|
| π Visitor | Not signed in | Browse published hackathons, schedules, rules and results |
| π§βπ» Participant | Default account | Create or join one team per hackathon, accept invitations, request to join teams, save hackathons |
| π© Team leader | Creating a team (or receiving leadership) | Invite, approve requests, remove members, transfer leadership, choose the challenge, register, submit |
| ποΈ Organizer | Organizer account verified by an admin, or added as co-organizer | Create events, move stages, manage challenges and rubric, review registrations, grant extensions, assign judges, announce, publish results |
| βοΈ Judge | Added to a specific hackathon by its organizer | Score assigned final submissions, write feedback, declare conflicts of interest |
| π‘οΈ Admin | Platform role | Change roles, suspend accounts (ends their sessions), verify organizers, moderate events, read the audit log |
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#EDE9FF','primaryBorderColor':'#7058F5','primaryTextColor':'#202747','lineColor':'#5A41E6'}}}%%
flowchart LR
V(["π Visitor"]) -->|sign up| P(["π§βπ» Participant"])
P -->|creates a team| L(["π© Team leader"])
P -->|added by organizer| J(["βοΈ Judge"])
P -->|organizer account| UO(["β³ Unverified organizer"])
UO -->|verified by admin| O(["ποΈ Organizer"])
A(["π‘οΈ Admin"]) -.->|verifies and moderates| UO
A -.->|changes roles, suspends| P
- A person can be on only one team per hackathon (database unique constraint), and judges of a hackathon can't join a team in it.
- Team size limits apply to invitations, joins and registration.
- Problem statements can have a team limit; the challenge locks after a final submission.
- Submissions need an approved registration and a live hackathon, and lock at the deadline unless the organizer grants that team an extension.
- Final submissions require a challenge, a 50+ character summary, a repository link and a demo or video link.
- The rubric locks once any judge starts scoring, so scores stay comparable.
- Judges can't score their own team; a conflict declaration removes their draft and notifies organizers.
- Rankings stay private until the organizer publishes them.
Each judge rates every criterion from 0 to 10. A judge's score is the weighted average
and a team's final score is the mean of its submitted evaluations.
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#EDE9FF','primaryBorderColor':'#7058F5','primaryTextColor':'#202747','lineColor':'#5A41E6'}}}%%
flowchart LR
R["Judge rates each criterion<br/>0 to 10"] --> W["Weighted average<br/>per judge"]
W --> M["Mean of submitted<br/>evaluations per team"]
M --> K{"Tie?"}
K -->|no| RK["Rank"]
K -->|yes| T["Earlier final submission wins"]
T --> C{"Still tied?"}
C -->|no| RK
C -->|yes| S["Teams share a rank"]
RK --> AW["π
Awards: top 3 + best per challenge"]
S --> AW
M -.->|no evaluations| U["Listed but unranked,<br/>flagged to organizers"]
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#EDE9FF','primaryBorderColor':'#7058F5','primaryTextColor':'#202747','lineColor':'#5A41E6','tertiaryColor':'#F8FAFF'}}}%%
flowchart LR
subgraph Client["π₯οΈ Client Β· React 19 + Vite"]
UI["Pages and layouts"]
RQ["TanStack Query"]
RR["React Router 7"]
H3D["Hero3D Β· React Three Fiber<br/>lazy-loaded"]
RTH["Real-time hooks"]
UI --> RQ
UI --> RR
UI --> H3D
UI --> RTH
end
subgraph Server["βοΈ Server Β· Node.js + Express"]
MW["Auth middleware<br/>requireAuth Β· requireRole"]
RT["Routes<br/>auth Β· events Β· manage Β· teams<br/>judging Β· notifications Β· dashboard Β· admin"]
LIB["Domain logic<br/>access rules Β· scoring Β· results<br/>notifications Β· uploads"]
SIO["Socket.IO<br/>user and event rooms"]
MW --> RT --> LIB
LIB --> SIO
end
DB[("ποΈ SQLite<br/>Prisma 7")]
FS[("π uploads/<br/>Multer")]
RQ -->|"REST /api Β· httpOnly cookie"| MW
RTH <-->|"WebSocket /socket.io"| SIO
LIB --> DB
LIB --> FS
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#EDE9FF','primaryBorderColor':'#7058F5','primaryTextColor':'#202747','lineColor':'#5A41E6','actorBkg':'#EDE9FF','actorBorder':'#7058F5'}}}%%
sequenceDiagram
autonumber
participant B as Browser
participant A as Express API
participant D as SQLite via Prisma
B->>A: POST /api/auth/login with email and password
A->>D: Find user by email
D-->>A: User and Argon2id hash
A->>A: Verify password with Argon2id
A->>A: Generate random session token
A->>D: Store SHA-256 hash of the token
A-->>B: Set-Cookie httpOnly session token
B->>A: Later requests carry the cookie
A->>D: Look up session by token hash
A->>A: requireAuth then requireRole
A-->>B: Authorised response
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#EDE9FF','primaryBorderColor':'#7058F5','primaryTextColor':'#202747','lineColor':'#5A41E6','actorBkg':'#EDE9FF','actorBorder':'#7058F5'}}}%%
sequenceDiagram
autonumber
actor L as Team leader
actor O as Organizer
actor J as Judge
participant S as HackSphere API
L->>S: Register team and choose challenge
O->>S: Approve registration
L->>S: Final submission (summary, repo, demo)
S-->>L: Confetti and confirmation
O->>S: Assign judges and move stage to Judging
J->>S: Score each criterion 0 to 10
S->>S: Weighted average, mean, tie-breaks
O->>S: Publish results
S-->>L: Rankings and awards visible
A simplified view of the main entities. The full schema (15 entities plus sessions and reset tokens) lives in
server/prisma/schema.prisma.
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#EDE9FF','primaryBorderColor':'#7058F5','primaryTextColor':'#202747','lineColor':'#5A41E6'}}}%%
erDiagram
USER ||--o{ SESSION : has
USER ||--o{ NOTIFICATION : receives
USER ||--o{ TEAM_MEMBER : joins
USER ||--o{ EVALUATION : writes
USER ||--o{ AUDIT_LOG : triggers
HACKATHON ||--o{ CHALLENGE : defines
HACKATHON ||--o{ RUBRIC_CRITERION : scored_by
HACKATHON ||--o{ TEAM : hosts
HACKATHON ||--o{ JUDGE_ASSIGNMENT : assigns
TEAM ||--o{ TEAM_MEMBER : has
TEAM |o--o| REGISTRATION : registers
TEAM |o--o| SUBMISSION : delivers
CHALLENGE |o--o{ TEAM : chosen_by
SUBMISSION ||--o{ EVALUATION : receives
Note
Prisma 7 needs prisma.config.ts (included) and generates the client into server/src/generated/prisma. npm run setup does this; re-run npm run db:generate after editing schema.prisma.
hacksphere/
βββ server/
β βββ prisma/schema.prisma 15 entities + sessions/reset tokens
β βββ prisma/seed.ts demo data for every stage and role
β βββ src/
β β βββ index.ts HTTP + Socket.IO bootstrap
β β βββ app.ts Express app, routes, static client (prod)
β β βββ middleware/auth.ts sessions, requireAuth, requireRole
β β βββ lib/ access rules, scoring, results, notifications, uploads
β β βββ routes/ auth, events, manage, teams, judging,
β β notifications, dashboard, admin
β βββ tests/ scoring unit tests, API smoke test
βββ client/
βββ src/
β βββ index.css design tokens: palette, glass, clay, neumorphic
β βββ components/ UI kit, Dialog, Tabs, Toast, Countdown, Confetti,
β β Hero3D, EventCard
β βββ layouts/ public layout, app shell with role-aware sidebar
β βββ auth/ session context
β βββ lib/ API client, types, formatting, real-time hooks
β βββ pages/ public, auth, app (participant/leader),
β organize, judge, admin
βββ e2e/ Playwright specs
| Tool | Version |
|---|---|
| Node.js | 24.15+ in the 24.x LTS line recommended, or 22.22.2+ in the 22.x line, or 26+ |
| npm | 10+ |
| OS | Windows, macOS or Linux |
Tip
No database server is needed. SQLite is a single file (server/dev.db) that is created for you.
Node 23 is unsupported by Prisma, and Node 20 is unsupported by the test runner. Check node --version before installing. If you use nvm, run nvm install 24 and nvm use 24 (the project includes .nvmrc). After changing Node versions, reinstall dependencies before running setup.
git clone https://github.com/LoganthP/HackSphere.git
cd HackSphereOpen two terminals in the project folder.
Terminal 1: API server
cd server
npm install
Copy-Item .env.example .env # Windows PowerShell: copy .env.example .env
npm run setup # generates Prisma client, creates dev.db, loads demo data
npm run dev # http://localhost:4000Terminal 2: web app
cd client
npm install
npm run dev # http://localhost:5173Open http://localhost:5173. The dev server proxies /api and /socket.io to port 4000, so cookies and live updates work without extra configuration.
β‘ Shortcut: use the root scripts
npm run install:all # install server and client dependencies
npm run setup # prepare the database
npm run dev:server # terminal 1
npm run dev:client # terminal 2
npm test # run testsEvery account uses the password HackSphere@123. On the sign-in page (in development) you can click an account to fill it in.
| Role | What to try | |
|---|---|---|
| π§βπ» Participant | student@hacksphere.dev |
Accept Ananya's invitation to Null Pointers |
| π© Team leader | leader@hacksphere.dev |
Register Null Pointers, choose a challenge, invite members |
| ποΈ Organizer | organizer@hacksphere.dev |
Approve registrations, assign judges, publish HealthHack results |
| βοΈ Judge | judge@hacksphere.dev |
Score DoseBuddy and ClinicFlow in HealthHack Sprint |
| π‘οΈ Admin | admin@hacksphere.dev |
Verify the Tech Club VVCE organizer, review the audit log |
| β³ Unverified organizer | techclub@hacksphere.dev |
See the verification-pending state |
The seed creates one hackathon at every stage:
| Hackathon | Stage |
|---|---|
| π‘ Campus Build Night | Announced |
| π’ CodeVerse 2026 | Registration open |
| π΅ GreenGrid | Live |
| π΄ HealthHack Sprint | Judging |
| π£ FinTech Forge | Completed, results published |
| βͺ AI for Bharat | Private draft |
To start over at any time:
cd server && npm run db:reset|
|
|
Note
npm run smoke needs a fresh seed and the server running.
- Type: Bricolage Grotesque for headings, Figtree for body (bundled, so the app works offline).
- Bento grid: dashboards and the command center.
- Glassmorphism: sidebar, top bar, overlays, featured cards.
- Claymorphism: stat tiles, avatars, podium, empty-state icons, the 3D hero.
- Neumorphism: toggles, pressed buttons, active nav indicator only.
- Motion: one orchestrated reveal per screen, spring dialogs, sliding nav indicator, confetti only for registration and final submission. All motion respects
prefers-reduced-motion. - Accessibility: visible focus rings, skip links, labelled controls with linked error messages, keyboard-operable switches, text colour picked automatically for WCAG contrast on coloured surfaces.
Build the client and let the API serve it:
cd client && npm run build
cd ../server && NODE_ENV=production npm start # serves the app and API on :4000Everything is on one origin, so session cookies work with the default COOKIE_SAMESITE=lax. Put it behind HTTPS (cookies are Secure in production).
Use a Vercel or Netlify frontend plus a persistent Node host (Render, Railway, Fly.io or a VPS). The host needs a persistent disk for dev.db and uploads/, so serverless functions are not suitable.
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#EDE9FF','primaryBorderColor':'#7058F5','primaryTextColor':'#202747','lineColor':'#5A41E6'}}}%%
flowchart LR
U(["π Browser"]) --> FE["Vercel / Netlify<br/>static client"]
FE -->|"rewrite /api/* and /socket.io/*"| BE["Persistent Node host<br/>Render Β· Railway Β· Fly.io Β· VPS"]
BE --> DB[("SQLite + uploads/<br/>persistent disk")]
| Approach | What to do |
|---|---|
| β Easiest: rewrite | Add a rewrite on the frontend host so /api/* and /socket.io/* proxy to your API. The browser sees one site and cookies just work |
| π§ Without a rewrite | Set VITE_API_URL=https://api.example.com when building the client. On the server set CLIENT_ORIGIN=https://your-frontend.app and COOKIE_SAMESITE=none (requires HTTPS) |
Important
SQLite suits a classroom deployment and moderate single-server load. For multiple servers, switch the Prisma datasource to PostgreSQL.
| Variable | Where | Purpose |
|---|---|---|
PORT |
server/.env |
API port (default 4000) |
CLIENT_ORIGIN |
server/.env |
Allowed frontend origin when split-hosted |
COOKIE_SAMESITE |
server/.env |
lax for one origin, none for split hosting over HTTPS |
NODE_ENV |
server | production serves the built client and sets Secure cookies |
VITE_API_URL |
client build | API base URL when not using a proxy or rewrite |
| Problem | Fix |
|---|---|
| "Can't reach the HackSphere server" | Start the API (cd server && npm run dev) before using the app |
Prisma reports an unsupported Node version, or prisma / tsx is not recognized after an install failure |
Switch to Node 24 LTS (24.15+), open a fresh terminal, confirm node --version, then run npm ci and npm run setup in server/ |
npm install fails building better-sqlite3 or argon2 |
Use Node 24 LTS. On Windows, re-run the install from a fresh terminal |
Cannot find module '../generated/prisma/client' |
Run npm run db:generate in server/ |
| Port 4000 or 5173 in use | Change PORT in server/.env; for the client run npx vite --port 5174 and set CLIENT_ORIGIN to match |
| Password reset email never arrives | There is no mail server in the prototype. The reset link is printed in the API terminal and shown on screen in development |
| 3D shapes on the landing page don't appear | The hero needs WebGL; everything else works without it. Slower machines may take a few seconds in dev mode |
| Want a clean slate | cd server && npm run db:reset |