Upgrades six PARTIAL modules to the gold-module standard (lesson/demonstration/ knowledge-check/guided-exercise/lab(s)/reflection), preserving every existing lab activity_id and scenario_id: root-06-m03 (jobs/cronjobs/daemonsets), root-07-m04 (ingress/networkpolicy), root-07-m02 (configuration/secrets, converted in place from a bare 5-lab bank), root-09-m02 (serviceaccounts/rbac), root-09-m03 (network/supply-chain security), root-10-m03 (troubleshooting framework). root-05-m01 (20-task kubectl-fluency bank) gets a minimal bookending lesson + reflection rather than a full redesign, since each task already carries its own explanation. root-12-m01, root-capstone-m01, and root-09-m04 are promoted to READY via documented exceptions (no new activities) where adding one would misrepresent their intentional design. Wires a new rootnode-security-assessment (exam, 60min, hint_policy disabled) using the existing Phase 3B assessment engine: 3 required hands-on CORE scenarios + 4 random-draw pools covering SecurityContext, RBAC, network policy, and the 7 previously-unwired CKS MCQs. Wires the previously-unwired readiness-probe-task scenario as an OPTIONAL practice lab in root-10-m01, using the existing is_required model. Adds root-11-m01-devops-platform-thinking — an honest, lesson-only ROOT 11 orientation module naming DevOps/platform engineering practices already exercised in ROOT 05-10/12, explicit that GitOps/CI-CD/IaC/observability need lab infrastructure this platform doesn't yet provide. No fake labs. Adds infra/scripts/validate-course-content.js: cross-checks the DB against scenario/content files for duplicate ids, orphans, invalid references, cycles, sequence collisions, instructor-notes leakage, and (new) self-paced content deferring explanation to a live instructor. 0 errors against the resulting 22 courses/modules, 163 activities, 98 scenario files. No application code changed — every gap was satisfiable with existing tables (course_modules, module_activities, assessment_definitions) and the existing content-delivery pipeline. Co-Authored-By: Claude Sonnet 5 <[email protected]>
RootNode Academy — Platform API (Phase 3B)
Lightweight service: no k3s, no --privileged, no Lab terminal runtime. See
docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md for the original split/migration design,
docs/PHASE3A_IDENTITY_AND_INSTRUCTOR_ACCESS.md for persistent sessions, per-instructor
accounts, RBAC, and audit logging, and docs/PHASE3B_LEARNING_AND_ASSESSMENT.md for attempt
telemetry, progressive hints, curriculum rules, and the exam/capstone engine — this file is the
operational reference.
Configuration reference
| Variable | Required | Meaning |
|---|---|---|
PLATFORM_DB_URL |
no (defaults to local dev Postgres) | Postgres connection string |
PROVISIONER_URL |
no | Provisioner base URL |
PROVISIONER_SHARED_SECRET |
yes | Internal Provisioner auth — process refuses to start without it |
PLATFORM_API_PORT |
no | Listen port, default 4300 |
FRONTEND_DIST_DIR |
no | Where the built frontend lives — set by the Dockerfile, override for local non-Docker dev |
SCENARIOS_DIR / BUNDLES_DIR / CURRICULUM_PATH_FILE |
no | Override the default scenarios/ locations, mainly for local dev outside Docker |
STUDENT_SESSION_TTL_HOURS / STUDENT_SESSION_IDLE_HOURS |
no | Default 8h / 2h |
INSTRUCTOR_SESSION_TTL_HOURS / INSTRUCTOR_SESSION_IDLE_HOURS |
no | Default 12h / 2h |
COOKIE_SECURE |
no | Set to 1 once served over real HTTPS — adds Secure to every session/CSRF cookie |
TRUST_PROXY |
no | Set to 1 once fronted by a trusted reverse proxy — honors X-Forwarded-For for req.ip (audit source_ip, login rate limiting) |
There is no ADMIN_PASSWORD anymore — instructor accounts live in Postgres (instructors
table), created via bootstrap-owner.js for the first one and the admin portal after that.
Local dev
cd platform
echo "PROVISIONER_SHARED_SECRET=$(openssl rand -hex 32)" > .env
docker compose -f docker-compose.dev.yml build
docker compose -f docker-compose.dev.yml up -d
cd api
PLATFORM_DB_URL=postgres://postgres:postgres@localhost:5433/rootnode_platform \
node bootstrap-owner.js [email protected] "Your Name" "a-real-password-12-chars-plus"
Everything is reachable through the Gateway at http://localhost:4200 — students at /,
instructors at /admin (sign in with the bootstrapped owner). Platform API itself isn't meant
to be hit directly by a browser (same boundary as the Provisioner relative to Platform API),
though nothing stops you from curling it directly at :4300 for local debugging.
Owner bootstrap
node bootstrap-owner.js <email> <display_name> [password]
Prefer BOOTSTRAP_OWNER_PASSWORD over the positional argument outside a throwaway local test —
CLI args can leak into shell history and ps. Refuses to run if an active owner already exists
unless the target email belongs to an existing instructor (the intended recovery path if a
platform is ever locked out with zero owners: point this at a known instructor account to
promote them).
SQLite migration
One-time cutover, run against the real data volume:
docker run --rm -v <identity-data-volume>:/data \
-e SQLITE_PATH=/data/progress.db -e PLATFORM_DB_URL=postgres://postgres:postgres@<host>:5433/rootnode_platform \
<platform-api-image> node migrate-sqlite.js
Always backs up the SQLite file first, is safe to re-run, and writes a JSON report — see
docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md §2/§3 for the full guarantees and rollback
procedure. This step is about academic data (students/cohorts/progress) — it has nothing to do
with instructor accounts, which are Phase 3A-native and start empty until you bootstrap one.
Tests
platform/test/e2e-phase2a.js (student-flow regression), platform/test/e2e-phase2b.js (the
classroom acceptance workflow), platform/test/e2e-phase3a.js (persistent sessions including a
real restart, instructor auth, RBAC, cohort scoping, CSRF, audit log), and
platform/test/e2e-phase3b.js (attempt telemetry, progressive hints, curriculum/Capstone
unlock rules, the exam engine including a real restart, two-student assessment isolation,
instructor analytics). All four need the full stack up and OWNER_EMAIL / OWNER_PASSWORD set
to a bootstrapped account — see platform/README.md.
Known limitations
See docs/PHASE3A_IDENTITY_AND_INSTRUCTOR_ACCESS.md "Known limitations" (sessions are
Postgres-backed and survive a restart, but there's no distributed revocation channel to the
Gateway's short-lived whoami cache; OIDC/SSO is a documented adapter interface, not a working
integration yet) and docs/PHASE3B_LEARNING_AND_ASSESSMENT.md §20 (no automatic
abandoned-attempt detection, needs-attention thresholds are fixed constants, pool_spec
authoring is raw JSON in the admin UI).