- Introduction
- Pitch
- Hogwarts
- Live Demo
- MVP
- Roadmap
- Launch Sprint
- PRD
- Get Started
- Localhost
- Architecture
- Structure
- Pattern
- Page
- Layout
- Content
- Types
- Config
- Actions
- Queries
- Authorization
- Validation
- Form
- Table
- Detail
- Card
- Util
- Hooks
- List Params
- Views
- README.md
- ISSUE.md
- Technology Stack
- Database
- File
- CDN Assets
- Entry Points
- Dashboard
- Authentication
- Credentials
- OAuth
- Flow Diagrams
- Multi-Tenancy
- Offline
- Onboarding
- Onboarding Videos
- Add Values
- Admission
- Application
- Attendance
- Compliance
- Profile
- Exams
- Exam Wizard
- Timetable
- Classrooms
- Notifications
- Conference
- LMS (Lumos)
- Finance
- Fee Management
- Invoice
- Wallet
- Salary
- Payroll
- Timesheet
- Expenses
- Budget
- Receipt
- Accounts
- Banking
- Reports
- Dashboard
- Permissions
- Messages
- Integration Flow
- Provision
- AI Document Processing
- Document Intelligence
- Internationalization
- Translation
- Translation Guide
- Icons
- Docs Factory
- Inspiration
- Listings
- Teachers
- Students
- Catalog
- Library
- Contributing
- Code of conduct
- GitHub Workflow
- Database Seeds
- Database Safety
- Test Accounts
- Playwright
- Prettier
- Block Rebound
Lumos is the platform's learning module. Students browse the shared catalog, enroll in courses, and watch lessons; teachers and admins contribute videos that flow through a review pipeline and stay under the contributor's control — including per-video paid unlocks. Two journeys meet at the lesson page, and this page documents both, then the code layout that implements them.
Governance. The Subject → Chapter → Lesson hierarchy is platform-owned: schools adopt it from the catalog and may request new subjects, but never author structure themselves. A school customizes by hiding/activating chapters, lessons, videos, or a lesson's quiz per school, and contributes at the lesson level — videos, study materials, and quiz questions. Approved lesson materials appear to students in the lesson's Resources section alongside the video.
The acceptance script lives in hogwarts#377 — the same flows as walkable test checklists.
How it flows
The full journey
The student journey (browse → enroll → watch → complete) and the contributor journey (upload → review → live → owner control) connect where an approved video lands in a lesson.
Three invariants hold everywhere:
- An unpaid-for video never has a playable link. The lock icon is cosmetic; the real paywall is that the server emits no video URL until the purchase exists.
- The player is never an empty black box. No video → the marketing story clip plays — but the fallback never replaces a paid video's lock screen, and never writes lesson progress.
- Schools never see each other's private data. School-visibility videos stay inside their school; only content approved as Public/Paid — by the platform desk, not a school admin — crosses schools, always credited to the uploader.
Discover & enroll
The catalog shows only published subjects the school has selected; content a school hid via override never appears for its students. Free courses enroll in one click with no payment UI; paid courses activate only after the Stripe webhook confirms. Free-course lessons are open even without enrolling — paid-course lessons are blocked for non-enrolled students, including by direct link.
Two ways in, on the same bar. Explore opens a shelf of six real courses with their artwork — scoped to the grade being browsed, one card per subject — above the popular search terms. Typing two characters raises a live list of matching courses, each with its thumbnail, grade and lesson count, so the six near-identical "Mathematics" rows in a K–12 catalog stay distinguishable; arrow keys and Enter open one, and See all results falls through to the full grid. Search is bilingual — a term typed in Arabic finds a course stored in English, and vice-versa — and it reads only the translation cache, so a keystroke never costs a translation API call. The whole catalog is one grade-wide query away: the grade filter applies to browsing, not to search.
Watch a lesson
A lesson with no videos — or one whose file is dead or undecodable — plays the story fallback clip instead of a black screen. Fallback playback records no progress and never auto-completes (the manual "Mark as Complete" still works). Real videos resume where the student left off, auto-complete on finish, and offer up-next. With multiple instructors, chips switch the player; the school's preferred instructor plays by default, instructors the school has disabled never appear at all, and every video credits its uploader.
The practice quiz
Below the video, a lesson can carry a short practice quiz drawn from the catalog's question bank — multiple choice, true/false, and fill-in-the-blank. A school can hide any lesson's quiz without hiding the lesson.
The quiz is graded on the server, against exactly the questions it showed you. Nothing about the answers reaches the browser until you submit: you pick an option, the server says which one was right and why. Once you submit, each question shows the correct answer with any explanation, and the score appears with it.
Scores reach the school gradebook when the student belongs to a class for that subject, and land there as their own row per lesson. Only the first attempt is recorded — you can retake a practice quiz as often as you like to study, and the quiz says plainly whether the attempt you just finished counted or not. Report cards read the same gradebook, which is why a retakeable practice score is not allowed to keep rewriting it.
Where quiz questions come from
Questions live in the platform catalog and attach to a lesson. A school gets them there two ways:
- Question Bank (Exams → Question Bank) — author a question, pick its subject, then optionally attach it to a chapter and lesson. Attaching it is what puts it in that lesson's practice quiz. The lesson picker offers only what the school actually teaches, minus anything it has hidden, and names read in your own language — the same picker the video upload dialog uses.
- Subject contributions — submitted for platform review, and visible to the school's own lessons once approved. School-authored questions default to school visibility; contributing one to the global catalog is a deliberate choice, not the default.
Contribute a video
Teachers, admins, and developers contribute through the same three-step dialog. Step one walks the catalog's own hierarchy rather than listing it flat, the same way the course catalog does: numbered grade buttons with that grade's subjects beneath them, then the chapters and lessons inside the subject you pick, with a search box for the last stretch. Grade comes first because a subject is defined per grade, so "Mathematics" means nothing on its own. Only what the school actually teaches is on offer — content the school has hidden from its LMS never appears — names read in your own language, picking a lesson moves you straight to the next step, and lessons come back a page at a time. Direct uploads go straight to storage with a progress bar — click to choose or drag the file in, and the file name becomes the title if you haven't typed one; a link that isn't a video is flagged right on the field; wrong types, oversized files, and a full storage quota are refused before any bytes move, and an uploaded-but-unsubmitted file cleans itself up when the dialog is abandoned. Quota counts the file's real size on storage, not what the client claims. Every submission lands as Pending — no role skips review — and the school's other admins are notified.
Review & publish
Two desks. The school admin's Review tab approves content that stays inside the school; anything Public or Paid belongs to the platform catalog desk, because it surfaces in every school — a school admin sees Approve disabled with an explanation (and the server refuses regardless), while Reject stays available. Both desks notify the contributor, and a rejection carries its reason into My Videos.
Ownership, control & paid unlocks
The contributor owns the video for good: visibility changes apply instantly, except widening an already-approved video to Public, which resubmits it for platform review (narrowing is always free). Revoke flips to Private and purges the CDN; replace resets approval; delete releases the storage quota. A paid video can't be un-paywalled through the visibility menu — only the explicit remove-paywall action, which warns that buyers aren't refunded. On the buying side, a lone paid video shows an Unlock-with-price pill on the lesson hero, checkout runs through Stripe, and the unlock is per-student with time-limited signed links.
Behind the scenes
Settings tabs are role-gated (teachers see only Videos; admins see all five, with a pending-count badge on Review). The Instructors surface lists every instructor whose videos reach the school's own subjects — the platform library, the school's own teachers, and any partner school's — with the coverage each one has, in lessons. A school can disable an instructor (their videos are then served on no surface, including mobile), star one as the default so their video plays first wherever they have one, or lock Lumos to a single instructor — its own teachers, or anyone it prefers. The lock is deliberately not absolute: on a lesson the locked instructor never covered, the remaining allowed instructors still play, because a lesson with no video records no progress and would put completion out of reach. Content overrides hide catalog items for one school without affecting others; parents get a read-only view of their child's progress; enrollment and completion emails go out via Resend. The whole surface — player, dialogs, review desk, search — follows the school's language, Arabic RTL included.
Certificates. Completing every lesson a school actually shows — hidden chapters and lessons don't count, so hiding content can never put a certificate out of reach — marks the enrollment complete and issues a certificate of completion. It carries the learner, the subject, the school, the date, and a certificate number, reads in the reader's own language, and is reachable from the course page as well as from the completion email. "Download" opens the browser's print dialog, which is also where Save as PDF lives. Certificates today attest to completing the course, not to passing an assessment — quiz and exam scores do not gate issuance.
Protecting school video
A school's lesson video is its own work. Lumos treats the link to that video as the thing worth guarding, because the link is what actually leaks.
Nothing playable in a Lumos page is a storage address. A lesson carries an
opaque reference — /api/lumos/video/<id> for video, /api/lumos/file/<kind>/<id>
for worksheets and notes — and that endpoint re-checks, on every single
request, who is asking: their session, their school, whether the video is
public or school-only or private, and whether a paid video has actually been
paid for. Only then does it hand back a signed link that stops working after
two hours. Revoke someone's access and the next request fails; there is no
durable URL left behind to keep working.
What that buys, concretely:
- A URL copied out of the browser stops working the same afternoon, and only ever worked for content that viewer was already allowed to watch.
- A link forwarded to somebody outside the school is useless — it is checked against their session, not the sender's.
- Videos from other schools, unapproved uploads, and unpurchased paid content are not merely hidden in the UI; the server refuses to produce a link at all.
Inside the player, downloading is discouraged the ordinary ways — no download control, no right-click save, no drag-to-desktop — and every frame carries a faint watermark identifying who is watching and when. Picture-in-Picture and casting are switched off for school video specifically because they would strip that watermark.
Screen recording cannot be prevented in a web browser — by us or by anyone. No web page can stop a phone camera, a screen recorder, or the operating system's own screenshot key. The watermark is the answer to that case: it does not stop a recording, it makes a leaked one traceable back to the account that made it. Blocking capture outright requires studio DRM (Widevine/FairPlay), which means moving video to a DRM-packaged streaming provider. Tell schools what is true: casual copying is closed off, and determined recording is attributable, not impossible.
Structure
The block follows the mirror pattern: routes under app/[lang]/s/[subdomain]/(school-dashboard)/lumos/ are thin wrappers, and everything lives in src/components/lumos/ (the component directory keeps the old name; the URL and UI are Lumos). Read fetchers in data/catalog/ are server-only modules wrapped in React cache() — deliberately not "use server" actions.
src/components/lumos/— one block, mirrored 1:1 to /streamtypes.ts— LumosDictionary, LumosContentProps, course typesauthorization.ts— RBAC matrix (DEVELOPER > ADMIN > TEACHER > STUDENT)list-params.ts— URL state (search, category, grade)header.tsx— stream navigation headersearch-bar.tsx— localized search + Explore dropdown (popular chips)lib/quota.ts— per-school video storage quota (check / inc / dec)home/— landing page — server sectionscontent.tsx— orchestrator (hero, skills, releases, continue)continue-watching-section.tsx— resume strip from LessonProgresscourses/— catalog browse + detail + enrollmentcontent.tsx— course grid (search, grade badges)[slug]/content.tsx— course detail — chapters, lessons, progressenrollment/— free enroll + Stripe checkout actionsdashboard/— the learning surfacescontent.tsx— student dashboard (enrolled courses)lesson/content.tsx— lesson player — fallback clip, unlock pill, progresslesson/catalog-actions.ts— progress writes, complete/incomplete, quizparent/— guardian read-only child progresslesson/instructor-switcher.tsx— instructor chips + per-video unlocksettings/— admin hub — 5 tabscontent.tsx— tab shell (overview / enrollments / instructors / review / videos)video-review-actions.ts— school review lane (platform gate for PUBLIC/PAID)video-review-content.tsx— pending queue UI (localized)instructor-settings.tsx— per-subject instructor preferenceenrollments/— school enrollment managementteach/— contributor surfacesvideos-content.tsx— My Videos table + upload entrypropose-video-dialog.tsx— 3-step wizard — URL or direct S3 uploadvideo-settings-dialog.tsx— owner controls (visibility, revoke, delete)get-proposable-lessons.ts— lessons the caller may contribute tovideo/— video lifecycle actionsvideo-actions.ts— uploadVideo — quota, HEAD size, reviewer notifyvideo-owner-actions.ts— visibility / paywall / revoke / replace / deletevideo-purchase-actions.ts— per-video Stripe unlock checkoutpayment/— Stripe success / cancel pagesemails/— enrollment + completion templates (Resend)shared/— player + validators + email servicevideo-player/— player, seek, up-next, watermark, onSourceErrorurl-validators.ts— video / image / document URL allowlistsdata/catalog/— read fetchers — React cache(), NOT "use server"get-lesson-with-progress.ts— ranked videos, paywall nulls, preference sortget-all-courses.ts— published subjects + search + filtersREADME.md— feature purpose, routes, statusISSUE.md— close log + post-release engineering debtCLAUDE.md— block-level context and key decisionsintegration points— outside the blocksrc/app/[lang]/s/[subdomain]/(school-dashboard)/lumos/— route mirrors (page.tsx thin wrappers)src/app/api/blob/presign/route.ts— direct-upload presign (POST) + orphan cleanup (DELETE)src/app/api/webhooks/stripe/route.ts— enrollment + video_purchase completionsrc/lib/s3.ts— HEAD (authoritative bytes) + DELETE helperssrc/components/internationalization/lumos-{en,ar}.json— the stream dictionary subtreesrc/tests/school-dashboard/lumos/— 278 unit tests across 19 filesRelated
- Catalog — owns
Subject/Chapter/Lesson, selections, overrides, and the instructor policy (per-subject preference, school-wide default, lock, block list) that Lumos reads. - Architecture — the mirror pattern and file contracts this block follows.
- Onboarding videos — the marketing "story" clip that doubles as the lesson fallback.
- QA guide (hogwarts#377) — these flows as a walkable acceptance script.