apps/teach and runs locally on port 7813. It is the teacher-facing operations app for Tuturuuu education workspaces. Teachers create and publish courses, enroll existing workspace users, author modules, save attendance, write posts and reports, and enter metrics through Teach-owned APIs.
Ownership model
Teach owns teacher authoring, administration, and shared education v1 API contracts. Its handlers cover courses, modules, quiz sets, flashcards, enrollment, attendance, posts, reports, metrics, attempts, grading, and related teacher workflows underapps/teach/src/app/api/v1. Teach also implements
selected education AI handlers locally.
Local handlers run before the Next.js fallback rewrites. Only unmatched
/api/v1/* and /api/ai/* paths go to Web, which remains responsible for
platform login, cross-app token issuance, and explicitly retained platform or
central AI services. New Teach-owned flows should add typed helpers in
@tuturuuu/internal-api and consume those helpers from Teach client components
with TanStack Query.
packages/education-core owns reusable server-only domain logic, not HTTP
traffic. packages/internal-api/src/teach.ts and education.ts select the Teach
origin for teacher and shared education contracts; explicit platform
exceptions such as storage operations retain the Web origin.
When a Teach route uses an admin client to write rows keyed by request-body IDs, the route must validate those body IDs against the URL workspace and course before writing. Workspace permission plus course existence is not enough, because the admin client bypasses RLS; score writes such as indicator_id and user_id must prove that the indicator belongs to the URL course/workspace and the user is an active course member in the same workspace.
Courses are stored as workspace_user_groups. The course-level Learn visibility switch is workspace_user_groups.is_course_published; module visibility remains workspace_course_modules.is_published. New courses default to unpublished drafts, non-guest, active, and empty. Teach can enroll existing workspace users into a course, but this self-serve pass does not create or invite new users.
Teach-owned attendance uses the course schedule fields on workspace_user_groups: sessions, starting_date, and ending_date. Teachers can generate recurring class sessions inside Teach, and the attendance calendar should mark unscheduled days, scheduled unchecked days, partial days, complete days, late days, and absent days without linking back to the web user-group schedule page.
Teach-local logout clears the host-only app-session cookies and stale Supabase Auth cookies on teach.tuturuuu.*, then redirects browser form submissions to the central apps/web /logout?from=Teach continuation. JSON callers can still POST /api/auth/logout and receive { success: true }.
Teach does not render its own login portal. Its /login route first checks for an existing Teach app-session JWT. If the app session is already present, it redirects inside Teach to the requested next path, usually /dashboard. Otherwise it redirects to the platform login at apps/web with a returnUrl pointing back to /verify-token. After the platform login confirms the account, apps/web generates a cross-app token for the teach target app and redirects back. Teach’s local POST /api/auth/verify-app-token route only completes the host-only cookie handoff; token validation is delegated back to the central web app, and Teach does not create a Teach-local Supabase Auth session. The handoff stores a Teach-local app-session cookie for satellite route guards and shared session material used when a retained platform service must be called. If older session material cannot be refreshed, Teach sends the user back through the platform handoff so the coordinated cookies are renewed without manual deletion.
The /dashboard entry route must only send users to /login when the Teach app-session is missing. If the app-session exists but the Teach bootstrap API returns no eligible education workspace, render an empty teacher state instead of redirecting back to login.
Learn follows the same platform-login pattern: the learner app keeps /verify-token for cross-app session completion, but /login delegates account selection to apps/web.
Teach and Learn both hide locale prefixes from public and protected URLs. Legacy locale-prefixed paths such as /vi/dashboard redirect to the unprefixed canonical path while preserving the selected locale in NEXT_LOCALE.
Teach proxy locale detection must sanitize Accept-Language before it reaches intl-localematcher. Wildcard or malformed locale tokens can throw a RangeError in the proxy and surface as a broken /login or protected course page even when the route itself still exists.
Teach metadata and auth return URLs must resolve to absolute HTTP(S) app URLs. Prefer TEACH_APP_URL or NEXT_PUBLIC_TEACH_APP_URL for the Teach origin and LEARN_APP_URL or NEXT_PUBLIC_LEARN_APP_URL for Learn handoffs. A valid absolute BASE_URL can be used as a fallback for Teach, but non-URL environment values such as development are ignored so local development falls back to https://teach.tuturuuu.localhost through Portless.
Visual direction
All non-apps/web education apps use the shared Neobrutalist design language from apps/learn/DESIGN.md: heavy foreground borders, offset shadows, paper-like surfaces, compact responsive grids, and theme-adaptive dynamic accents. Teach uses this language for public teacher orientation surfaces and protected dashboards.
Teach should read as a teacher operations dashboard, not a landing page. The protected surface should keep courses, module authoring, learner enrollment, schedule-aware attendance, posts, report previews, score metrics, settings, and learner-preview handoffs visible as first-class paths. Use multiple dynamic accent roles across these work loops and keep rails readable in light, dark, and system themes.
Teach module generation now accepts optional teacher context in the AI modal. Teachers can add class level, learning goals, or other constraints before uploading source material; the Teach-owned course-generation route passes that context into the AI prompt alongside the document content.
Report authoring should include a Teach-local preview before save. The preview should show learner identity, course context, score, report body, feedback, and a metric snapshot so teachers do not need to open apps/web report pages for normal course reporting. When a learner-facing check is useful, link intentionally to apps/learn course, assignment, report, or marks pages.
Teach reads and mutates course groups and module data through its local API
handlers. Those routes resolve the Teach app-session actor and verify workspace,
course, and resource access before using admin-backed data clients.
Verification
After changing Teach routes, run focused route tests first. Finish Teach API or UI changes with the app typecheck, repository checks, and the owning app build:CI and deployment
Teach has dedicated Vercel workflows:.github/workflows/vercel-preview-teach.yaml.github/workflows/vercel-production-teach.yaml
tuturuuu.ts and use the shared ci-check.yml switchboard. They require environment-scoped Vercel credentials plus VERCEL_TEACH_PROJECT_ID; production Supabase values should live in the Vercel project environment rather than GitHub Actions.
Deployment credentials must stay environment-scoped in GitHub Actions. The preview job is bound to the vercel-preview-teach GitHub Environment, and the production job is bound to vercel-production-teach. Store VERCEL_TOKEN, VERCEL_ORG_ID, and VERCEL_TEACH_PROJECT_ID in those environments instead of repository-wide or organization-wide secrets. Production Supabase values remain in the Vercel project environment pulled by vercel pull. The repository-level TURBO_TOKEN and TURBO_TEAM variable are passed only to the wrapped vercel build step; never place them at workflow or job scope, and never expose the token to pull-request or Dependabot code.
Preview dispatch is manual-only. Run vercel-preview-teach.yaml from main, set preview_ref to the reviewed branch, tag, or SHA, and keep TRUSTED_PREVIEW_DEPLOY_ACTORS limited to maintainers approved to run secret-backed preview builds. Manual production dispatch is only valid from refs/heads/production.