← All releases
v2.8.0 — Release

Eruscent — Institutional peer tutoring platform

spring boot · java 21 · next.js · postgres · clerk · flyway eruscent.com ↗
Eruscent UI Preview Banner

Eruscent is a production-grade, multi-tenant B2B SaaS platform connecting university students with peer tutors. It's an institutional-scale system: universities are tenants, campuses are subdivisions, departments are the unit of academic scope, and every user belongs to that hierarchy. The entire system enforces this boundary at every layer — not by convention, but by design. Engineered and shipped end-to-end, solo.

Core Architecture: Multi-Tenant Hierarchy

The tenant model is four levels deep: University → Campus → Department → User. This isn't just a data model — it's an enforcement contract. Every service method, every repository query, every CORS-allowed origin is scoped to this hierarchy. A student in Department A cannot see, access, or interact with data in Department B, even within the same university.

University (Tenant Root)
Top-level isolation boundary. Each university has its own allowed email domains, enforced by the 3-tier domain gating engine. Platform-level super admins manage universities; everything beneath them is scoped to their institution.
Campus (Domain-Locked)
Campuses subdivide universities geographically or operationally. Domain-locked CORS rules and subdomain routing are configured at the campus level, allowing a single platform deployment to serve multiple campuses simultaneously.
Department (Academic Scope)
The primary unit of academic telemetry. Department heads have analytics access to session volumes, bottleneck subject areas, tutor coverage gaps, and attendance drop-off rates — scoped strictly to their department's data.
User Principals
Students and tutors live at the department level. A user can hold multiple roles simultaneously — a verified tutor who is also enrolled as a student. Spring Security maps all active roles into the security context as a multi-authority principal.

Identity: JWKS, JIT Provisioning & Self-Healing

Authentication is fully decoupled from the application. Clerk acts as the identity provider, issuing JWTs signed with RSA keys published at a public JWKS endpoint. The backend verifies tokens locally — the JWKS URI is used once at startup to fetch public keys, then cached. No round-trips to Clerk on every request.

Institutional Domain Gating

Registration is restricted in real time. You can't create an account on Eruscent unless your email domain is on the approved list for your institution. The EmailDomainService implements a 3-tier fallback strategy:

Dual Session Architecture & Conflict Engine

Eruscent supports two distinct tutoring session modes, both with real-time conflict prevention and state lifecycle management.

Concurrency: Optimistic Locking Guard

During high-demand registration windows — a popular tutor opens a group lobby, multiple students try to enroll simultaneously — the database faces concurrent mutation attempts on the same record. Eruscent handles this with JPA optimistic locking:

Zero-Trust Peer Chat

The in-session messaging system doesn't trust the request alone. Every chat access attempt goes through validateUserAccess in MessageService:

Security: OWASP Hardening & Rate Limiting

The backend was subjected to a comprehensive security engineering audit covering the OWASP Top 10. Key controls implemented:

Profile Protected Endpoints Limit Action
Auth /api/v1/auth/**, login, register 5 req / min per IP HTTP 429
Public Contact /api/v1/public/contact 2 req / hr per IP HTTP 429
State Mutation All POST, PUT, DELETE, PATCH 60 req / min per IP HTTP 429
Read Bypass All GET and OPTIONS preflight Unthrottled Allowed

Analytics & Caching Architecture

Background Engines & Automated Maintenance

DevOps: CI/CD Pipeline

Technology Stack

Layer Technology Purpose
Frontend framework Next.js 16 App Router Server & client components, multi-role dashboards
Styling Tailwind CSS v4 Utility-first design system
Backend framework Spring Boot 3.4 OWASP-hardened REST API, security filter chain
Language Java 21 Virtual threads, records, pattern matching
ORM Spring Data JPA + Hibernate Criteria API specifications, optimistic locking
Database PostgreSQL 16 Relational integrity, Flyway migrations
Schema migrations Flyway Versioned, reproducible schema evolution
Authentication Clerk + JWKS Stateless JWT verification with local key cache
Rate limiting Bucket4j Per-IP token bucket with configurable profiles
API documentation OpenAPI 3.0 / Swagger UI Auto-generated, always in sync with code
CI/CD GitHub Actions Live Postgres 16 container tests + weekly security audits
Email JavaMailSender / SMTP Transactional email: session alerts, low-attendance notifications

Notes

The multi-tenant hierarchy sounds simple on paper — university, campus, department, user — but enforcing it consistently across 28 REST API endpoints, 4 primary role types (Student, Tutor, Department Admin, Super Admin), and a caching layer that aggregates across tenants without leaking data between them required treating the hierarchy as a first-class architectural primitive, not a filter condition bolted on at the end.

The hardest single decision was the optimistic locking guard. The tempting alternative is pessimistic locking — lock the row, wait, write. It's simpler to reason about. But under the enrollment load a popular tutor's lobby generates at the moment it goes live, pessimistic locking turns into a queue backed by a timeout, and users get errors that feel like bugs rather than honest "you got there second" responses. The 409 Conflict + structured error message is the honest design: it tells the client exactly what happened and why.

← Previous: ResuMaxxing Back to all releases →