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 inside Teach. Protected data and mutations still run through centralized apps/web API routes.
Ownership model
Teach is a standalone teacher UI, but not a separate backend. Core teacher actions must stay insideapps/teach; avoid protected dashboard and course actions that jump teachers back to apps/web. The app forwards /api/v1/* and /api/ai/* traffic to the central web app through the satellite rewrite and API proxy guard; Teach-local API routes must stay limited to the auth cookie handoff and logout surfaces that require the Teach host.
apps/web remains the protected API owner. Teach-callable routes must opt in to app-session auth for the teach audience, resolve permissions from the forwarded app-session user, and then perform admin-backed reads/writes only after workspace membership is verified. New Teach-owned flows should add typed helpers in @tuturuuu/internal-api and consume those helpers from Teach client components with TanStack Query.
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 both a Teach-local app-session cookie for satellite route guards and a Web-issued app-session cookie for rewritten Web API requests. Protected Teach routes require both cookies; if an older local-only cookie is present, Teach sends the user back through the platform handoff so the coordinated cookie pair is refreshed without manual cookie 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 central 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 central 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 central apps/web APIs. Web API routes that serve Teach must opt in to app-session auth for the teach audience and resolve permissions from the forwarded app-session user before falling back to workspace group membership filters.
Verification
After changing Teach code, run: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. Do not add TURBO_TOKEN, TURBO_TEAM, or production Supabase service keys to workflow-level env; Teach deploys should rely on Vercel project environment variables pulled by vercel pull.
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.