<!-- canonical: https://matiforge.com/work/building-jversity -->
# Building Jversity: a multi-tenant educational LMS platform

**Client:** MatiForge (own product)  
**Industry:** Education  
**Services:** Product engineering, Education platforms, Live video platforms, Cloud, DevOps and operations  
**Stack:** NestJS, Next.js, PostgreSQL, Redis, Agora, AWS IVS, Razorpay, Stripe, Cashfree, PayU, Cloudflare R2, Fly.io, Vercel  
**Timeline:** 2026–ongoing

## Context

Jversity is our own product: a hosted platform on which a coaching institute or a single teacher runs live classes, online courses and a blog under their own brand, each service on its own subscription. Every institute is a tenant with its own domain, branding, students and payment account. We designed it, built it, and we operate it every day.

Building for ourselves meant we could not hide behind a client's requirements. Every shortcut would be ours to live with.

## What made it hard

Four things, each of which we have written up properly elsewhere. The short version:

**Tenant isolation had to be structural**, not a rule people remember. Every shared medium carries the organisation explicitly, and we audited the platform against that one rule before any external tenant signed up. [Enforcing tenant isolation beyond the database](/insights/tenant-isolation-by-construction).

**Payments run on each tenant's own gateway**, so the money never touches us. That is right for them and for GST, and it meant building a provider-neutral spine underneath four providers. [Payments architecture for multi-tenant platforms](/insights/multi-tenant-payments-architecture).

**Live classes come in three formats**, and they are genuinely different products rather than one feature with a switch. Each is now documented on its own: [1:1](/products/one-to-one-live-classes), [batch](/products/batch-live-classes) and [broadcast](/products/broadcast-live-classes).

**Two planes, not one.** The tenant-facing application and the operator console are separate deployments with separate routes and separate boot graphs, so an internal tool can never leak a public endpoint. This is the constraint that shaped the most code and the one nobody asks about until it has already gone wrong somewhere else.

## What we did

We built the backend as a modular NestJS service with a separate worker for recordings and reconciliation, a Next.js tenant application that resolves the organisation from the hostname, a separate operator console, and a mobile app. Storage and delivery moved to a platform-provided layer so an institute never has to configure a bucket. Email is platform-provided too, with each institute verifying its own sending domain.

Everything deploys through a single promotion path, documented in runbooks a new engineer can follow, and is monitored with alerts that page a person.

## What's next

An org page builder replacing the fixed landing-page templates, a self-hosted edition for institutes that must keep data on their own infrastructure, and a mobile checkout that matches the web one.
