Files
rootnode-academy/platform/api/README.md
T
ced4568 45a147a61f docs(phase3b): document the learning and assessment engine
New docs/PHASE3B_LEARNING_AND_ASSESSMENT.md covers the attempt/learning-record model,
progressive hints (including the Gateway response-rewrite mechanism), curriculum completion
rules, the assessment/exam engine and its timer/draw integrity guarantees, the RootNode
Capstone design, achievement records, the deliberately minimal versioning model, instructor
analytics, the Phase 3B RBAC matrix, security/integrity assumptions, dev/test commands, known
limitations, and the Phase 4 boundary.

Updates ENGINEERING_SOP.md (roadmap items 2/7/8 now done, new section 12, corrects a stale
capstone-placeholder description in section 4), LAB_ARCHITECTURE.md's status line,
README.md (corrects the 'Lab image unmodified' claim now that its frontend bundle carries
Phase 3B UX, plus new platform properties), platform/README.md, and platform/api/README.md.
2026-08-18 14:36:13 -05:00

4.7 KiB

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).