β¨ 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 | 20.19 or newer (22 LTS recommended) |
| 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.
git clone https://github.com/LoganthP/HackSphere.git
cd HackSphereOpen two terminals in the project folder.
Terminal 1: API server
cd server
npm install
cp .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 |
npm install fails building better-sqlite3 or argon2 |
Use Node 20.19+ or 22 LTS, which have prebuilt binaries. 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 |