Problem
Most online assessment platforms are SaaS-only. Organisations with regulated data, custom branding, or on-prem requirements either accept the SaaS constraint or roll their own. The self-host options that do exist are usually thin: single-mode auth, no AI question generation, no certificate engine, no proctoring.
I wanted a credible self-hostable alternative, the kind of thing a university IT team or a regulated employer could actually drop into their network. Multi-role auth including LDAP. AI-generated questions but against any OpenAI-compatible endpoint, so admins keep the LLM on-prem if they want. QR-verifiable PDF certificates. Proctoring counters. Configurable everything.
Approach
Clean-architecture .NET 8 backend, Next.js 16 App Router frontend, SQL Server 2022. The features that matter:
- Authentication. Local password with self-service reset, LDAP (Novell.Directory.Ldap), and Google OAuth. Admins can mix modes per org. Per-session refresh tokens with a self-service device-management page. Rate-limited login and reset endpoints. Account provisioning supports admin-create-with-temp-password or invitation-link mode where the user sets their own password from a 72-hour activation email.
- AI question generation. Admins point the platform at any OpenAI-compatible endpoint: OpenAI, Ollama, LM Studio, Groq, OpenRouter. The LLM can stay on-prem. Generated questions are editable before publishing.
- Certificate engine. QuestPDF for PDFs, QRCoder for QR codes that link back to a public verification endpoint. The cert template is admin-customisable. Certificates auto-attach to the pass email.
- Proctoring. Tab-switch and fullscreen-exit counters with configurable strict-mode auto-submit on threshold.
- Branded transactional email. Admin-editable templates with HTML-encoded placeholders, an in-process queue with bounded SMTP concurrency.
- Course material library. Per-lesson prerequisite gating before exam access.
- Public access codes. One-shot candidates arrive via a shared link, no account needed.
Architecture
Clean Architecture layout: Domain is pure POCOs, Application defines interfaces and CQRS handlers (commands and queries via MediatR), Infrastructure implements the interfaces (EF DbContext, LDAP client, AI client, email queue), Api is the HTTP surface. New features land as a Features/<Feature>/Commands or Queries folder under Application.
What’s interesting
Reflections
- Self-hostable matters more than the marketing copy admits. Real enterprise adopters look for LDAP, on-prem LLMs, and audit trails before they look at the feature list.
- Clean Architecture pays off at this scale. Adding a feature (course material gating, certificate templates, etc.) is mechanical: a Domain entity, an Application command handler, an Infrastructure repository, an Api endpoint. Each layer tests in isolation.
- Email queueing inside the process keeps the deploy simple (no Redis, no separate worker) at the cost of bounded throughput. Acceptable at organisational scale, swappable later.
- Letting admins point at any OpenAI-compatible endpoint costs almost nothing in the code (a base URL field) and unlocks the on-prem use case entirely. Should always be the default for self-hostable AI features.