Skip to content

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:

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

Project Foundations → Design System → Persistence → Auth.js → Email & Account Flows

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.json engines; see root AGENTS.md) and Turbopack dev server on port 3001.
  • Folder skeleton: src/{app,components,lib,types,db,auth,schemas,i18n} plus tests/{unit,e2e} and messages/{en,es,fr,de,pt,zh}.json.
  • Zod-validated env via src/lib/env.ts — covers AUTH_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 (see apps/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.

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 Database in src/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.ts driven by DATABASE_URL.
  • Migration runner: scripts/db-migrate.ts using Kysely's Migrator. Initial migrations live under src/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.

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 when RESEND_API_KEY is set, falls back to SMTP when SMTP_HOST is set, and uses a console driver outside production when neither is configured (see client.ts). Templates are server-only and locale-aware.
  • Verification tokens in src/lib/auth/tokens.ts: verification_tokens with type ∈ {'email_verify','password_reset','magic_link','email_change'}, hashed at rest, single-use via used_at, TTLs in TOKEN_TTL_SECONDS.
  • Invitations use the separate invitations table (hashed token, accepted_at, expiry, optional course_slug). Acceptance is wired through src/auth/actions/accept-invitation.ts and 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 via bumpTokenVersion (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:

  1. npm run check (Biome auto-fix) — clean.
  2. npm run build — zero TypeScript errors.
  3. npm run test:components — every component changed in the phase has a passing co-located test.
  4. 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.