Files
rootnode-academy/docs/REMOTE_AND_SELF_PACED_ARCHITECTURE.md
T
ced4568 5bc0743b08
Release to Docker Hub / Build & Push Docker Image (push) Canceled after 0s
Release to Docker Hub / Create GitHub Release (push) Canceled after 0s
docs(phase4.1): pre-production hardening documentation
Master overview (PHASE4_1_PREPRODUCTION_HARDENING.md) covering all 27
substantive items, plus REMOTE_AND_SELF_PACED_ARCHITECTURE.md (reviews
what already works for remote/self-paced delivery - student-driven
provisioning, cohort-less students, idle-reaper-destroy-then-continue -
and the one real gap: student authentication, explicitly Phase 5 scope,
not built here).

Updated PRODUCTION_NETWORK_SECURITY.md (the Gateway-to-Lab gap is now
RESOLVED, with the full design writeup and a new control-plane-vs-data-plane
section), PRODUCTION_DEPLOYMENT.md (four-cert TLS bundle),
PRODUCTION_VALIDATION_CHECKLIST.md (Router mTLS check, admission control
checks), PHASE4_PRODUCTION_INFRASTRUCTURE.md and ENGINEERING_SOP.md and
LAB_ARCHITECTURE.md (stale 'known gap' pointers updated to point at the
resolution), BACKUP_DR.md (destructive drill), README.md and
platform/README.md (Phase 4.1 pointers, lab-router component, updated
known-limitations).

Also fixes a real pre-existing bug found while auditing what gets
committed: infra/.gitignore's patterns were written repo-root-relative
(e.g. "infra/terraform/...") but the file lives in infra/, so git resolved
them relative to THAT directory and none of them ever matched anything -
a real .terraform/ provider cache showed up as untracked when it should
have been ignored. Fixed by dropping the redundant leading "infra/" from
every pattern.

Status: PHASE 4.1 COMPLETE / PRE-PRODUCTION HARDENING COMPLETE / READY FOR
HARDWARE DEPLOYMENT.
2026-08-24 20:16:20 -05:00

5.8 KiB

Remote & Self-Paced Delivery Architecture

Phase 4.1 §12. This is an architecture review, not a new product — no self-registration, payments, or public enrollment is built here (explicit non-goal). The question this document answers: does the infrastructure and application assume "an instructor always provisions the Lab," and if so, where, and what would need to change for self-paced delivery to work.

The short answer: less of that assumption exists than expected

RootNode Academy's Lab lifecycle was built student-driven from Phase 1 — POST /api/environment/launch is called by the STUDENT'S OWN session, not by an instructor action. "Prepare Labs" (POST /api/admin/cohorts/:id/prepare-labs) is a classroom convenience that front-loads several students' launches at once before a synchronous class session, not the only path to a running Lab. This was true before Phase 4.1 and confirmed still true by this phase's own regression suites (platform/test/e2e-phase2a.js roster's two students with cohort_id: null and takes them through login → curriculum → launch → scenario work with zero cohort involvement) and by two new Phase 4.1 drills built specifically to exercise the remote/returning -student narrative:

  • infra/scripts/drills/test-lab-host-loss.js — student launches, makes progress, their Lab is destroyed out from under them (standing in for an idle-reaper destroy), Platform stays up, progress is untouched, they launch again unprompted and continue.
  • infra/scripts/drills/test-fake-cohort-rehearsal.js — the same "returns later, fresh Lab, curriculum continues" narrative as part of a fuller classroom rehearsal.

Both pass today, with zero application code changes needed for the self-paced-relevant parts of this flow specifically — see the Phase 4.1 report for the full run results.

What already works, unmodified

  • Student-initiated provisioning — no instructor step is required between "student wants to work" and "Lab exists." (platform/api/src/server.js's /api/environment/launch.)
  • Cohort-less students — cohort_id is nullable on student creation (POST /api/admin/students); a student with no cohort logs in, sees the curriculum, launches Labs, and makes progress identically to a cohorted one. Confirmed by inspection AND by the existing regression suite, not just by schema reading.
  • Idle reaper → academic state survives → return → continue — the exact lifecycle this section describes as the target self-paced experience is what Phase 1's reaper + Phase 2B's Postgres-backed academic data (separate from the disposable Lab) were already designed to produce. Phase 4.1 proved it end-to-end, twice (see above).
  • Curriculum content itself is not cohort-gated — GET /api/curriculum/path reads the one curriculum path file directly (curriculum.loadCurriculumPath()); it never checks curriculum_assignments or cohort membership at all. A self-paced student sees the full curriculum with no instructor action required to "unlock" it.

What's genuinely missing for a real self-paced product (Phase 5, not built here)

  1. No self-registration / public enrollment. A student row is created only by an instructor (POST /api/admin/students, admin/owner RBAC-gated). There is no public "sign up" surface. This is almost certainly the right call to leave alone for now — it's explicitly a Phase 5+ product/business decision (course commerce, is it free, who's allowed to enroll), not an infrastructure gap.

  2. No real student authentication. A student session is created from a bare student_id (POST /api/login, no password) — deliberately, since today every student_id is issued and controlled by an instructor via the roster, not chosen or known by the public. A genuinely remote/self-paced product where students access their account without an instructor in the loop needs real credentials (email+password, or a magic link) — reusing the same password_hash/scrypt machinery instructors already has (src/auth/password.js) would be the natural extension, not a new auth system. This is the one item on this list that would actually block self-paced delivery today — everything else already works or is a product decision, not an infrastructure one.

  3. curriculum_assignments.student_id is schema-ready but unwired. The table has supported a direct per-student curriculum assignment (independent of any cohort) since Phase 2B (CONSTRAINT curriculum_assignment_target CHECK (cohort_id IS NOT NULL OR student_id IS NOT NULL)) — but nothing in the application ever reads or writes that column today; there's only one curriculum path, so nothing has needed to differentiate yet. Not a gap that blocks self-paced delivery now (every student already sees the one path with no gate), but the moment a second curriculum path exists, this is where per-individual assignment lands — the schema was designed for exactly this and doesn't need a migration to support it.

  4. No delivery-mode concept anywhere. In-person, remote, and self-paced sessions aren't distinguished in any data model today, and nothing currently needs them to be — capacity planning, the reaper, and academic records all behave identically regardless of how a student is attending. Worth being aware this is unmodeled, not worth building a taxonomy for before a real product reason exists (YAGNI).

What Phase 4.1 deliberately did NOT do here

No signup flow, no student password/email auth implementation, no per-student curriculum assignment route, no payments, no public course catalog, no held/premium scenario content. All explicit non-goals per this phase's authorization. This document exists so Phase 5 planning starts from an accurate picture of the gap (one real item: student authentication) instead of a guess.