Files
rootnode-academy/platform/api/README.md
T
ced4568 40692926ee docs(phase3a): document production identity architecture
New docs/PHASE3A_IDENTITY_AND_INSTRUCTOR_ACCESS.md covers the schema, persistent session
design, instructor accounts, the full RBAC matrix, cohort scoping (including the documented
raw-environment-id simplification), student session hardening (explicitly still
classroom/pilot identification, not production auth), CSRF, rate limiting, audit logging,
the API inventory, a security hardening checklist, test results, known limitations, the
future OIDC path, and production HTTPS/cookie expectations — with an explicit BUILT NOW vs.
FUTURE split.

Updates ENGINEERING_SOP.md (new §9/§10, closing a stale '(§9)' forward-reference that dated
back to Phase 2B), LAB_ARCHITECTURE.md status line, README.md, platform/README.md, and
platform/api/README.md (bootstrap-owner usage, new config vars, no more ADMIN_PASSWORD).
2026-08-18 13:36:45 -05:00

4.2 KiB

RootNode Academy — Platform API (Phase 3A)

Lightweight service: no k3s, no --privileged, no Lab terminal runtime. See docs/PHASE2B_ADMIN_AND_PLATFORM_DATA.md for the original split/migration design and docs/PHASE3A_IDENTITY_AND_INSTRUCTOR_ACCESS.md for persistent sessions, per-instructor accounts, RBAC, and audit logging — 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), and platform/test/e2e-phase3a.js (persistent sessions including a real restart, instructor auth, RBAC, cohort scoping, CSRF, audit log). All three 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 (survive a restart) but there's no distributed revocation channel to the Gateway's short-lived whoami cache, exam mode's live bookkeeping stays Lab-local, and OIDC/SSO is a documented adapter interface, not a working integration yet.