Milestone 1: Foundational¶
Status: Tracking. Phases 0–3.5 are wired in tree (
src/auth/,src/db/,src/lib/email/,proxy.ts); see code as authoritative when this milestone diverges.Reading guide: This milestone summarises the foundational phases of the LMS implementation plan. Detailed phase prose lives in
reference/implementation.md. When a phase header here and the implementation plan disagree, the implementation plan and the code itself win.See also:
- Part 2: Core Application — Schemas/CMS client, public site, enrollment, entry pagination.
- Part 3: Operationalization — Quiz, dashboards, errors/security, well-known files.
- Part 4: Hand-off and Roadmap — Testing/CI, documentation, creator/marketplace proposals.
Overview¶
This milestone covers the foundational pieces that every later phase depends on: a runnable scaffold, the design system shell, the persistence layer, Auth.js v5 wiring, and the transactional email + account flows.
Dependency order
Phase Checklist¶
| Phase | Title | Status | Source |
|---|---|---|---|
| 0 | Project Foundations | [x] | Phase 0 |
| 1 | Design System & Layout Shell | [x] | Phase 1 |
| 2 | Persistence Layer (Kysely + migrations) | [x] | Phase 2 |
| 3 | Auth.js (v5) Integration | [x] | Phase 3 |
| 3.5 | Email Provider & Account Flows | [x] | Phase 3.5 |
Uncheck a phase here if the tree diverges from this milestone, and log the regression under Implementation Plan Follow-Ups in
roadmap.md.
Phase 0 — Project Foundations¶
Goal: a runnable scaffold with linting, typing, env, and folder layout aligned to the rules.
- Node ≥ 22 baseline (monorepo root
package.jsonengines; see rootAGENTS.md) and Turbopack dev server on port 3001. - Folder skeleton:
src/{app,components,lib,types,db,auth,schemas,i18n}plustests/{unit,e2e}andmessages/{en,es,fr,de,pt,zh}.json. - Zod-validated env via
src/lib/env.ts— coversAUTH_SECRET,DATABASE_URL,CMS_API_URL,CMS_PROJECT_UUID,CMS_API_UUID,CMS_WEBHOOK_SECRET,RATE_LIMIT_DRIVER,EMAIL_FROM,RESEND_API_KEY,SMTP_*,APP_BASE_DOMAIN. - Scripts:
db:migrate,db:seed,openapi:generate,test*,docs:*,verify(seeapps/lms/package.json).
Reference detail: reference/implementation.md — Phase 0 and reference/tech-stack.md.
Phase 1 — Design System & Layout Shell¶
Goal: shadcn/ui installed, design tokens wired, locale-aware root layout, public website chrome.
- shadcn/ui
new-yorkneutral;@open-learning-hub/design-tokensinglobals.css; primitives from@open-learning-hub/ui. - next-intl + next-themes (
attribute="class",defaultTheme="dark",enableSystem,disableTransitionOnChange) insrc/app/[locale]/layout.tsx. - Public chrome:
SiteHeader,SiteFooter,MainNav,ThemeToggle,LocaleSwitcher. - Vitest + co-located
*.test.tsxper custom component (tests/setup.ts,renderWithTheme) per.cursor/rules/030-testing.mdc. - WCAG:
focus-ring,skip-link,AccessibilityAnnouncer,useFocusTrap;data-testidon custom components.
Reference detail: reference/implementation.md — Phase 1 and T-027 CMS/LMS drift matrix for shadcn parity vs the CMS.
Phase 2 — Persistence Layer (Kysely + migrations)¶
Goal: type-safe DB layer for both PostgreSQL (prod) and SQLite (dev) with seed data.
- Handwritten Kysely
Databaseinsrc/db/types.ts:tenants,users,course_admin_assignments,invitations,enrollments,entry_progress,quiz_attempts,verification_tokens(email_verify,password_reset,magic_link,email_change),oauth_accounts,audit_log. - Dev seed via
scripts/db-seed.ts. - Dialect picker in
src/db/client.tsdriven byDATABASE_URL. - Migration runner:
scripts/db-migrate.tsusing Kysely'sMigrator. Initial migrations live undersrc/db/migrations/. - Query helpers grouped under
src/db/queries/(admin, audit, course-admin, enrollments, entry progress, invitations, oauth accounts, quiz attempts, tenants, users, verification tokens,withTenantOverride).
Reference detail: reference/implementation.md — Phase 2, reference/user-management.md, reference/tenant-configuration.md, reference/course-enrollment-system.md, reference/quiz-system.md, reference/entry-pagination.md, reference/analytics.md.
Phase 3 — Auth.js (v5) Integration¶
Goal: Credentials sign-in, JWT sessions enriched with userId, role, tenantId, tokenVersion, assignedCourses, route protection via proxy.ts.
- Auth.js v5 config in
src/auth/config.ts(Credentials + optional Google). - Helpers in
src/auth/credentials.ts,src/auth/oauth.ts,src/auth/guards.ts. - Tenant resolution + auth gating in
src/proxy.ts(Next.js 16 — notmiddleware.ts). - The NextAuth route handler (
src/app/api/auth/\[...nextauth\]/route.ts) re-exportshandlers.GET/POST. requireSession(),requireRole(...),requireCourseAccess(slug), andwithTenantOverride(...)insrc/auth/guards.ts.- Proxy
/admingate:tenant_admin/super_adminon all admin paths;course_adminonly on/admin/courses/{slug}/...whenslug ∈ assignedCourses. - Credentials sign-in rate-limited in
src/auth/actions/sign-in.ts(IP + normalized email, 10 requests / 15 min per120-security.mdc).
Reference detail: reference/implementation.md — Phase 3, reference/user-management.md, development/auth.md.
Phase 3.5 — Email Provider & Account Flows¶
Goal: self-service account creation, email verification, password reset, magic-link sign-in, invitation acceptance, account email change, and admin-triggered session invalidation, all backed by a shared transactional email layer.
- Email layer in
src/lib/email/—sendEmail()selects Resend whenRESEND_API_KEYis set, falls back to SMTP whenSMTP_HOSTis set, and uses a console driver outside production when neither is configured (seeclient.ts). Templates are server-only and locale-aware. - Verification tokens in
src/lib/auth/tokens.ts:verification_tokenswithtype ∈ {'email_verify','password_reset','magic_link','email_change'}, hashed at rest, single-use viaused_at, TTLs inTOKEN_TTL_SECONDS. - Invitations use the separate
invitationstable (hashed token,accepted_at, expiry, optionalcourse_slug). Acceptance is wired throughsrc/auth/actions/accept-invitation.tsand the/invite/[token]route. - Learner routes under
src/app/[locale]/learn/:/learn/sign-up,/learn/verify/\[token\],/learn/forgot,/learn/reset/\[token\],/learn/magic,/learn/magic/\[token\],/learn/account,/learn/account/email/\[token\]. - Server actions under
src/auth/actions/:signUp,verifyEmail,forgotPassword,resetPassword,requestMagicLink,consumeMagicLink,resendVerification,acceptInvitation. - Admin hooks in
src/app/[locale]/admin/users/actions.ts— resend verification (adminResendVerificationAction), issue password-reset email (adminSendPasswordResetAction), and force sign-out everywhere viabumpTokenVersion(adminForceSignOutAction). - Security per
.cursor/rules/120-security.mdc: sign-up, forgot, verify-resend rate-limited by IP + normalized email; forgot and duplicate-signup responses are user-enumeration-safe.
Reference detail: reference/implementation.md — Phase 3.5 (historical build steps), development/auth.md (operational flows and sequence diagrams), admin/security-and-rate-limiting.md.
Foundation Verification¶
After each phase in this milestone:
npm run check(Biome auto-fix) — clean.npm run build— zero TypeScript errors.npm run test:components— every component changed in the phase has a passing co-located test.- Manual smoke: sign-in, sign-up, verify, forgot/reset, magic-link, and admin force-sign-out paths.
Later-milestone and cross-cutting follow-ups (Phases 10+) live in roadmap.md.